Webhooks
Configure a workspace event webhook over the REST API, verify the HMAC signature, and handle the call.completed and sms.received event payloads.
Webhooks
Agoralia forwards workspace events (completed calls, inbound messages) to an HTTPS URL you control. You self-serve the configuration over the REST API: the destination URL, the subscribed event types, and a signing secret.
The two endpoints require the scopes webhooks:read and
webhooks:write.
Configure a webhook
Read the current config
curl https://api.agoralia.app/api/v1/webhooks \
-H "X-API-Key: ag_your_key_here"The secret is never returned — only whether one is set:
{
"url": "https://example.com/agoralia/hook",
"events": ["call.completed"],
"secret_set": true,
"signature_header": "X-Agoralia-Signature"
}Set the URL, events, and secret
PUT /webhooks updates any of: the destination url, the subscribed events, and the
signing secret (via rotate_secret). The signing secret is returned only when it is
generated — on first set, or when you pass rotate_secret: true. Store it immediately;
it can't be retrieved again.
curl -X PUT https://api.agoralia.app/api/v1/webhooks \
-H "X-API-Key: ag_your_key_here" \
-H "Content-Type: application/json" \
-d '{
"url": "https://example.com/agoralia/hook",
"events": ["call.completed", "sms.received"],
"rotate_secret": true
}'{
"url": "https://example.com/agoralia/hook",
"events": ["call.completed", "sms.received"],
"signature_header": "X-Agoralia-Signature",
"secret": "k3y...store-this-now"
}| Field | Type | Notes |
|---|---|---|
url | string | null | Destination URL. Empty string or null disables forwarding. |
events | string[] | null | Subscribed event types. An empty list means all events. |
rotate_secret | bool | Generate a new signing secret and return it once. A secret is also generated automatically the first time you set a URL with no secret yet. |
Delivery & headers
Events are delivered as an HTTP POST with a JSON body. Delivery is best-effort with a
small retry budget (up to 3 attempts; 4xx responses are not retried). Each request
carries:
| Header | Value |
|---|---|
Content-Type | application/json |
X-Agoralia-Event | The event type, e.g. call.completed. |
X-Agoralia-Signature | sha256=<hex> HMAC of the raw body (only when a secret is set). |
Verifying the signature
When a secret is set, the body is signed with HMAC-SHA256 and sent in the
X-Agoralia-Signature header as sha256=<hex>. Compute the same HMAC over the raw
request body with your secret and compare in constant time.
import hashlib, hmac
def verify(secret: str, raw_body: bytes, signature_header: str) -> bool:
expected = "sha256=" + hmac.new(secret.encode(), raw_body, hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, signature_header)Event envelope
Every event shares the same envelope; the event-specific fields are under data.
{
"id": "call_or_message_id",
"type": "call.completed",
"created_at": "2026-01-01T12:00:00+00:00",
"workspace_id": "ws_...",
"data": { }
}Events
call.completed
Fired when a call finishes. The caller-supplied metadata is included verbatim so you
can correlate the event back to your own records.
{
"call_id": "...",
"provider_call_id": "...",
"campaign_id": "...",
"lead_id": "...",
"direction": "outbound",
"status": "completed",
"outcome": "...",
"duration_seconds": 92,
"from_number": "+15551230000",
"to_number": "+15559876543",
"ended_reason": "...",
"summary": "...",
"structured_data": { },
"success_evaluation": "...",
"analysis": { },
"metadata": { }
}sms.received
Fired when an inbound SMS reply arrives on a workspace number. Correlate by
from/to and the stored conversation_id.
{
"message_id": "...",
"conversation_id": "...",
"from": "+15559876543",
"to": "+15551230000",
"body": "..."
}Related
- API Endpoints — full endpoint reference.
- Scopes —
webhooks:read/webhooks:write. - Errors & Rate Limits — status codes and 429 handling.
Errors & Rate Limits
HTTP status codes the Agoralia API returns, the error body shape, per-plan rate limits and 429 handling, and how idempotency works.
Troubleshooting
Fixes for the most common Agoralia errors — 401/422 on REST, "agent not synced", 429 rate limits, recording forbidden by country, MCP OAuth, and CLI auth.