Webhook Administration
Webhook Administration
Section titled “Webhook Administration”Arbitex can push real-time event notifications to external HTTP endpoints via webhooks. When a subscribed event fires, Arbitex delivers a signed HTTP POST to the configured URL. All webhook management endpoints require an admin user.
Webhook Concepts
Section titled “Webhook Concepts”| Concept | Description |
|---|---|
| Webhook | A named subscription pairing a URL with one or more event types |
| Event type | A string identifying the kind of event (e.g., "new_conversation") |
| Secret | A shared string used to compute the HMAC-SHA256 signature on each delivery |
| Delivery | A single HTTP POST attempt to the webhook URL for a specific event |
Supported Event Types
Section titled “Supported Event Types”| Event type | When it fires |
|---|---|
new_conversation | A user creates a new conversation |
dlp_trigger | A DLP rule matches content in a message |
quota_exceeded | A user or group quota is exceeded |
bundle_state_change | A compliance bundle changes state (enabled/disabled/acknowledged) |
A webhook can subscribe to multiple event types in the same registration.
Managing Webhooks
Section titled “Managing Webhooks”All endpoints are prefixed /api/admin/webhooks and require an admin JWT.
List Webhooks
Section titled “List Webhooks”GET /api/admin/webhooks/Authorization: Bearer <admin-token>Returns all webhooks for the tenant, ordered by created_at descending.
Response 200 OK:
[ { "id": "3f8a1b2c-...", "name": "SIEM Integration", "url": "https://siem.example.com/arbitex/events", "events": ["dlp_trigger", "quota_exceeded"], "enabled": true, "created_at": "2026-03-01T10:00:00Z", "updated_at": "2026-03-01T10:00:00Z" }]Get Webhook Detail
Section titled “Get Webhook Detail”GET /api/admin/webhooks/{webhook_id}Authorization: Bearer <admin-token>Returns the webhook plus its 20 most recent delivery attempts.
Response 200 OK:
{ "id": "3f8a1b2c-...", "name": "SIEM Integration", "url": "https://siem.example.com/arbitex/events", "events": ["dlp_trigger"], "enabled": true, "created_at": "2026-03-01T10:00:00Z", "updated_at": "2026-03-01T10:00:00Z", "recent_deliveries": [ { "id": "d1a2b3c4-...", "webhook_id": "3f8a1b2c-...", "event_type": "dlp_trigger", "payload": { ... }, "status": "delivered", "attempts": 1, "last_error": null, "response_status": 200, "created_at": "2026-03-12T14:22:00Z" } ]}Create a Webhook
Section titled “Create a Webhook”POST /api/admin/webhooks/Authorization: Bearer <admin-token>Content-Type: application/json
{ "name": "SIEM Integration", "url": "https://siem.example.com/arbitex/events", "events": ["dlp_trigger", "quota_exceeded"], "secret": "my-shared-secret-32chars+", "enabled": true}Field constraints:
| Field | Constraint |
|---|---|
name | 1–255 characters |
url | Valid HTTPS URL, max 2000 characters |
events | Non-empty list of valid WebhookEventType values |
secret | 8–255 characters |
enabled | Default true |
Response 201 Created — the created WebhookResponse object.
Update a Webhook
Section titled “Update a Webhook”PUT /api/admin/webhooks/{webhook_id}Authorization: Bearer <admin-token>Content-Type: application/json
{ "enabled": false}All fields are optional. Only provided fields are updated; omitted fields retain their current values.
Response 200 OK — the updated WebhookResponse object.
Delete a Webhook
Section titled “Delete a Webhook”DELETE /api/admin/webhooks/{webhook_id}Authorization: Bearer <admin-token>Response 204 No Content
Deleting a webhook also deletes all associated delivery records (cascade).
Testing Delivery
Section titled “Testing Delivery”Send a test webhook_test event to verify connectivity and HMAC signing:
POST /api/admin/webhooks/{webhook_id}/testAuthorization: Bearer <admin-token>The test payload sent to the webhook URL:
{ "event_type": "webhook_test", "payload": { "message": "This is a test webhook delivery from Arbitex.", "webhook_id": "3f8a1b2c-...", "webhook_name": "SIEM Integration" }, "timestamp": "2026-03-12T14:22:00Z"}Response 200 OK:
{ "success": true, "status_code": 200, "error": null}On failure (e.g., timeout, non-2xx response):
{ "success": false, "status_code": 503, "error": "HTTP 503: Service Unavailable"}Delivery Behaviour
Section titled “Delivery Behaviour”Request Format
Section titled “Request Format”Each delivery is an HTTP POST with these headers:
Content-Type: application/jsonX-Webhook-Signature: <hex-encoded HMAC-SHA256>The body has three top-level fields:
{ "event_type": "dlp_trigger", "payload": { ... }, "timestamp": "2026-03-12T14:22:00Z"}HMAC Signature Verification
Section titled “HMAC Signature Verification”The X-Webhook-Signature header is the HMAC-SHA256 of the raw JSON body bytes, computed with the webhook’s shared secret as the key.
To verify on your endpoint:
import hashlib, hmac
def verify_signature(body_bytes: bytes, secret: str, header_sig: str) -> bool: expected = hmac.new(secret.encode(), body_bytes, hashlib.sha256).hexdigest() return hmac.compare_digest(expected, header_sig)Always use hmac.compare_digest (constant-time comparison) to prevent timing attacks.
Retry Behaviour
Section titled “Retry Behaviour”Arbitex retries failed deliveries with exponential backoff:
| Attempt | Delay before attempt |
|---|---|
| 1 | Immediate |
| 2 | 1 second |
| 3 | 2 seconds |
After 3 attempts, the delivery record is marked "failed". No further automatic retries are performed.
Triggers for retry:
- Non-2xx HTTP response from the target URL
- Connection timeout (10 second limit per attempt)
- Network error
Triggers for immediate failure (no retry):
- URL blocked by SSRF protection (see below)
Request Timeout
Section titled “Request Timeout”Each HTTP request has a 10-second timeout. If the target endpoint does not respond within 10 seconds, the attempt is marked as a timeout error and retried.
Delivery Monitoring
Section titled “Delivery Monitoring”Delivery status is visible in the webhook detail endpoint (GET /api/admin/webhooks/{id}) via the recent_deliveries array (last 20 deliveries).
Delivery Status Values
Section titled “Delivery Status Values”| Status | Meaning |
|---|---|
"pending" | Delivery attempt in progress |
"delivered" | HTTP 2xx received from target |
"failed" | All 3 attempts exhausted without success |
Dead Letter Queue
Section titled “Dead Letter Queue”There is no automatic dead-letter queue or re-delivery mechanism. Failed deliveries remain in the webhook_deliveries table with status "failed" for audit purposes. To re-send a failed event, use the test endpoint or wait for the next occurrence of the event.
For high-reliability integrations, configure your endpoint to acknowledge all deliveries with a 200 OK immediately (even before processing), then process asynchronously from a local queue.
SSRF Protection
Section titled “SSRF Protection”The delivery service validates each webhook URL with an SSRF guard before making any HTTP request. URLs that resolve to private, loopback, link-local, or reserved IP ranges are blocked:
10.0.0.0/8,172.16.0.0/12,192.168.0.0/16127.0.0.0/8(localhost)169.254.0.0/16(link-local)- Cloud metadata endpoints (e.g.,
169.254.169.254)
Blocked deliveries are recorded with status "failed" and last_error: "URL blocked by SSRF protection: target resolves to a private, loopback, or reserved IP address". No retry is performed.
Cloud Portal Webhook Implementation
Section titled “Cloud Portal Webhook Implementation”The Cloud Portal has a separate webhook surface for outpost health and dead-letter notifications, exposed at:
GET /v1/orgs/{org_id}/notificationsThis endpoint returns:
- Outpost heartbeat health alerts (outposts not seen for more than 5 minutes)
- Webhook dead-letter notifications (deliveries that failed after all retries in cloud-connected orgs)
Portal-level notifications are distinct from the platform webhook subscriptions documented above. Platform webhooks fire on conversation/DLP/quota/compliance events; portal notifications surface infrastructure health for operators.
Troubleshooting
Section titled “Troubleshooting”Deliveries Are Not Arriving
Section titled “Deliveries Are Not Arriving”- Check
GET /api/admin/webhooks/{id}— reviewrecent_deliveriesforlast_errorvalues. - Confirm the webhook is
"enabled": true. - Test connectivity with
POST /api/admin/webhooks/{id}/test. - Verify the target URL is reachable from the Arbitex server and is not a private IP.
Signature Verification Failures
Section titled “Signature Verification Failures”- Ensure you compute HMAC-SHA256 over the raw request body bytes (not a re-serialised JSON object).
- Confirm the secret used in your verification matches the secret set during webhook creation. Secrets are not returned by
GETendpoints — if you have lost the secret, update the webhook with a new one viaPUT.
Event Types Not Matching
Section titled “Event Types Not Matching”- Verify the
event_typestring in the received payload matches what your handler expects. - Ensure the webhook’s
eventsarray includes the event type you want to receive.
Connection Timeout
Section titled “Connection Timeout”- Ensure your endpoint responds within 10 seconds. For slow consumers, respond
200 OKimmediately and process the payload asynchronously.