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"
}
📘

Tip

Leave filter_types empty (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 svix
from 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 dict

Node.js:

npm install svix
const { 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);
⚠️

Important

Always 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 typeDescription
screening.execution.status_changedFired when a screening execution status changes (e.g. CREATEDPROCESSINGPROCESSING_DONE)
screening.response.status_changedFired when a screening response status changes (e.g. CREATEDGENERATINGANSWERED_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"
}
FieldTypeDescription
event_typestringAlways screening.execution.status_changed
workspace_idUUIDThe workspace this screening belongs to
timestampdatetimeWhen the event occurred (ISO 8601)
screening_idUUIDThe screening ID
execution_idUUIDThe specific execution that changed
statusstringThe new status
previous_statusstringThe 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"
}
FieldTypeDescription
event_typestringAlways screening.response.status_changed
workspace_idUUIDThe workspace this screening belongs to
timestampdatetimeWhen the event occurred (ISO 8601)
screening_idUUIDThe screening ID
execution_idUUIDThe execution this response belongs to
response_idUUIDThe specific response that changed
question_idUUIDThe question this response answers
statusstringThe new status
previous_statusstringThe 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"
📘

Note

Deleting 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:

HeaderDescription
svix-idUnique message identifier
svix-timestampUnix timestamp (seconds) when the message was sent
svix-signatureThe 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");
  }
});
⚠️

Important

Use 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.