Webhooks API
The Webhooks API manages outbound webhook subscriptions for platform events in a space. Qarion sends JSON payloads to HTTPS endpoints, signs each delivery with HMAC-SHA256, and records delivery attempts for troubleshooting.
All paths below are under /api/v1.0. Webhook administration is space-scoped and feature-gated by webhooks.retry.
Endpoints
| Method | Endpoint | Description |
|---|---|---|
GET | /spaces/{slug}/webhooks/event-types | List supported event types with categories and descriptions. |
GET | /spaces/{slug}/webhooks | List webhook subscriptions for a space. |
POST | /spaces/{slug}/webhooks | Create a webhook subscription. |
PATCH | /spaces/{slug}/webhooks/{webhook_id} | Update a webhook subscription. |
DELETE | /spaces/{slug}/webhooks/{webhook_id} | Delete a webhook and its delivery history. |
POST | /spaces/{slug}/webhooks/{webhook_id}/test | Send a synthetic test event. |
GET | /spaces/{slug}/webhooks/{webhook_id}/deliveries | List delivery attempts for one webhook. |
Creating a webhook also enforces the webhooks.max_webhooks tier limit for the space.
Supported Events
GET /api/v1.0/spaces/{slug}/webhooks/event-types
Returns the canonical event catalog. Current event types include:
| Category | Event types |
|---|---|
| Products | product.created, product.updated, product.deleted, product.archived |
| Alerts | alert.created, alert.resolved |
| Quality | quality_check.passed, quality_check.failed |
| Governance | access_request.created, access_request.approved, access_request.rejected, access.auto_revoked |
| Issues | issue.created, issue.updated, issue.closed |
| Contracts | contract.created, contract.updated, contract.archived |
| Workflows | workflow.completed, workflow.failed |
| Recertification | recertification.cycle_created, recertification.cycle_completed, recertification.access_revoked |
Use event_types: ["*"] to subscribe to every supported event.
List Webhooks
GET /api/v1.0/spaces/{slug}/webhooks
Returns subscriptions ordered by creation time descending.
[
{
"id": "1e5a3bbb-4b8e-4c38-8f04-f4db1b520f4f",
"space_id": "2a2423bd-c759-48cf-9588-7f21c12fba8c",
"name": "Governance Event Sink",
"url": "https://hooks.example.com/qarion",
"secret_last4": "****a1b2",
"is_active": true,
"event_types": [
"product.created",
"quality_check.failed"
],
"headers": {
"Authorization": "****"
},
"created_by": {
"id": "67b1bc7e-90d4-4df0-b232-fbb6b947e8cf",
"email": "admin@example.com",
"full_name": "Admin User"
},
"created_at": "2026-06-29T09:00:00Z",
"updated_at": "2026-06-29T09:00:00Z"
}
]
Secrets and custom header values are never returned in plaintext. Custom header names are preserved with masked values.
Create Webhook
POST /api/v1.0/spaces/{slug}/webhooks
Request Body
{
"name": "Governance Event Sink",
"url": "https://hooks.example.com/qarion",
"secret": "receiver-signing-secret",
"event_types": [
"product.created",
"quality_check.failed"
],
"headers": {
"Authorization": "Bearer receiver-token"
}
}
| Field | Required | Description |
|---|---|---|
name | Yes | Display name for the subscription. |
url | Yes | Public HTTPS endpoint. Unsafe, private, or non-HTTPS URLs are rejected. |
secret | No | HMAC signing secret. If omitted, Qarion generates one, but the raw generated value is not returned. Provide a known secret when the receiver must verify signatures. |
event_types | No | Event type list. Defaults to ["*"]. Must contain at least one supported type or *. |
headers | No | Extra outbound headers. Values are encrypted at rest and masked in responses. |
Custom headers cannot override Qarion-controlled headers: Content-Type, X-Qarion-Event, or X-Qarion-Signature.
Update Webhook
PATCH /api/v1.0/spaces/{slug}/webhooks/{webhook_id}
Request Body
{
"name": "Governance Event Sink",
"url": "https://hooks.example.com/qarion-v2",
"is_active": true,
"event_types": [
"*"
],
"headers": {
"Authorization": "****",
"X-Receiver-Tenant": "qarion-prod"
}
}
All fields are optional. Omitted fields are unchanged. Masked header values preserve the existing encrypted value for that header name, which lets clients round-trip masked responses without erasing stored credentials.
Delete Webhook
DELETE /api/v1.0/spaces/{slug}/webhooks/{webhook_id}
Deletes the webhook and its delivery history. The response is 204 No Content.
Test Webhook
POST /api/v1.0/spaces/{slug}/webhooks/{webhook_id}/test
Sends a synthetic test event to the configured URL and records a delivery attempt.
{
"success": true,
"status_code": 200,
"error": null
}
The test payload has this shape:
{
"event": "test",
"message": "This is a test event from Qarion to verify your webhook endpoint.",
"webhook_name": "Governance Event Sink"
}
Delivery Log
GET /api/v1.0/spaces/{slug}/webhooks/{webhook_id}/deliveries?skip=0&limit=50
Query Parameters
| Parameter | Type | Description |
|---|---|---|
skip | integer | Number of delivery records to skip. Default 0. |
limit | integer | Number of delivery records to return. Default 50, maximum 200. |
Deliveries are ordered by delivered_at descending.
[
{
"id": "8e4e5bb4-a26d-4ace-9043-521e2ed78e50",
"webhook_id": "1e5a3bbb-4b8e-4c38-8f04-f4db1b520f4f",
"event_type": "quality_check.failed",
"payload": {
"quality_check_id": "check-uuid",
"status": "failed"
},
"status_code": 200,
"response_body": "ok",
"success": true,
"attempt": 1,
"error": null,
"delivered_at": "2026-06-29T09:30:00Z"
}
]
Response bodies are truncated to 500 characters.
Delivery Signing
Every outbound delivery includes these headers:
| Header | Description |
|---|---|
Content-Type | Always application/json. |
X-Qarion-Event | Event type being delivered. |
X-Qarion-Signature | sha256=<hex digest> HMAC-SHA256 signature of the raw JSON request body using the webhook secret. |
To verify a delivery, compute HMAC-SHA256 over the raw request body bytes with the shared secret, hex-encode it, and compare it to the value after sha256=.
Errors
| Status | Description |
|---|---|
401 | Authentication is missing or invalid. |
403 | Caller cannot administer the target space, the feature is unavailable, or the space exceeded the configured webhook subscription limit. |
404 | Webhook or space was not found. |
422 | Invalid event type, unsafe URL, invalid HTTPS URL shape, or reserved custom header. |