Webhook payload reference
This article documents the exact format of the data Rafiki AI sends to your webhook URL. Use it when you're mapping fields in Zapier, building a receiver on your own server, or troubleshooting.
Event types
Event | When it fires |
| A call has been fully processed (transcript + summary + analytics) |
| You clicked the Test Webhook button in Settings |
Payload shape
Every webhook has this structure:
{ "event": "meeting.completed", "timestamp": "2026-04-17T14:30:00Z", "meeting": { "id": "661f1a2b3c4d5e6f7a8b9c0d", "title": "Discovery Call - Acme Corp", "date": "2026-04-17T14:00:00Z", "duration": 1847, "provider": "Zoom", "status": "Completed", "host": "[email protected]", "meetingUrl": "https://app.getrafiki.ai/#/meetings/661f1a...", "attendees": [ {"name": "Sarah Rep", "email": "[email protected]", "role": "organizer"}, {"name": "John Smith", "email": "[email protected]", "role": "attendees"} ] }, "summary": "Discovery call with Acme Corp. Key pain points: manual note-taking eating up SDR time...", "transcript": [ {"speaker": "Sarah Rep", "start": 0.0, "end": 12.5, "text": "Thanks for joining today."}, {"speaker": "John Smith", "start": 12.5, "end": 28.3, "text": "Happy to be here."} ]}
Top-level fields
Field | Type | Description |
| string | Event identifier, e.g., |
| ISO 8601 | When the webhook was sent |
| object | Meeting metadata (always present) |
| string | AI-generated summary — only included if "Meeting summary" is enabled in Settings |
| array | Speaker-segmented transcript — only included if "Full transcript" is enabled |
Meeting object fields
Field | Type | Description |
| string | Rafiki AI meeting ID (MongoDB ObjectID) |
| string | Meeting title/subject |
| ISO 8601 | When the call occurred |
| integer | Call length in seconds |
| string | Source platform: |
| string | Always |
| string | Email of the host / organizer / rep |
| string | Link to view the call in Rafiki AI |
| array | List of participants (if "Attendee details" is enabled) |
Transcript array items
Each item in the transcript array represents one contiguous block of speech:
Field | Type | Description |
| string | Name of the speaker (or email if name unknown) |
| float | Start time in seconds from beginning of call |
| float | End time in seconds |
| string | Transcribed speech for this segment |
Request headers
Header | Value | Purpose |
|
| Always JSON |
|
| Event type (redundant with payload field, useful for routing) |
| UUID v4 | Unique ID per delivery attempt — use for idempotency |
|
| HMAC-SHA256 of the request body using your signing secret (only present if a secret is set) |
Verifying the signature
If you've set a signing secret in Settings, Rafiki AI signs every webhook request. Your server can verify that a request genuinely came from Rafiki AI.
Node.js example
const crypto = require('crypto');function verifyRafikiSignature(rawBody, signatureHeader, secret) { const expected = 'sha256=' + crypto .createHmac('sha256', secret) .update(rawBody) .digest('hex'); return crypto.timingSafeEqual( Buffer.from(signatureHeader), Buffer.from(expected) );}// In your Express handler:app.post('/rafiki-webhook', (req, res) => { const signature = req.headers['x-rafiki-signature']; const rawBody = req.rawBody; // ensure body parser preserves raw bytes if (!verifyRafikiSignature(rawBody, signature, process.env.RAFIKI_SECRET)) { return res.status(401).send('Invalid signature'); } // Process the webhook res.status(200).send('OK');});
Python example
import hmacimport hashlibdef verify_rafiki_signature(raw_body_bytes, signature_header, secret): expected = 'sha256=' + hmac.new( secret.encode(), raw_body_bytes, hashlib.sha256 ).hexdigest() return hmac.compare_digest(signature_header, expected)
Important: Verify against the raw request bytes, not a re-serialized JSON string. Key ordering and whitespace matter for HMAC.
Delivery guarantees
Rafiki AI retries up to 3 times on network failures or non-2xx HTTP responses
Retry backoff: 1s, 5s, 15s
Timeout per attempt: 10 seconds
After 3 failures, delivery is abandoned — your meeting is still saved in Rafiki AI
Your endpoint should respond with
200 OKwithin 5 seconds; queue heavy work asynchronously
