Documentation Index
Fetch the complete documentation index at: https://docs.pam.ai/llms.txt
Use this file to discover all available pages before exploring further.
Webhooks let your integration receive real-time outbound campaign and conversation events at an HTTPS endpoint that you control.
Register a webhook with the event types you want to receive:
curl https://api.pamhq.com/v1/webhooks \
-X POST \
-H "Authorization: Bearer $PAM_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"url": "https://example.com/pam-webhooks",
"events": ["call.started", "call.ended", "sms.message_received", "sms.message_delivery_updated"]
}'
The response includes a signing secret once. Store it securely and use it to verify future webhook payloads.
Event Types
| Event | Description |
|---|
call.started | Voice call connected or started ringing. |
call.ended | Voice call disconnected before analysis is complete. |
call.analyzed | Post-call analysis is available. |
sms.message_sent | Outbound SMS sent to a contact and persisted. |
sms.message_received | Inbound SMS received from a contact and persisted. |
sms.message_delivery_updated | Canonical delivery status changed for an outbound SMS. |
sms.tool_call_invocation | SMS agent invoked a tool during the conversation loop. |
sms.tool_call_result | Tool result was persisted for an SMS conversation. |
contact.opt_out | Contact opted out of messages. |
contact.opt_in | Contact opted back in. |
Payload Shape
Webhook payloads share a common envelope:
{
"event": "sms.message_received",
"timestamp": "2026-04-10T14:00:00.000Z",
"conversationId": "b7ee8d1b-5e3e-4a1a-8f0a-9e2a4f8f1aa2",
"agentId": "2c4a1e72-7a5d-4b6e-9c2a-01a78b7b3f3f",
"clientOrgId": "acme",
"channel": "sms",
"data": {
"messageId": "f1c2b1a0-3e4d-4a5b-9c7d-0e1f2a3b4c5d",
"sequenceNumber": 1,
"from": "+15555550199",
"to": "+15555550123",
"content": "Yes please",
"direction": "inbound",
"sentAt": "2026-04-10T14:00:00.000Z"
}
}
PAM routes webhooks by clientOrgId, agentVersionId, and event type. A conversation event is delivered only to webhook subscriptions for the conversation’s client organization.
Voice Event Examples
call.started fires when the provider reports the call has started:
{
"event": "call.started",
"timestamp": "2026-04-10T14:00:02.123Z",
"conversationId": "b7ee8d1b-5e3e-4a1a-8f0a-9e2a4f8f1aa2",
"agentId": "2c4a1e72-7a5d-4b6e-9c2a-01a78b7b3f3f",
"clientOrgId": "acme",
"channel": "voice",
"data": {
"fromNumber": "+15555550123",
"toNumber": "+15555550199",
"startedAt": "2026-04-10T14:00:02.010Z"
}
}
call.ended includes the final transcript and call timing:
{
"event": "call.ended",
"timestamp": "2026-04-10T14:03:41.456Z",
"conversationId": "b7ee8d1b-5e3e-4a1a-8f0a-9e2a4f8f1aa2",
"agentId": "2c4a1e72-7a5d-4b6e-9c2a-01a78b7b3f3f",
"clientOrgId": "acme",
"channel": "voice",
"data": {
"fromNumber": "+15555550123",
"toNumber": "+15555550199",
"startedAt": "2026-04-10T14:00:02.010Z",
"endedAt": "2026-04-10T14:03:41.000Z",
"durationMs": 218990,
"endReason": "user_hangup",
"recordingUrl": "https://recordings.example/abc.mp3",
"transcript": [
{ "role": "assistant", "content": "Hi, this is PAM Motors calling about your recall." },
{ "role": "user", "content": "Thanks, I can bring the car in tomorrow." }
]
}
}
call.analyzed arrives after post-call analysis is complete:
{
"event": "call.analyzed",
"timestamp": "2026-04-10T14:04:05.000Z",
"conversationId": "b7ee8d1b-5e3e-4a1a-8f0a-9e2a4f8f1aa2",
"agentId": "2c4a1e72-7a5d-4b6e-9c2a-01a78b7b3f3f",
"clientOrgId": "acme",
"channel": "voice",
"data": {
"analysis": {
"call_summary": "Customer agreed to schedule recall service.",
"call_successful": true
},
"recordingUrl": "https://recordings.example/abc.mp3"
}
}
SMS Event Examples
sms.message_sent fires for the opening SMS and each later assistant SMS:
{
"event": "sms.message_sent",
"timestamp": "2026-04-10T14:00:00.000Z",
"conversationId": "b7ee8d1b-5e3e-4a1a-8f0a-9e2a4f8f1aa2",
"agentId": "2c4a1e72-7a5d-4b6e-9c2a-01a78b7b3f3f",
"clientOrgId": "acme",
"channel": "sms",
"data": {
"messageId": "4b5c6d7e-8f9a-0b1c-2d3e-4f5a6b7c8d9e",
"sequenceNumber": 1,
"from": "+15555550123",
"to": "+15555550199",
"content": "Hi Ada, this is PAM Motors following up on your service recall.",
"direction": "outbound",
"sentAt": "2026-04-10T14:00:00.000Z"
}
}
sms.message_delivery_updated is a canonical delivery projection. It is not a raw carrier event:
{
"event": "sms.message_delivery_updated",
"timestamp": "2026-04-10T14:00:05.000Z",
"conversationId": "b7ee8d1b-5e3e-4a1a-8f0a-9e2a4f8f1aa2",
"agentId": "2c4a1e72-7a5d-4b6e-9c2a-01a78b7b3f3f",
"clientOrgId": "acme",
"channel": "sms",
"data": {
"messageId": "4b5c6d7e-8f9a-0b1c-2d3e-4f5a6b7c8d9e",
"sequenceNumber": 1,
"from": "+15555550123",
"to": "+15555550199",
"direction": "outbound",
"deliveryStatus": "delivered",
"sentAt": "2026-04-10T14:00:00.000Z",
"deliveredAt": "2026-04-10T14:00:05.000Z",
"failedAt": null,
"segments": 1,
"encoding": "GSM_7",
"updatedAt": "2026-04-10T14:00:05.000Z"
}
}
Tool-call events expose the tool name, tool version, arguments, and result without exposing provider transport details.
Signature Verification
Every delivery includes headers for the event type and payload signature:
X-Pam-Signature: sha256=abc123def456...
X-Pam-Event: sms.message_received
The signature is an HMAC-SHA256 of the raw request body using the webhook endpoint’s secret as the key.
const crypto = require("crypto");
function verifySignature(secret, body, signatureHeader) {
const expected = crypto
.createHmac("sha256", secret)
.update(body)
.digest("hex");
const received = signatureHeader.replace("sha256=", "");
return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(received));
}
Delivery Notes
- Webhook URLs must use HTTPS.
- The webhook secret is returned only when the endpoint is created.
- Delivery is at least once, so deduplicate events by delivery id and event body.
- Failed deliveries are retried with exponential backoff.
- Delivery times out after 10 seconds.
- Return a
2xx response quickly after verifying and enqueueing the event.