Webhooks
Push real-time notifications to your server whenever something happens in your screenings — no polling required. Register an endpoint, and we'll send you an HTTP request every time a screening execution or response changes status.
Quick start
Step 1: Register an endpoint
Tell us where to send events by creating a webhook endpoint:
curl -X POST https://api.platform.briink.com/workspaces/{workspace_id}/webhooks/endpoints \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"url": "https://your-server.com/webhooks",
"description": "Production webhook receiver",
"filter_types": ["screening.execution.status_changed"]
}'Response:
{
"id": "ep_2abc3def4ghi",
"url": "https://your-server.com/webhooks",
"description": "Production webhook receiver",
"filter_types": ["screening.execution.status_changed"],
"disabled": false,
"created_at": "2026-03-17T10:00:00Z",
"updated_at": "2026-03-17T10:00:00Z"
}
TipLeave
filter_typesempty (or omit it) to receive all event types.
Step 2: Receive events
When a screening execution changes status, your endpoint receives a POST request like this:
{
"event_type": "screening.execution.status_changed",
"workspace_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"timestamp": "2026-03-17T10:05:00Z",
"screening_id": "d4e5f6a7-b8c9-0123-d456-e78901234567",
"execution_id": "f6a7b8c9-d0e1-2345-f678-901234567890",
"status": "PROCESSING_DONE",
"previous_status": "PROCESSING"
}Your server should return a 2xx status code to acknowledge receipt.
Step 3: Verify signatures
Every webhook request includes signature headers so you can verify it came from Briink. Use the svix library to verify:
Python:
pip install svixfrom svix.webhooks import Webhook
# Your endpoint's signing secret (from the endpoint creation response)
secret = "whsec_..."
# Use the raw request body (bytes, not parsed JSON)
payload = request.body
headers = {
"svix-id": request.headers["svix-id"],
"svix-timestamp": request.headers["svix-timestamp"],
"svix-signature": request.headers["svix-signature"],
}
wh = Webhook(secret)
event = wh.verify(payload, headers)
# event is now the verified payload dictNode.js:
npm install svixconst { Webhook } = require("svix");
const secret = "whsec_...";
const wh = new Webhook(secret);
const payload = req.body; // raw body as string or Buffer
const headers = {
"svix-id": req.headers["svix-id"],
"svix-timestamp": req.headers["svix-timestamp"],
"svix-signature": req.headers["svix-signature"],
};
const event = wh.verify(payload, headers);
ImportantAlways verify using the raw request body (before any JSON parsing). Parsing and re-serializing can change the byte representation and break the signature check.
Event types
| Event type | Description |
|---|---|
screening.execution.status_changed | Fired when a screening execution status changes (e.g. CREATED → PROCESSING → PROCESSING_DONE) |
screening.response.status_changed | Fired when a screening response status changes (e.g. CREATED → GENERATING → ANSWERED_BY_AI) |
You can list all available event types via the API:
curl https://api.platform.briink.com/workspaces/{workspace_id}/webhooks/event-types \
-H "x-api-key: YOUR_API_KEY"Event payloads
screening.execution.status_changed
Sent when a screening execution transitions between statuses.
{
"event_type": "screening.execution.status_changed",
"workspace_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"timestamp": "2026-03-17T10:05:00Z",
"screening_id": "d4e5f6a7-b8c9-0123-d456-e78901234567",
"execution_id": "f6a7b8c9-d0e1-2345-f678-901234567890",
"status": "PROCESSING_DONE",
"previous_status": "PROCESSING"
}| Field | Type | Description |
|---|---|---|
event_type | string | Always screening.execution.status_changed |
workspace_id | UUID | The workspace this screening belongs to |
timestamp | datetime | When the event occurred (ISO 8601) |
screening_id | UUID | The screening ID |
execution_id | UUID | The specific execution that changed |
status | string | The new status |
previous_status | string | The status before the change |
screening.response.status_changed
Sent when an individual screening response transitions between statuses.
{
"event_type": "screening.response.status_changed",
"workspace_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"timestamp": "2026-03-17T10:06:00Z",
"screening_id": "d4e5f6a7-b8c9-0123-d456-e78901234567",
"execution_id": "f6a7b8c9-d0e1-2345-f678-901234567890",
"response_id": "c9d0e1f2-a3b4-5678-c901-234567890abc",
"question_id": "b8c9d0e1-f2a3-4567-b890-123456789def",
"status": "ANSWERED_BY_AI",
"previous_status": "GENERATING"
}| Field | Type | Description |
|---|---|---|
event_type | string | Always screening.response.status_changed |
workspace_id | UUID | The workspace this screening belongs to |
timestamp | datetime | When the event occurred (ISO 8601) |
screening_id | UUID | The screening ID |
execution_id | UUID | The execution this response belongs to |
response_id | UUID | The specific response that changed |
question_id | UUID | The question this response answers |
status | string | The new status |
previous_status | string | The status before the change |
Managing endpoints
List all endpoints
curl https://api.platform.briink.com/workspaces/{workspace_id}/webhooks/endpoints \
-H "x-api-key: YOUR_API_KEY"Get a specific endpoint
curl https://api.platform.briink.com/workspaces/{workspace_id}/webhooks/endpoints/{endpoint_id} \
-H "x-api-key: YOUR_API_KEY"Update an endpoint
Change the URL, description, event filters, or disable/enable the endpoint:
curl -X PUT https://api.platform.briink.com/workspaces/{workspace_id}/webhooks/endpoints/{endpoint_id} \
-H "x-api-key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"filter_types": [
"screening.execution.status_changed",
"screening.response.status_changed"
],
"disabled": false
}'Delete an endpoint
curl -X DELETE https://api.platform.briink.com/workspaces/{workspace_id}/webhooks/endpoints/{endpoint_id} \
-H "x-api-key: YOUR_API_KEY"
NoteDeleting an endpoint is permanent. Any in-flight events for that endpoint will still be retried until they expire.
Verifying webhook signatures
Every webhook request sent by Briink is signed so you can be sure it's authentic. We use Svix under the hood, and each request includes three headers:
| Header | Description |
|---|---|
svix-id | Unique message identifier |
svix-timestamp | Unix timestamp (seconds) when the message was sent |
svix-signature | The signature(s) to verify against |
Why verify?
Without verification, anyone who discovers your endpoint URL could send fake events. Always verify in production.
Python
from svix.webhooks import Webhook
def handle_webhook(request):
secret = "whsec_..." # your endpoint signing secret
wh = Webhook(secret)
payload = request.body # raw bytes
headers = {
"svix-id": request.headers["svix-id"],
"svix-timestamp": request.headers["svix-timestamp"],
"svix-signature": request.headers["svix-signature"],
}
try:
event = wh.verify(payload, headers)
except Exception:
return HttpResponse(status=400)
# Process the verified event
if event["event_type"] == "screening.execution.status_changed":
handle_execution_update(event)
elif event["event_type"] == "screening.response.status_changed":
handle_response_update(event)
return HttpResponse(status=200)Node.js
const { Webhook } = require("svix");
app.post("/webhooks", (req, res) => {
const secret = "whsec_..."; // your endpoint signing secret
const wh = new Webhook(secret);
try {
const event = wh.verify(req.body, {
"svix-id": req.headers["svix-id"],
"svix-timestamp": req.headers["svix-timestamp"],
"svix-signature": req.headers["svix-signature"],
});
// Process the verified event
if (event.event_type === "screening.execution.status_changed") {
handleExecutionUpdate(event);
} else if (event.event_type === "screening.response.status_changed") {
handleResponseUpdate(event);
}
res.status(200).send();
} catch (err) {
res.status(400).send("Invalid signature");
}
});
ImportantUse the raw request body for verification. If you're using a framework that auto-parses JSON (like Express), configure it to pass through the raw body. In Express, use
app.use(express.raw({ type: "application/json" }))for webhook routes.
Retry behavior
If your endpoint returns a non-2xx response (or doesn't respond at all), we'll automatically retry the delivery with exponential backoff. Retries continue over the course of several hours, giving your server time to recover from temporary issues.
Each event has a unique svix-id header, so you can safely deduplicate if your endpoint receives the same event more than once.
Updated 3 days ago