Webhooks
Receive real-time HTTP notifications when events happen in your JagCall account. No polling required.
What Are Webhooks?
Webhooks are HTTP POST requests sent from JagCall to your server whenever a specific event occurs, such as a call completing or an SMS being received. Instead of polling the API, your application receives updates in real time.
Each webhook request includes a JSON payload describing the event, a timestamp, and an HMAC-SHA256 signature for verification.
Configuration
You can configure webhook URLs at two levels:
- Per agent: In the agent settings, add a webhook URL that receives events only for that agent.
- Organization-wide: In Settings → Webhooks, add a URL that receives all events across your organization.
When both are configured, both URLs receive the event. Your endpoint must return a 2xx status code within 10 seconds to acknowledge receipt.
Event Types
call.startedFired when an inbound or outbound call connects.
Payload
{
"event": "call.started",
"timestamp": "2025-11-01T14:30:00Z",
"data": {
"call_id": "call_xxx",
"agent_id": "agt_xxx",
"direction": "inbound",
"from": "+14155559876",
"to": "+14155551234"
}
}call.completedFired when a call ends normally.
Payload
{
"event": "call.completed",
"timestamp": "2025-11-01T14:32:22Z",
"data": {
"call_id": "call_xxx",
"agent_id": "agt_xxx",
"duration_seconds": 142,
"status": "completed",
"recording_available": true
}
}call.failedFired when a call fails to connect or encounters an error.
Payload
{
"event": "call.failed",
"timestamp": "2025-11-01T14:30:05Z",
"data": {
"call_id": "call_xxx",
"agent_id": "agt_xxx",
"error_code": "no_answer",
"error_message": "The call was not answered after 30 seconds."
}
}call.transcript.readyFired when the full transcript is available after a call.
Payload
{
"event": "call.transcript.ready",
"timestamp": "2025-11-01T14:33:00Z",
"data": {
"call_id": "call_xxx",
"agent_id": "agt_xxx",
"transcript_url": "https://jagcall.com/v1/calls/call_xxx/transcript"
}
}sms.sentFired when an outbound SMS is sent.
Payload
{
"event": "sms.sent",
"timestamp": "2025-11-01T15:00:00Z",
"data": {
"sms_id": "sms_xxx",
"from": "+14155551234",
"to": "+14155559876",
"body": "Your appointment is confirmed.",
"status": "sent"
}
}sms.receivedFired when an inbound SMS is received on one of your numbers.
Payload
{
"event": "sms.received",
"timestamp": "2025-11-01T15:05:00Z",
"data": {
"sms_id": "sms_yyy",
"from": "+14155559876",
"to": "+14155551234",
"body": "Thanks, see you then!"
}
}Signature Verification
Every webhook request includes two headers for verification:
X-JagCall-Signature— HMAC-SHA256 hex digest of{timestamp}.{body}X-JagCall-Timestamp— Unix timestamp of when the webhook was sent
Your webhook signing secret is available in Settings → Webhooks. Always verify signatures to prevent spoofing.
import hmac
import hashlib
def verify_webhook(payload_body: bytes, signature: str, timestamp: str, secret: str) -> bool:
"""Verify the HMAC-SHA256 signature of a JagCall webhook."""
signed_content = f"{timestamp}.{payload_body.decode('utf-8')}"
expected = hmac.new(
secret.encode("utf-8"),
signed_content.encode("utf-8"),
hashlib.sha256,
).hexdigest()
return hmac.compare_digest(expected, signature)import crypto from "crypto";
function verifyWebhook(
payloadBody: string,
signature: string,
timestamp: string,
secret: string
): boolean {
const signedContent = `${timestamp}.${payloadBody}`;
const expected = crypto
.createHmac("sha256", secret)
.update(signedContent)
.digest("hex");
return crypto.timingSafeEqual(
Buffer.from(expected),
Buffer.from(signature)
);
}Reject webhooks with a timestamp older than 5 minutes to prevent replay attacks. Compare the X-JagCall-Timestamp header against the current time.
Retry Policy
If your endpoint does not return a 2xx status code within 10 seconds, JagCall retries with exponential backoff:
| Attempt | Delay |
|---|---|
| 1st retry | 30 seconds |
| 2nd retry | 2 minutes |
| 3rd retry | 15 minutes |
| 4th retry | 1 hour |
| 5th retry (final) | 6 hours |
After 5 failed retries, the event is marked as failed. You can view failed events and manually retry them from Settings → Webhooks → Event Log.
Testing Webhooks
During development, you can test webhooks without deploying:
- Use a tunnel service like ngrok to expose your local server:
ngrok http 3000 - Copy the HTTPS URL and paste it as your webhook URL in Settings.
- Use the Send Test Event button in the webhook settings to trigger a sample event.
- Inspect the request in your server logs or ngrok dashboard.