- every call of one agent, or of the whole workspace, including inbound calls to your numbers and WhatsApp calls
- campaign lifecycle: started, paused, resumed, finished, stopped
- wallet alerts: balance low, calls refused for balance, balance back up
- payments: payment received, payment failed
webhook_url, the campaign webhook_url and a phone number’s webhook_url keep working as described in Webhooks.
If you use both for the same call or campaign, both receive it.
Set it up
All routes use your API key and act on the key’s workspace.1
Create an endpoint
Response (201)
headers (up to 16) are stored encrypted, and reads show only their names.2
Subscribe it to events
"scope": {"type": "agent", "agent_id": "agt_…"}. Wallet and payment events belong to the whole workspace, so an agent subscription cannot list them (the API answers 400).3
Send a test
"test": true. The 202 response is the queued delivery. Look it up under deliveries.GET /v2/webhooks/event-types lists every event with its label, group, one-line description, scopes, JSON schema and an example payload.
Event types
Calls
Scope: one agent or the whole workspace.data.call is the call object.
Campaigns
Scope: one agent (the campaigns that agent runs) or the whole workspace.data.campaign is the same object, with the same event id, that the campaign’s own webhook_url receives.
Wallet and payments
Scope: the whole workspace only.
The balance in these events is the one
GET /v2/wallet shows: what you can spend, after calls in progress. We check it about once a minute, so wallet.balance_low reaches you within a minute of the crossing. A top-up, a call or an API charge can each cause one.
If several workspaces share your company’s wallet, each workspace with a subscription receives the payment events.
Low balance alert level
The alert level is ₹500 until you change it.Response
GET /v2/webhooks/settings reads it. Set it to 0 to turn wallet.balance_low off; wallet.depleted still fires. Pass if_revision if you want a stale write refused.
Payloads
Every event has the same envelope.wallet.balance_low
wallet.balance_low
wallet.depleted
wallet.depleted
required_inr is what the refused call needed to start. data.call is absent when a campaign paused before placing a call.campaign.completed
campaign.completed
payment.succeeded
payment.succeeded
kind is top_up, monthly_payment, plan_upgrade or subscription. amount_inr is the amount credited, before GST. payment.failed carries the same payment object with a reason in the bank’s words.GET /v2/webhooks/event-types.
Verify the signature
Every delivery is signed with the endpoint’ssigning_secret, using the same scheme as call webhooks:
v1 is the lowercase hex HMAC-SHA256(signing_secret, "<t>.<raw_body>"). Reject a timestamp more than 300 seconds from now, and compare in constant time.
POST /v2/webhooks/endpoints/{id}/rotate-secret with {"if_revision": N} returns a new secret that starts signing five minutes later. Install it alongside the old one before then, and keep the old one for an hour. X-Mirai-Key-Version tells you which secret signed a delivery.
Deliveries and retries
Delivery is at least once. Return any2xx after you have stored the event; store id with a unique constraint so a repeat does nothing. Events can arrive out of order, so act on created_at and the object’s state, not arrival order.
A failed delivery is retried after 1 second, 7 seconds, 49 seconds, about 6 minutes, then every 30 minutes, for up to 24 hours. A Retry-After header from you is respected.
Manage endpoints and subscriptions
Creating an endpoint, rotating, testing and retrying accept an
Idempotency-Key header, so a retried request never creates a second endpoint or secret. Two subscriptions that match the same event for the same endpoint produce one delivery.
Checklist
- Endpoint is public HTTPS on port 443 and answers
2xxwithin a few seconds. signing_secretstored; signature checked on the raw body; old timestamps rejected.- Events stored by
idwith a unique constraint. - Alert level set to the balance at which you want to top up.
- A test event sent and seen in your logs.