Agoralia docs
Reference

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

GET /webhooks
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.

PUT /webhooks
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
      }'
Response (secret shown once)
{
  "url": "https://example.com/agoralia/hook",
  "events": ["call.completed", "sms.received"],
  "signature_header": "X-Agoralia-Signature",
  "secret": "k3y...store-this-now"
}
FieldTypeNotes
urlstring | nullDestination URL. Empty string or null disables forwarding.
eventsstring[] | nullSubscribed event types. An empty list means all events.
rotate_secretboolGenerate 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:

HeaderValue
Content-Typeapplication/json
X-Agoralia-EventThe event type, e.g. call.completed.
X-Agoralia-Signaturesha256=<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.

Verify (Python)
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.

data
{
  "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.

data
{
  "message_id": "...",
  "conversation_id": "...",
  "from": "+15559876543",
  "to": "+15551230000",
  "body": "..."
}