Vaaad AI

CRM Webhooks

Push call and booking events from Vaaad AI to any CRM, automation tool, or custom endpoint.

How it works

Vaaad fires HTTP POST requests to your configured URL whenever the events you opt into occur. Configure webhooks in Settings, Integrations.

Each webhook supports filters, custom payload templates, automatic retries on failure, and signature verification.

Common use cases: pushing call records into HubSpot, Salesforce, Zoho, Pipedrive, or piping events into Zapier and Make automations.

Available events

EventFires when
call.completedAny call ends, with transcript, summary, sentiment, outcome, goal, recording
call.booking_createdAn agent books a new appointment during a call

More booking lifecycle events (confirmed, completed, cancelled) are on the roadmap.

Filters

Reduce webhook noise by only delivering events that match your criteria. Available for call.completed:

  • Minimum call duration. Skip short calls (for example, only deliver calls 30 seconds or longer).
  • Sentiment. Only positive, neutral, or negative calls. Pick any combination.

Filters apply to call events only. Booking events always fire when subscribed.

Custom payload (Liquid templates)

By default, Vaaad sends the standard JSON payload shown further down. To match your CRM's exact field names, write a Liquid template:

{
  "name": "{{contact.name}}",
  "phone": "{{contact.phone}}",
  "duration_min": {{call.duration_minutes}},
  "summary": {{call.summary | json}},
  "sentiment": "{{call.sentiment}}",
  "outcome": "{{call.outcome}}",
  "recording": "{{call.recording_url}}"
}

All payload fields are accessible as variables ({{contact.name}}, {{call.duration_minutes}}, and so on). Use the | json filter when injecting strings into JSON, to handle quotes and special characters safely.

Retries

Failed deliveries are retried automatically with this schedule:

  • 1st retry: 1 minute after the first failure
  • 2nd retry: 5 minutes after the first retry
  • 3rd retry: 30 minutes after the second retry

After 4 total failed attempts (the original plus 3 retries), the delivery is marked as permanently failed. You can manually replay any failed delivery from the webhook detail panel in Settings.

HTTP responses 2xx are considered successful. Anything else, including timeouts, triggers a retry.

Request headers

Every webhook delivery includes these headers:

  • X-Vaaad-Event: event name, for example call.completed
  • X-Vaaad-Timestamp: Unix timestamp (seconds) when the delivery was sent
  • X-Vaaad-Signature: sha256= followed by an HMAC-SHA256 of {timestamp}.{body}
  • User-Agent: VaaadAI-Webhook/1.0

The signature header is only included if you set a webhook secret in Settings. We strongly recommend setting one and verifying it on your side.

Sample call payload

{
  "event": "call.completed",
  "call_id": "cmoxyz...",
  "timestamp": "2026-04-27T10:15:23.412Z",
  "contact": {
    "name": "Rohit Kumar",
    "phone": "+919876543210",
    "email": null
  },
  "call": {
    "direction": "outbound",
    "status": "completed",
    "duration_seconds": 184,
    "duration_minutes": 4,
    "cost_inr": 16.00,
    "outcome": "Lead",
    "sentiment": "positive",
    "summary": "Customer is interested in the demo. Requested follow-up by Tuesday.",
    "goal_achieved": "yes",
    "topics": ["pricing", "demo", "timeline"],
    "recording_url": "https://r2.../recording.wav",
    "agent_name": "Vaaad AI Receptionist",
    "agent_id": "cmnabc...",
    "campaign_name": "Q2 Outreach",
    "campaign_id": "cmnxyz...",
    "started_at": "2026-04-27T10:12:01.000Z",
    "ended_at": "2026-04-27T10:15:05.000Z"
  },
  "booking": null
}

Fields like sentiment, summary, outcome, goal_achieved, and topics come from automatic post-call analysis. They may be null if the call had no transcribed user speech (for example a no-answer).

Sample booking payload

{
  "event": "call.booking_created",
  "booking_id": "cmnabc...",
  "timestamp": "2026-04-27T10:15:23.412Z",
  "contact": {
    "name": "Rohit Kumar",
    "phone": "+919876543210"
  },
  "booking": {
    "type": "demo",
    "status": "confirmed",
    "datetime": "2026-04-29T15:30:00.000+05:30",
    "notes": "Demo booking via call",
    "call_id": "cmoxyz...",
    "created_at": "2026-04-27T10:15:23.412Z"
  }
}

Verifying signatures (Node.js)

import crypto from "crypto"
import express from "express"

const app = express()
app.post("/your-webhook-endpoint", express.raw({ type: "application/json" }), (req, res) => {
  const timestamp = req.headers["x-vaaad-timestamp"]
  const signature = (req.headers["x-vaaad-signature"] ?? "").replace("sha256=", "")
  const secret = process.env.VAAAD_WEBHOOK_SECRET

  const expected = crypto
    .createHmac("sha256", secret)
    .update(`${timestamp}.${req.body.toString()}`)
    .digest("hex")

  if (signature !== expected) {
    return res.status(401).send("Invalid signature")
  }

  // Optional: reject replays older than 5 min
  const ageSec = Math.floor(Date.now()/1000) - Number(timestamp)
  if (ageSec > 300) return res.status(401).send("Stale request")

  const payload = JSON.parse(req.body.toString())
  console.log("Received:", payload.event, payload)
  res.status(200).send("ok")
})

Security best practices

  • Always verify X-Vaaad-Signature before processing the payload
  • Reject requests with timestamps older than 5 minutes to prevent replay attacks
  • Keep your webhook secret in environment variables, never in code
  • Use HTTPS endpoints only
  • Idempotency: handle duplicate deliveries gracefully (use call_id or booking_id as a dedupe key)
  • Logs older than 30 days are auto-deleted from your dashboard