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-signature header?
  • How do I handle call_started, call_ended, and call_failed events?
  • 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_at": "<ISO 8601>", "data": { ... } }. The event type is in the body, not a header.

EventSent When
call_startedA session is created and the voice pipeline is ready
call_endedA session ends normally (includes duration_seconds and a best-effort transcript)
call_failedA 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

HeaderDescription
x-mistral-signaturehmac-sha256=<hex digest> of the raw body
content-typeapplication/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


Repository

hookdeck/webhook-skills

v0.1.0 · MIT · Updated Nov 3, 2026

View on GitHub →