Webhooks are available on Pro, Business, and Enterprise plans. Endpoint limits vary by tier.
Authentication
All webhook management endpoints require a session token (cookie-based) or a gpat_/aat_ API key:
Authorization: Bearer gpat_your_personal_api_token
Callers must have Admin role or above within the organization.
List webhook endpoints
Returns all configured webhook endpoints for the organization, sorted by creation date (newest first).
Response
[
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"url": "https://example.com/hooks/grantiva",
"events": ["attestation.completed", "risk_threshold_exceeded"],
"isActive": true,
"description": "Production event receiver",
"createdAt": "2026-03-01T12:00:00Z",
"updatedAt": "2026-03-10T08:30:00Z"
}
]
Create a webhook endpoint
POST /api/v1/org/webhooks
Request body
{
"url": "https://example.com/hooks/grantiva",
"events": ["attestation.completed", "risk_threshold_exceeded"],
"description": "Production event receiver"
}
| Field | Type | Required | Description |
|---|
url | string | Yes | HTTPS destination URL |
events | string[] | Yes | Event types to subscribe to. Must contain at least one. |
description | string | No | Human-readable label for this endpoint |
Supported event types
| Event | Description |
|---|
attestation.completed | Device successfully attested |
risk_threshold_exceeded | Device risk score crossed a threshold |
device_blocked | Device blocked due to high risk score |
Response — 201 Created
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"url": "https://example.com/hooks/grantiva",
"events": ["attestation.completed"],
"isActive": true,
"description": "Production event receiver",
"secret": "whsec_abc123...",
"createdAt": "2026-03-01T12:00:00Z"
}
The secret field is returned only at creation time. Store it immediately — it cannot be retrieved again. Use it to verify the X-Grantiva-Signature header on incoming deliveries.
Update a webhook endpoint
PATCH /api/v1/org/webhooks/:id
Toggle the endpoint on/off, change subscribed events, or update the description. All fields are optional.
Request body
{
"isActive": false,
"events": ["attestation.completed", "device_blocked"],
"description": "Updated label"
}
Response — 200 OK
Returns the updated WebhookResponse object (same shape as list response, without secret).
Delete a webhook endpoint
DELETE /api/v1/org/webhooks/:id
Permanently removes the webhook endpoint. In-flight deliveries are not retried after deletion.
Response — 204 No Content
Signature verification
Every delivery includes an X-Grantiva-Signature header containing an HMAC-SHA256 signature of the raw request body, signed with your webhook’s secret.
Verify the signature before processing any payload:
import CryptoKit
func isValidSignature(payload: Data, signature: String, secret: String) -> Bool {
guard let secretData = secret.data(using: .utf8) else { return false }
let key = SymmetricKey(data: secretData)
let mac = HMAC<SHA256>.authenticationCode(for: payload, using: key)
let expected = "sha256=" + Data(mac).map { String(format: "%02x", $0) }.joined()
return expected == signature
}
For Node.js, Python, and Go examples see Webhooks Concepts.
See also