Skip to main content

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

EventDescription
call.startedVoice call connected or started ringing.
call.endedVoice call disconnected before analysis is complete.
call.analyzedPost-call analysis is available.
sms.message_sentOutbound SMS sent to a contact and persisted.
sms.message_receivedInbound SMS received from a contact and persisted.
sms.message_delivery_updatedCanonical delivery status changed for an outbound SMS.
sms.tool_call_invocationSMS agent invoked a tool during the conversation loop.
sms.tool_call_resultTool result was persisted for an SMS conversation.
contact.opt_outContact opted out of messages.
contact.opt_inContact 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.