Agent skill
Mistral Webhooks Skill
Receive and verify Mistral AI Studio voice agent webhooks. Use when setting up Mistral webhook handlers, debugging x-mistral-signature verification, or handling call lifecycle events like call_started, call_ended, and call_failed.
Install this skill
npx skills add hookdeck/webhook-skills --skill mistral-webhooks
When to Use This Skill
- How do I receive Mistral voice agent webhooks?
- How do I verify the
x-mistral-signatureheader? - How do I handle
call_started,call_ended, andcall_failedevents? - Why is my Mistral webhook signature verification failing?
- How do I store call transcripts from Mistral AI Studio voice agents?
Verification (core)
Mistral signs the raw request body with HMAC-SHA256, keyed with the endpoint signing secret (used as a UTF-8 string). The hex digest is sent in x-mistral-signature as hmac-sha256=<hex>. Strip the hmac-sha256= prefix, then compare timing-safe. There is no timestamp in the signed content.
Node:
const crypto = require('crypto');
function verify(rawBody, header, secret) {
if (!header || !header.startsWith('hmac-sha256=')) return false;
const received = header.slice('hmac-sha256='.length);
const expected = crypto.createHmac('sha256', secret).update(rawBody).digest('hex');
try {
return crypto.timingSafeEqual(Buffer.from(received), Buffer.from(expected));
} catch {
return false; // different lengths
}
}
Python:
import hmac, hashlib
def verify(raw_body: bytes, header: str, secret: str) -> bool:
if not header or not header.startswith("hmac-sha256="):
return False
received = header.removeprefix("hmac-sha256=")
expected = hmac.new(secret.encode(), raw_body, hashlib.sha256).hexdigest()
return hmac.compare_digest(received.encode(), expected.encode())
Mistral does not ship a webhook verification helper in its SDKs, so verification is manual in every framework.
For complete handlers with route wiring, event dispatch, and tests, see:
Common Event Types
Every delivery is a JSON POST with the envelope { "event": "<type>", "created_. The event type is in the body, not a header.
| Event | Sent When |
|---|---|
call_started | A session is created and the voice pipeline is ready |
call_ended | A session ends normally (includes duration_seconds and a best-effort transcript) |
call_failed | A session fails to start or hits a fatal error (error field, e.g. livekit_error) |
For payload fields, see references/overview.md or Mistral webhooks docs.
Important Headers
| Header | Description |
|---|---|
x-mistral-signature | hmac-sha256=<hex digest> of the raw body |
content-type | application/json |
Delivery and Retries
Respond within 10 seconds. Any 2xx is success. Mistral retries 5xx, 429 and transport errors (3 attempts in total, backoff 2s then 4s) and does not retry other 4xx. Deduplicate with session_id + event.
Environment Variables
MISTRAL_WEBHOOK_SECRET=your_endpoint_signing_secret # Generated when you create the endpoint; cannot be rotated
Local Development
# Start tunnel (no account needed)
npx hookdeck-cli listen 3000 mistral --path /webhooks/mistral
Reference Materials
- references/overview.md - Mistral webhook concepts and payloads
- references/setup.md - Endpoint and subscription setup
- references/verification.md - Signature verification details