> ## Documentation Index
> Fetch the complete documentation index at: https://docs.grantiva.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Risk Alert Rules

> Fire webhooks when a device's risk score crosses a configured threshold

<Info>Configuring risk alert rules and reading the delivery log require the **Business** plan or higher. Listing existing rules is available to all plans.</Info>

Risk alert rules are evaluated after every attestation, once the device's risk score is computed. When a score crosses a rule's threshold, Grantiva POSTs an alert payload to the rule's webhook URL. Alerts never block or slow down the attestation path — delivery happens in the background.

**Delivery behavior**

* **Cooldown**: the same device will not re-trigger the same rule within 1 hour.
* **Retries**: up to 3 attempts per alert, with 1 min / 5 min / 30 min backoff.
* **Signing**: every request carries an `X-Grantiva-Signature` header — an HMAC-SHA256 of the raw body, signed with the rule's secret (generated at rule creation).

## Authentication

All endpoints require a dashboard session (cookie-based) with organization context. Creating, updating, and deleting rules additionally requires **Admin** role or above.

***

## Alert payload

When a rule fires, your webhook URL receives:

```json theme={null}
{
  "bundleId": "com.yourapp.example",
  "crossedAt": "2026-07-26T12:00:00Z",
  "deviceId": "abc123keyid",
  "riskScore": 87,
  "teamId": "ABBM6U9RM5",
  "threshold": 75
}
```

Headers on the outgoing request:

| Header                 | Value                                 |
| ---------------------- | ------------------------------------- |
| `X-Grantiva-Signature` | HMAC-SHA256 signature of the raw body |
| `X-Grantiva-Event`     | `risk_alert`                          |
| `X-Grantiva-Delivery`  | Delivery UUID                         |
| `User-Agent`           | `Grantiva-Webhooks/1.0`               |

***

## List rules

```
GET /api/v1/org/risk-alerts/rules
```

Returns all alert rules for the organization, newest first.

**Response**

```json theme={null}
[
  {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "name": "Critical risk devices",
    "threshold": 75,
    "comparison": "gt",
    "webhookUrl": "https://example.com/hooks/risk-alerts",
    "isActive": true,
    "createdAt": "2026-06-01T12:00:00Z"
  }
]
```

***

## Create a rule

```
POST /api/v1/org/risk-alerts/rules
```

Requires **Admin** role and **Business** plan or higher.

**Request body**

```json theme={null}
{
  "name": "Critical risk devices",
  "threshold": 75,
  "comparison": "gt",
  "webhookUrl": "https://example.com/hooks/risk-alerts"
}
```

| Field        | Type    | Required | Description                                            |
| ------------ | ------- | -------- | ------------------------------------------------------ |
| `name`       | string  | Yes      | Label, 1–100 characters                                |
| `threshold`  | integer | Yes      | Risk score threshold, 0–100                            |
| `comparison` | string  | Yes      | `gt` (score > threshold) or `gte` (score >= threshold) |
| `webhookUrl` | string  | Yes      | URL to POST alert payloads to                          |

**Response** — `201 Created`

Returns the rule object (same shape as the list response). New rules are active immediately.

***

## Update a rule

```
PATCH /api/v1/org/risk-alerts/rules/:ruleId
```

Requires **Admin** role and **Business** plan or higher. All fields are optional; only provided fields change.

**Request body**

```json theme={null}
{
  "name": "High risk devices",
  "threshold": 60,
  "comparison": "gte",
  "webhookUrl": "https://example.com/hooks/risk-alerts-v2",
  "isActive": false
}
```

**Response** — `200 OK`

Returns the updated rule object.

**Errors**

| Status | Meaning                                                   |
| ------ | --------------------------------------------------------- |
| 400    | `threshold` outside 0–100, or `comparison` not `gt`/`gte` |
| 403    | Not an Admin, or plan below Business                      |
| 404    | Rule not found in this organization                       |

***

## Delete a rule

```
DELETE /api/v1/org/risk-alerts/rules/:ruleId
```

Requires **Admin** role and **Business** plan or higher.

**Response** — `204 No Content`

***

## List alert deliveries

```
GET /api/v1/org/risk-alerts/deliveries
```

Requires **Business** plan or higher (any member role). Returns the 200 most recent alert deliveries across all of the organization's rules, newest first.

**Response**

```json theme={null}
[
  {
    "id": "7f3e2a10-1b2c-4d5e-8f90-112233445566",
    "ruleId": "550e8400-e29b-41d4-a716-446655440000",
    "deviceId": "abc123keyid",
    "riskScore": 87,
    "status": "success",
    "attemptCount": 1,
    "httpStatus": 200,
    "error": null,
    "deliveredAt": "2026-07-26T12:00:03Z",
    "createdAt": "2026-07-26T12:00:02Z"
  }
]
```

| Field          | Type            | Description                                                 |
| -------------- | --------------- | ----------------------------------------------------------- |
| `status`       | string          | `pending`, `success`, or `failed` (failed after 3 attempts) |
| `attemptCount` | integer         | Delivery attempts made                                      |
| `httpStatus`   | integer \| null | Last HTTP status your endpoint returned                     |
| `error`        | string \| null  | Last error, e.g. `"HTTP 500"` or a transport error          |
| `deliveredAt`  | string \| null  | When delivery succeeded                                     |

***

## See also

* [Webhook Endpoints](/api-reference/webhooks/endpoints) — org-wide event webhooks with the same signature scheme
* [Webhook Deliveries & Testing](/api-reference/org/webhook-endpoints)
