ExoVoIPDeveloper guide

Webhooks

Webhooks send phone system events to your systems with a signed HTTPS request: no connection to keep open. Scopes webhooks:read / webhooks:write; at most 10 webhooks per tenant.

Creating a webhook

curl -s -X POST https://centralino.exovoip.it/api/v1/webhooks \
  -H "X-API-Key: $EXOPBX_API_KEY" -H "Content-Type: application/json" \
  -d '{"name":"CRM","url":"https://crm.example.com/exopbx","events":["channel.hangup","calendar.override"]}'
curl -s -X POST https://centralino.exovoip.it/api/v1/webhooks/2/test -H "X-API-Key: $EXOPBX_API_KEY"
  • events: event types (the same as real-time) or ["*"] for all. The list of available types is at GET /v1/webhooks/event-types.
  • The whsec_… secret is shown only on creation and rotation (POST /v1/webhooks/:id/rotate-secret).
  • POST /v1/webhooks/:id/test immediately sends a webhook.test event and returns the result.

Request sent

POST to the webhook URL, JSON body:

{
  "id": "7f1c1f4e-2c1b-4d4e-9d61-0b8a2b8e6c55",
  "type": "channel.hangup",
  "tenantId": 3,
  "createdAt": "2026-11-04T10:00:00.000Z",
  "data": { "type": "channel.hangup", "uniqueid": "1762250400.42", "durationS": 95 }
}
HeaderContent
X-ExoPBX-Signaturet=<unix>,v1=<hmac>: signature to verify
X-ExoPBX-TimestampTime of sending (Unix seconds)
X-ExoPBX-EventEvent type
X-ExoPBX-DeliveryEvent id, the same across all attempts: use it to discard duplicates
User-AgentExoPBX-Webhook/1.0

Verifying the signature

The signature is the hex-encoded HMAC-SHA256 of the string <t>.<raw body>, computed with the webhook secret. Reject requests with a timestamp older than 5 minutes. Use the body exactly as received: re-serializing the JSON changes the signature.

# Verifica manuale con openssl: firma = HMAC-SHA256("<t>.<corpo>")
printf '%s.%s' "$T" "$BODY" | openssl dgst -sha256 -hmac "$EXOPBX_WEBHOOK_SECRET" -hex

Complete endpoint examples (Node, Flask, PHP) are included in the SDK archives, in the examples folder.

Delivery and retries

  • Success: any 2xx response within 10 seconds. Respond immediately and process the event afterwards.
  • Otherwise, retries after 30 s, 2 min, 8 min, 32 min, about 2 h, 6 h and 6 h (8 attempts in total); after that the delivery stays failed and can be retried from the log.
  • Redirects are not followed; the URL must be public (http or https, https recommended).
  • Re-enabling a webhook that was disabled after consecutive errors resets the counter.
curl -s "https://centralino.exovoip.it/api/v1/webhooks/2/deliveries?status=failed" -H "X-API-Key: $EXOPBX_API_KEY"
curl -s -X POST https://centralino.exovoip.it/api/v1/webhooks/2/deliveries/918/retry -H "X-API-Key: $EXOPBX_API_KEY"
curl -s -X POST https://centralino.exovoip.it/api/v1/webhooks/2/rotate-secret -H "X-API-Key: $EXOPBX_API_KEY"

The delivery log is kept for 30 days.

Endpoints

RequestScopeSDK method
GET /v1/webhooks/event-typeswebhooks:readwebhooks.eventTypes() · py: webhooks.event_types()
GET /v1/webhookswebhooks:readwebhooks.list()
POST /v1/webhookswebhooks:writewebhooks.create()
GET /v1/webhooks/:idwebhooks:readwebhooks.get()
PATCH /v1/webhooks/:idwebhooks:writewebhooks.update()
DELETE /v1/webhooks/:idwebhooks:writewebhooks.delete()
POST /v1/webhooks/:id/rotate-secretwebhooks:writewebhooks.rotateSecret() · py: webhooks.rotate_secret()
POST /v1/webhooks/:id/testwebhooks:writewebhooks.test()
GET /v1/webhooks/:id/deliverieswebhooks:readwebhooks.deliveries()
POST /v1/webhooks/:id/deliveries/:deliveryId/retrywebhooks:writewebhooks.redeliver()