Agent skill

CallRail Webhooks Skill

Receive and verify CallRail webhooks. Use when setting up CallRail call tracking webhook handlers, verifying the Signature header (base64 HMAC-SHA1 with the company signing key), debugging signature verification failures, or handling pre-call, post-call, call modified, text message and form submission webhooks.

Install this skill

npx skills add hookdeck/webhook-skills --skill callrail-webhooks


When to Use This Skill

  • How do I receive CallRail webhooks?
  • How do I verify the CallRail Signature header?
  • Why does my CallRail webhook signature verification fail?
  • How do I tell which CallRail webhook (post-call, call modified, text message, form submission) I received?
  • How do I handle CallRail pre-call and post-call webhooks without missing recordings?

Verification (core)

CallRail sends a Signature header: the base64 encoding of an HMAC-SHA1 of the raw request body, keyed with the company's signing key (shown on the Webhooks configuration page, or as signing_key from the Integrations API). Use the key as a plain string; do not hex-decode it. There is no timestamp in the signed content.

const crypto = require('crypto');

function verifyCallRailSignature(rawBody, signature, signingKey) {
  if (!signature || !signingKey) return false;
  const expected = crypto.createHmac('sha1', signingKey).update(rawBody).digest('base64');
  try {
    return crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected));
  } catch {
    return false; // length mismatch
  }
}

Docs test vector: key 072e77e426f92738a72fe23c4d1953b4 over the JSON body in the CallRail docs (compact, no trailing newline) gives UZAHbUdfm3GqL7qzilGozGzWV64=.

For complete handlers with tests, see examples/express/, examples/nextjs/, examples/fastapi/.

Webhook Types

CallRail sends no event type in the body or in a header. The type is the webhook URL you registered, one per key of the integration config object. Add ?callrail_event=<name> to each URL (the examples read it), or infer the type from the payload shape.

TypeConfig key / callrail_eventPayload
Pre-Callpre_call_webhook / pre_callCall object, sent before the call connects
Call Routing Completeanswered_call_webhook / answered_callCall object, after routing
Post-Callpost_call_webhook / post_callFull call object, up to 20 min after hangup
Call Modifiedupdated_call_webhook / updated_callCall object plus a changes array
Outbound Post-Callpost_outbound_call_webhook / post_outbound_callCall object with call_type: "outbound"
Outbound Call Modifiedupdated_outbound_call_webhook / updated_outbound_callCall object plus changes
Text Message Receivedsms_received_webhook / sms_receivedsource_number, destination_number, content
Text Message Sentsms_sent_webhook / sms_sentSame as received, plus agent
Form Submissionform_captured_webhook / form_capturedform_data, form_url, submitted_at

Convert Assist Complete webhooks (answers, last_interaction_resource_id) are also documented; the examples report them as convert_assist_complete.

Important Headers and Behavior

ItemValue
Signaturebase64 HMAC-SHA1 of the raw body
Method / bodyPOST with a JSON body (a few legacy fields are also in the query string; ignore them)
SuccessAny 2xx. CallRail does not resend failed webhooks, and repeated failures can disable the integration
Replay protectionCompare the timestamp field in the body (submitted_at for forms) with the current time. Optional in the examples because a call's timestamp is its start time and post-call webhooks can arrive 20 minutes after hangup

Environment Variables

CALLRAIL_SIGNING_KEY=your_company_signing_key        # Webhooks configuration page
CALLRAIL_TIMESTAMP_TOLERANCE_SECONDS=0               # optional, 0 disables the replay check

Local Development

# Start tunnel (no account needed)
npx hookdeck-cli listen 3000 callrail --path /webhooks/callrail

Reference Materials


Repository

hookdeck/webhook-skills

v0.1.0 · MIT · Updated Nov 2, 2026

View on GitHub →