Webhooks API
Webhooks endpoints in the Brix REST API: 8 operations (GET, POST, PATCH, DELETE). Each lists its path, auth, required permission and a curl example.
View as Markdown · For AI agents: the full docs index is at /llms.txt
Base URL https://api.brixsignage.com. Send Authorization: Bearer $BRIX_API_KEY unless an operation says Auth: none or Auth: no API key. The Permission line names what the key must hold. See Authentication and scopes, Errors and rate limits and Pagination.
GET /v1/webhooksPOST /v1/webhooksPATCH /v1/webhooks/{id}DELETE /v1/webhooks/{id}POST /v1/webhooks/{id}/testGET /v1/webhooks/deliveriesPOST /v1/webhooks/deliveries/{id}/replayGET /v1/webhooks/events
Every 4xx and 5xx response below returns this body:
| Field | Type | Description |
|---|---|---|
error | string | Machine-readable code: unauthorized, forbidden, not_found, validation_error, conflict, rate_limited, internal_error, … |
message | string | Human-readable explanation. Safe to show an operator. |
requestId | string | Present on 5xx: quote it to support. |
GET /v1/webhooks
List the workspace's outbound webhook endpoints. The signing secret is never included in this response; it is shown only once, at creation. Notes. - Needs the permission at the workspace root: a location-scoped key is refused.
curl "https://api.brixsignage.com/v1/webhooks" \
-H "Authorization: Bearer $BRIX_API_KEY" Response 200: Success.
| Field | Type | Description |
|---|---|---|
data | array of WebhookEndpoint | |
data[].id | string | Webhook endpoint id. |
data[].url | string | Where deliveries are POSTed. |
data[].description | string | null | |
data[].secretPreview | string | First 8 characters of the signing secret. |
data[].events | array of string | Subscribed event names (see GET /v1/webhooks/events). |
data[].enabled | boolean | |
data[].autoDisabledAt | string | null | Set when repeated failures switched the endpoint off. |
data[].autoDisabledReason | string | null | |
data[].consecutiveFailures | integer | |
data[].lastDeliveryAt | string | null | |
data[].lastStatus | integer | null | HTTP status of the last delivery attempt. |
data[].createdAt | string | ISO-8601 timestamp (UTC). |
data[].updatedAt | string | ISO-8601 timestamp (UTC). |
Response 401: Missing, expired or revoked bearer token.
Response 403: The token lacks the permission this operation needs (see x-brix-permission).
Response 5XX: Server error. The body carries a requestId to quote to support.
POST /v1/webhooks
Create an endpoint. The response includes the signing secret once; after that, only a preview of it is available, since it is stored encrypted and cannot be retrieved in full. The endpoint URL is checked at creation and again before every delivery, since a hostname that resolved to a public address at save time could later be re-pointed at an internal address. Only content.recalled and content.restored events are actually sent today, even though the event catalog lists more.
The response carries the signing secret ONCE. Store it: later reads return only secretPreview.
Notes.
- Validation failures are 400 bad_request, where most other routes answer 422 validation_error.
Request body: application/json
| Field | Type | Required | Description |
|---|---|---|---|
url | string | yes | An https URL on the public internet (private and loopback hosts are refused). |
events | array of "screen.offline" | "screen.online" | "content.recalled" | "content.restored" | "approval.requested" | "approval.decided" | "emergency.started" | "emergency.cleared" | no | Events to deliver. Default: none. An unknown name is refused. |
description | string | no | Label; cut to 200 characters. |
curl -X POST "https://api.brixsignage.com/v1/webhooks" \
-H "Authorization: Bearer $BRIX_API_KEY" \
-H "Content-Type: application/json" \
-d '{"url":"https://hooks.example.com/brix","events":["screen.offline","screen.online"],"description":"Ops alerts"}' Response 201: Success.
| Field | Type | Description |
|---|---|---|
data | object | |
data.id | string | Webhook endpoint id. |
data.url | string | Where deliveries are POSTed. |
data.description | string | null | |
data.secretPreview | string | First 8 characters of the signing secret. |
data.events | array of string | Subscribed event names (see GET /v1/webhooks/events). |
data.enabled | boolean | |
data.autoDisabledAt | string | null | Set when repeated failures switched the endpoint off. |
data.autoDisabledReason | string | null | |
data.consecutiveFailures | integer | |
data.lastDeliveryAt | string | null | |
data.lastStatus | integer | null | HTTP status of the last delivery attempt. |
data.createdAt | string | ISO-8601 timestamp (UTC). |
data.updatedAt | string | ISO-8601 timestamp (UTC). |
data.secret | string | The HMAC signing secret (64 hex characters). Shown only in this response. |
{
"data": {
"id": "whe_7a8b9c0d1e2f3a4b",
"url": "https://hooks.example.com/brix",
"description": "Ops alerts",
"secretPreview": "3f9a1c2e",
"events": [
"screen.offline",
"screen.online"
],
"enabled": true,
"autoDisabledAt": null,
"autoDisabledReason": null,
"consecutiveFailures": 0,
"lastDeliveryAt": null,
"lastStatus": null,
"createdAt": "2026-09-28T09:00:00.000Z",
"updatedAt": "2026-09-28T09:00:00.000Z",
"secret": "3f9a1c2e4b5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2c3d4e5f6a7b8c9d0e1f"
}
} Response 400: bad_request (missing url, unknown event) or unsafe_url.
Response 401: Missing, expired or revoked bearer token.
Response 403: The token lacks the permission this operation needs (see x-brix-permission).
Response 5XX: Server error. The body carries a requestId to quote to support.
PATCH /v1/webhooks/{id}
Change a webhook endpoint's URL, event subscriptions, description, or enabled state. Re-enabling an endpoint clears any automatic disable and resets its failure count, since re-enabling means the receiver has been confirmed fixed.
Notes.
- Needs the permission at the workspace root: a location-scoped key is refused.
- Validation failures are 400 bad_request, where most other routes answer 422 validation_error.
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
id | path | string | yes | Webhook endpoint id. |
Request body: application/json
| Field | Type | Required | Description |
|---|---|---|---|
url | string | no | An https URL on the public internet. |
events | array of "screen.offline" | "screen.online" | "content.recalled" | "content.restored" | "approval.requested" | "approval.decided" | "emergency.started" | "emergency.cleared" | no | Replaces the subscription. An unknown name is refused. |
description | string | no | Cut to 200 characters. |
enabled | boolean | no | true also clears an automatic switch-off and the failure count. |
curl -X PATCH "https://api.brixsignage.com/v1/webhooks/{id}" \
-H "Authorization: Bearer $BRIX_API_KEY" \
-H "Content-Type: application/json" \
-d '{"enabled":true}' Response 200: Success.
| Field | Type | Description |
|---|---|---|
data | WebhookEndpoint | A customer webhook endpoint. The signing secret is never returned after creation. |
data.id | string | Webhook endpoint id. |
data.url | string | Where deliveries are POSTed. |
data.description | string | null | |
data.secretPreview | string | First 8 characters of the signing secret. |
data.events | array of string | Subscribed event names (see GET /v1/webhooks/events). |
data.enabled | boolean | |
data.autoDisabledAt | string | null | Set when repeated failures switched the endpoint off. |
data.autoDisabledReason | string | null | |
data.consecutiveFailures | integer | |
data.lastDeliveryAt | string | null | |
data.lastStatus | integer | null | HTTP status of the last delivery attempt. |
data.createdAt | string | ISO-8601 timestamp (UTC). |
data.updatedAt | string | ISO-8601 timestamp (UTC). |
{
"data": {
"id": "whe_7a8b9c0d1e2f3a4b",
"url": "https://hooks.example.com/brix",
"description": "Ops alerts",
"secretPreview": "3f9a1c2e",
"events": [
"screen.offline",
"screen.online"
],
"enabled": true,
"autoDisabledAt": null,
"autoDisabledReason": null,
"consecutiveFailures": 0,
"lastDeliveryAt": null,
"lastStatus": null,
"createdAt": "2026-09-28T09:00:00.000Z",
"updatedAt": "2026-09-28T09:00:00.000Z"
}
} Response 400: bad_request (unknown event) or unsafe_url.
Response 401: Missing, expired or revoked bearer token.
Response 403: The token lacks the permission this operation needs (see x-brix-permission).
Response 404: No such endpoint in this workspace.
Response 5XX: Server error. The body carries a requestId to quote to support.
DELETE /v1/webhooks/{id}
Delete a webhook endpoint. Any deliveries still pending for it are abandoned rather than retried.
Notes.
- Answers { data: { id } } — without the deleted: true most other deletes return.
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
id | path | string | yes | Webhook endpoint id. |
curl -X DELETE "https://api.brixsignage.com/v1/webhooks/{id}" \
-H "Authorization: Bearer $BRIX_API_KEY" Response 200: Success.
| Field | Type | Description |
|---|---|---|
data | object | |
data.id | string |
Response 401: Missing, expired or revoked bearer token.
Response 403: The token lacks the permission this operation needs (see x-brix-permission).
Response 404: No such endpoint in this workspace.
Response 5XX: Server error. The body carries a requestId to quote to support.
POST /v1/webhooks/{id}/test
Send a real, signed sample delivery to this endpoint now. It is a genuine delivery rather than a simulation, so it exercises the same signature verification your receiver uses in production. The payload has the same fields as a real event of that type, filled from one of your screens where possible, plus test: true. Pick the event with event in the body; by default the test uses the first event the endpoint is subscribed to. This call returns 409 if the endpoint is not subscribed to that event.
Sends a real, signed delivery of one subscribed event now and records it in the delivery log. data carries every documented key of that event (the same shape a real event has), filled from a screen in the workspace when there is one and with sample values otherwise, plus test: true. A failed send is NOT an HTTP error: the answer is 200 with ok: false.
Notes.
- A test payload has data.test: true. Real events never carry test.
- Needs the permission at the workspace root: a location-scoped key is refused.
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
id | path | string | yes | Webhook endpoint id. |
Request body: application/json
| Field | Type | Required | Description |
|---|---|---|---|
event | "screen.offline" | "screen.online" | "content.recalled" | "content.restored" | "approval.requested" | "approval.decided" | "emergency.started" | "emergency.cleared" | no | The event to test. Default: the first event the endpoint is subscribed to. |
curl -X POST "https://api.brixsignage.com/v1/webhooks/{id}/test" \
-H "Authorization: Bearer $BRIX_API_KEY" \
-H "Content-Type: application/json" Response 200: Success.
| Field | Type | Description |
|---|---|---|
data | object | |
data.deliveryId | string | Id of the delivery this call made (a new whd_… row in the delivery log). |
data.ok | boolean | The receiver answered 2xx. |
data.status | integer | null | HTTP status the receiver answered with; null when no answer came. |
data.error | string | null | Why the delivery failed (http 500, a timeout, an unsafe address); null on success. |
data.event | "screen.offline" | "screen.online" | "content.recalled" | "content.restored" | "approval.requested" | "approval.decided" | "emergency.started" | "emergency.cleared" | The event the test delivery sent. |
Response 400: bad_request: event is not a webhook event.
Response 401: Missing, expired or revoked bearer token.
Response 403: The token lacks the permission this operation needs (see x-brix-permission).
Response 404: No such endpoint in this workspace.
Response 409: not_subscribed: the endpoint is not subscribed to event (or to any event), or it is switched off.
Response 5XX: Server error. The body carries a requestId to quote to support.
GET /v1/webhooks/deliveries
List the webhook delivery log: what was sent, the response received, the number of attempts, and what is still pending. Each entry includes the exact payload that was sent. Results are paginated and can be filtered by endpoint and status. Failed deliveries are retried with increasing delay over roughly 8 to 12 hours before they are given up on.
Notes.
- Always paged: without ?limit a page has 50 deliveries (not every row, unlike the lists that page only on request), and nextCursor is always present.
- Needs the permission at the workspace root: a location-scoped key is refused.
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
limit | query | string | no | Page size, 1–200. Default 50. |
cursor | query | string | no | The nextCursor of the previous page. |
endpointId | query | string | no | Only this endpoint's deliveries. |
status | query | "pending" | "delivered" | "failed" | "abandoned" | no | Only deliveries in this state. Another value is ignored. |
curl "https://api.brixsignage.com/v1/webhooks/deliveries" \
-H "Authorization: Bearer $BRIX_API_KEY" Response 200: Success.
| Field | Type | Description |
|---|---|---|
data | array of WebhookDelivery | Newest first. |
data[].id | string | Webhook delivery id. |
data[].endpointId | string | |
data[].event | string | |
data[].status | "pending" | "delivered" | "failed" | "abandoned" | failed is retried later; abandoned is not retried again. |
data[].attempts | integer | |
data[].occurredAt | string | ISO-8601 timestamp (UTC). |
data[].createdAt | string | ISO-8601 timestamp (UTC). |
data[].nextAttemptAt | string | null | When the next retry is due; null when none is. |
data[].deliveredAt | string | null | |
data[].responseStatus | integer | null | |
data[].error | string | null | |
data[].replayOf | string | null | Set on a delivery made by a replay: the delivery it resends. |
data[].payload | object | null | The JSON body as it was sent; null if the stored copy cannot be read. |
nextCursor | string | null | Send it back as ?cursor= for the next page; null on the last page. |
Response 401: Missing, expired or revoked bearer token.
Response 403: The token lacks the permission this operation needs (see x-brix-permission).
Response 5XX: Server error. The body carries a requestId to quote to support.
POST /v1/webhooks/deliveries/{id}/replay
Resend a delivery as a new delivery with its own id and a replayOf field pointing at the original. The original delivery record is left unchanged, so a receiver that deduplicates on delivery id will not silently ignore the resend.
Sends the payload again now, as a new delivery. A failed send is NOT an HTTP error: the answer is 200 with ok: false.
Notes.
- Needs the permission at the workspace root: a location-scoped key is refused.
| Parameter | In | Type | Required | Description |
|---|---|---|---|---|
id | path | string | yes | Id of the delivery to send again. |
curl -X POST "https://api.brixsignage.com/v1/webhooks/deliveries/{id}/replay" \
-H "Authorization: Bearer $BRIX_API_KEY" Response 200: Success.
| Field | Type | Description |
|---|---|---|
data | object | |
data.deliveryId | string | Id of the delivery this call made (a new whd_… row in the delivery log). |
data.ok | boolean | The receiver answered 2xx. |
data.status | integer | null | HTTP status the receiver answered with; null when no answer came. |
data.error | string | null | Why the delivery failed (http 500, a timeout, an unsafe address); null on success. |
Response 401: Missing, expired or revoked bearer token.
Response 403: The token lacks the permission this operation needs (see x-brix-permission).
Response 404: No such delivery in this workspace.
Response 5XX: Server error. The body carries a requestId to quote to support.
GET /v1/webhooks/events
List the catalog of event types that can be subscribed to, each with a label and a description of what it carries. Only content.recalled and content.restored events are actually sent today; other listed events are not yet emitted.
Notes.
- data per event — screen.offline: screenId, screenName, status, nodeId, nodeName, lastSeenAt, upForSec, scheduledOff. screen.online: screenId, screenName, status, nodeId, nodeName, lastSeenAt, offlineSince, offlineForSec. approval.requested: requestId, contentKind, contentId, contentName, nodeId, nodeName, requestedById, requestedByName, requestedAt, note. approval.decided: the same plus decision (approved | rejected | withdrawn), decidedById, decidedByName, decidedAt, decisionNote. emergency.started: emergencyId, headline, body, severity, contentKind, contentId, scopeKind, scopeNodeId, scopeName, screenCount, triggeredById, triggeredByName, triggeredAt, expiresAt, templateId, templateName. emergency.cleared: the same plus clearedAt, clearedById, clearedByName, reason (cleared | expired | replaced | content-deleted). content.recalled: kind, id, name, reason, recalledBy. content.restored: kind, id, name.
- screen.offline is sent once per outage, when Brix marks the screen offline (seconds after a socket drop, up to 45 minutes for a screen that reports only by heartbeat). A screen that drops again before it is back is the same outage. screen.online follows when it reports again, within about 5 minutes.
curl "https://api.brixsignage.com/v1/webhooks/events" \
-H "Authorization: Bearer $BRIX_API_KEY" Response 200: Success.
| Field | Type | Description |
|---|---|---|
data | array of object | |
data[].event | "screen.offline" | "screen.online" | "content.recalled" | "content.restored" | "approval.requested" | "approval.decided" | "emergency.started" | "emergency.cleared" | |
data[].label | string | |
data[].what | string | What the event means and carries. |
data[].fields | array of string | The keys of the delivery's data object. Every key is always sent (null when unknown). Keys are added over time, never renamed or removed. |
Response 401: Missing, expired or revoked bearer token.
Response 403: The token lacks the permission this operation needs (see x-brix-permission).
Response 5XX: Server error. The body carries a requestId to quote to support.