Webhooks — configuration and reliability
Arbitex webhooks push real-time event notifications to an HTTP endpoint of your choice. When a subscribed event occurs — a new conversation, a DLP rule trigger, a quota breach, a compliance bundle state change, or a billing event — Arbitex sends a signed JSON payload to your configured URL within seconds.
Common use cases include feeding Arbitex events into SIEM pipelines, triggering remediation workflows, updating compliance dashboards, and syncing audit records to external systems.
All webhook management endpoints require an admin role. The router prefix is /api/admin/webhooks.
Overview
Section titled “Overview”Each webhook registration binds one URL to one or more event types. When a matching event fires, Arbitex delivers an HTTP POST request to your endpoint with:
- A JSON body describing the event and its payload
- An
X-Arbitex-Signatureheader containing an HMAC-SHA256 signature you can use to verify authenticity - Automatic retry logic on delivery failure
Delivery is best-effort with up to three attempts. See Retry policy for timing details.
Registering a webhook
Section titled “Registering a webhook”Send a POST request to create a new webhook registration.
POST /api/admin/webhooks/Request body:
{ "name": "My SIEM Integration", "url": "https://ingest.example.com/arbitex-events", "events": ["new_conversation", "dlp_trigger"], "secret": "your-signing-secret", "enabled": true}Example:
curl -s -X POST https://api.arbitex.ai/api/admin/webhooks/ \ -H "Authorization: Bearer $ADMIN_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "name": "My SIEM Integration", "url": "https://ingest.example.com/arbitex-events", "events": ["new_conversation", "dlp_trigger"], "secret": "your-signing-secret", "enabled": true }'Field descriptions:
| Field | Required | Description |
|---|---|---|
name | Yes | Human-readable label for this webhook |
url | Yes | HTTPS endpoint that will receive POST requests |
events | Yes | List of event types to subscribe to — at least one required |
secret | Yes | Signing secret for HMAC-SHA256 signature verification — 8 to 255 characters |
enabled | No | Whether the webhook is active; defaults to true |
The response includes the assigned webhook_id, which you use for all subsequent operations on this registration.
Supported event types
Section titled “Supported event types”Subscribe to any combination of the following event types. Pass them as string values in the events array.
Platform events
| Event type | When it fires |
|---|---|
new_conversation | A new conversation is created on the platform |
dlp_trigger | A DLP policy rule matches content in a conversation |
quota_exceeded | A user or group reaches or exceeds their configured usage quota |
bundle_state_change | A compliance bundle transitions to a new state (e.g., active, archived) |
Billing events (cloud-0020)
| Event type | When it fires |
|---|---|
quota_exceeded | Current monthly request count reaches the plan limit |
usage_threshold | Usage reaches 80%, 90%, or 100% of the monthly limit |
invoice_generated | A new invoice is created for the organization |
A single webhook can subscribe to multiple event types simultaneously.
Managing webhooks
Section titled “Managing webhooks”List all webhooks
Section titled “List all webhooks”Returns all webhook registrations for the tenant.
GET /api/admin/webhooks/curl -s https://api.arbitex.ai/api/admin/webhooks/ \ -H "Authorization: Bearer $ADMIN_TOKEN"View a webhook and its delivery history
Section titled “View a webhook and its delivery history”Returns the webhook registration details along with the last 20 delivery records.
GET /api/admin/webhooks/{webhook_id}curl -s https://api.arbitex.ai/api/admin/webhooks/wh_01abc123 \ -H "Authorization: Bearer $ADMIN_TOKEN"Update a webhook
Section titled “Update a webhook”Use a PUT request to update any combination of fields. All fields are optional — only the fields you include are changed.
PUT /api/admin/webhooks/{webhook_id}# Disable a webhook temporarilycurl -s -X PUT https://api.arbitex.ai/api/admin/webhooks/wh_01abc123 \ -H "Authorization: Bearer $ADMIN_TOKEN" \ -H "Content-Type: application/json" \ -d '{"enabled": false}'
# Update the URL and add an event typecurl -s -X PUT https://api.arbitex.ai/api/admin/webhooks/wh_01abc123 \ -H "Authorization: Bearer $ADMIN_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "url": "https://ingest.example.com/arbitex-events-v2", "events": ["new_conversation", "dlp_trigger", "quota_exceeded"] }'
# Rotate the signing secretcurl -s -X PUT https://api.arbitex.ai/api/admin/webhooks/wh_01abc123 \ -H "Authorization: Bearer $ADMIN_TOKEN" \ -H "Content-Type: application/json" \ -d '{"secret": "new-signing-secret-min8"}'Updatable fields: name, url, events, secret, enabled.
Delete a webhook
Section titled “Delete a webhook”Permanently removes the webhook registration and stops all future deliveries.
DELETE /api/admin/webhooks/{webhook_id}curl -s -X DELETE https://api.arbitex.ai/api/admin/webhooks/wh_01abc123 \ -H "Authorization: Bearer $ADMIN_TOKEN"Retry policy
Section titled “Retry policy”When a delivery attempt fails (non-2xx response, connection error, or 10-second timeout), Arbitex retries up to two additional times using exponential backoff:
| Attempt | Delay before attempt |
|---|---|
| 1 (initial) | Immediate |
| 2 | 1 second |
| 3 | 5 seconds |
| (if 3 fails) | Delivery moves to dead letter queue after 30-second wait |
The request timeout per attempt is 10 seconds. Connections that do not complete within 10 seconds are treated as failures and trigger the retry sequence.
Dead letter queue
Section titled “Dead letter queue”If all three attempts fail, the delivery record status is set to dead_letter. Dead letter deliveries are not retried automatically. To re-deliver:
- Fix the issue at your endpoint (verify URL reachability, check response codes).
- Use the delivery log to identify the delivery ID.
- Re-trigger the original event (if available) or re-run from your SIEM pipeline using the delivery log payload.
Check the delivery log regularly for dead_letter entries. A pattern of dead letter deliveries indicates a persistent connectivity or endpoint configuration issue.
HMAC signature verification
Section titled “HMAC signature verification”Every delivery includes an X-Arbitex-Signature header. Verifying this signature confirms the request originated from Arbitex and that the body has not been tampered with in transit.
Header format:
X-Arbitex-Signature: sha256=<hex_digest>Algorithm: HMAC-SHA256, computed over the raw request body bytes using the secret you provided at webhook creation.
Python
Section titled “Python”import hmacimport hashlib
def verify_arbitex_signature(raw_body: bytes, secret: str, signature_header: str) -> bool: """ Verify an Arbitex webhook signature.
Args: raw_body: The raw, unmodified request body bytes. secret: The signing secret configured on the webhook. signature_header: The value of the X-Arbitex-Signature header.
Returns: True if the signature is valid, False otherwise. """ expected_prefix = "sha256=" if not signature_header.startswith(expected_prefix): return False
received_digest = signature_header[len(expected_prefix):] expected_digest = hmac.new( key=secret.encode("utf-8"), msg=raw_body, digestmod=hashlib.sha256, ).hexdigest()
return hmac.compare_digest(expected_digest, received_digest)
# Example usage in a Flask handlerfrom flask import Flask, request, abort
app = Flask(__name__)WEBHOOK_SECRET = "your-signing-secret"
@app.route("/arbitex-events", methods=["POST"])def handle_webhook(): signature = request.headers.get("X-Arbitex-Signature", "") if not verify_arbitex_signature(request.get_data(), WEBHOOK_SECRET, signature): abort(403)
event = request.get_json() print(f"Received event: {event['event_type']}") return "", 200Always use hmac.compare_digest for the final comparison to prevent timing-based attacks.
Shell (openssl)
Section titled “Shell (openssl)”# Compute the expected digest from the raw body and secretBODY='{"event_type":"dlp_trigger","payload":{}}'SECRET="your-signing-secret"
EXPECTED=$(echo -n "$BODY" | openssl dgst -sha256 -hmac "$SECRET" | awk '{print $2}')echo "sha256=$EXPECTED"Compare the output against the X-Arbitex-Signature header value. Both must match exactly (prefix included) for the delivery to be considered authentic.
Testing webhook delivery
Section titled “Testing webhook delivery”Send a synthetic webhook_test event to verify that your endpoint is reachable and that your signature verification logic is working correctly.
POST /api/admin/webhooks/{webhook_id}/testcurl -s -X POST https://api.arbitex.ai/api/admin/webhooks/wh_01abc123/test \ -H "Authorization: Bearer $ADMIN_TOKEN"Response:
{ "success": true, "status_code": 200, "error": null}If the delivery fails, success is false, status_code reflects the HTTP response code returned by your endpoint (or null on connection failure), and error contains a description of the failure.
Use the test endpoint after initial registration, after rotating the signing secret, and after making infrastructure changes that could affect reachability.
Delivery log and monitoring
Section titled “Delivery log and monitoring”Every delivery attempt is written to the webhook delivery log. The GET /api/admin/webhooks/{webhook_id} response includes a deliveries array containing the last 20 delivery records.
Delivery record fields:
| Field | Description |
|---|---|
id | Unique delivery log ID |
webhook_id | ID of the webhook registration |
event_type | The event type that triggered this delivery |
status | Current delivery status (see below) |
attempt_count | Number of attempts made so far (1–3) |
last_attempt_at | Timestamp of the most recent attempt |
next_retry_at | Scheduled time for the next retry, or null if terminal |
response_code | HTTP status code from the last attempt, or null on connection failure |
error_message | Error detail from the last failed attempt, or null on success |
payload | The JSON body sent (truncated to 10,000 characters) |
Status values:
| Status | Description |
|---|---|
pending | Delivery is in progress (first attempt) |
delivered | At least one attempt received a 2xx response |
failed | An attempt failed; a retry is scheduled (next_retry_at is set) |
dead_letter | All three attempts failed; no further retries will occur |
Debugging failed deliveries:
If you see failed or dead_letter status:
- Check
response_code— a 4xx response from your endpoint indicates a configuration issue (wrong URL, authentication failure). - Check
error_message—"Request timed out"means your endpoint did not respond within 10 seconds. - Verify the webhook URL is publicly reachable from Arbitex infrastructure.
- Verify your endpoint returns a 2xx status code synchronously (do not defer to a background job without responding first).
- Verify the signing secret in your webhook registration matches what your endpoint expects.
Alert on failed deliveries:
Configure an alert rule to fire when dead_letter deliveries exceed a threshold:
POST /api/admin/alert-rules{ "name": "webhook-dead-letter-alert", "condition": { "metric": "webhook_dead_letter_count", "threshold": 5, "window_minutes": 60 }, "action": { "type": "email", "recipients": ["ops@example.com"] }}Delivery payload format
Section titled “Delivery payload format”All webhook deliveries use the same envelope format:
{ "event_type": "dlp_trigger", "payload": { "request_id": "req_01abc123", "user_id": "user_01def456", "rule_id": "finance-pii-block", "entity_type": "credit_card", "action_taken": "BLOCK", "timestamp": "2026-03-10T16:30:00Z" }, "timestamp": "2026-03-10T16:30:00.123456Z"}The outer timestamp is the delivery time. The inner payload.timestamp is the event time. They may differ slightly due to processing latency.
Billing event example:
{ "event_type": "usage_threshold", "payload": { "org_id": "org_01abc123", "threshold": 0.80, "current_requests": 8000, "monthly_limit": 10000, "period": "2026-03" }, "timestamp": "2026-03-10T16:30:00.123456Z"}See also
Section titled “See also”- Policy Engine admin guide — configure the DLP rules that produce
dlp_triggerevents - Admin operations — set user and group quotas that produce
quota_exceededevents - Compliance bundles — manage bundles that produce
bundle_state_changeevents - Audit log — full audit trail of admin actions including webhook management