Webhooks
Webhooks deliver real-time notifications to your server when events occur in your AgentPhone project. Each project has a single master webhook endpoint that receives all events.
You can also configure per-agent webhooks to route events for specific agents to different endpoints. See the Agent Webhooks guide.
Events
All inbound messages (SMS, iMessage, and voice) are delivered as a unified agent.message event. The channel field tells you the source. When a call ends, agent.call_ended delivers the full transcript and call analysis. iMessage reactions trigger agent.reaction.
Webhook payload
Each webhook delivery includes the following structure. SMS, iMessage, and voice share the same top-level format:
agent.message fields by channel
agent.message keeps one envelope and changes only the data shape by channel:
For voice events, the data field contains the transcript instead:
When a call ends, agent.call_ended delivers the full transcript (not limited by contextLimit), call duration, and optional analysis:
agent.call_ended is fire-and-forget — it does not expect a response body. Return 200 OK to acknowledge receipt.
When someone reacts to an iMessage your agent sent, agent.reaction is delivered:
The reactionType is one of: love, like, dislike, laugh, emphasize, question.
Voice webhook responses
For voice events, your webhook must return a JSON object ({...}) that tells the agent what to say. Non-object responses (numbers, strings, arrays) are ignored and the caller hears silence.
Streaming response (recommended): Return Content-Type: application/x-ndjson with newline-delimited JSON chunks. TTS starts speaking on the first chunk while your server continues processing.
Mark interim chunks with "interim": true — the final chunk (without interim) closes the turn. Voice webhooks have a 30-second default timeout (configurable from 5–120 seconds per webhook) — always stream an interim chunk before doing slow work like LLM tool calls.
Simple response: Return a single JSON object for instant replies.
Security
Each webhook delivery includes these headers:
The signature is computed over the timestamp + body: the signed string is {timestamp}.{raw_body}, hashed with HMAC-SHA256 using your webhook secret. Always verify the timestamp is within 5 minutes to prevent replay attacks.
Verification example (Node.js)
Verification example (Python)
Retry behavior
If your webhook endpoint fails or doesn’t respond, we automatically retry delivery with exponential backoff:
After 5 retries (6 total attempts), the delivery is marked as failed. You can view failed deliveries via GET /v1/webhooks/deliveries. Always return 200 OK quickly to avoid retries — process webhooks asynchronously if needed.
Handling duplicate deliveries
Due to retries, your endpoint may receive the same webhook multiple times. Use the X-Webhook-ID header for idempotency:
Conversation state
Store custom metadata on conversations to persist context across messages. This state is included in every webhook payload as conversationState.
This metadata appears in subsequent webhook payloads as conversationState, enabling your AI backend to maintain context across messages without managing state yourself.
Create or update webhook
Configure the webhook endpoint for your project. Each project can have one active master webhook. If a webhook already exists, it will be updated.
Request body
A new signing secret is generated each time you create or update a webhook. Save the secret value from the response.
Example
Get webhook
Get the current webhook configuration for your project. Returns null if no webhook is configured.
Delete webhook
Remove the master webhook configuration. Events will no longer be delivered.
Example
Webhook deliveries
View the delivery history for your master webhook to monitor delivery status and debug issues.
Query parameters
Example
Test webhook
Send a test webhook to verify your endpoint is working correctly. This sends a fake message payload to your configured URL.
