Limited beta. This webhook is available to workspaces in the Calls API limited beta,
because the recording it announces is downloaded with GET /v1/calls/{id}/recording, which
is part of that beta. To request access, email
[email protected].
Mixmax sends this POST to your endpoint when the recording of a call placed with the
Mixmax Dialer is ready to download. The body identifies the call and nothing more: no audio,
phone numbers, names, email addresses or transcript text. Download the audio from
recordingUrl with your API token.
Register your endpoint
Workspace admins register the endpoint in the Mixmax app:
- Open Admin center > API keys (
https://app.mixmax.com/dashboard/admin/api-tokens)
and find the Webhooks section. - Enter your endpoint URL and select Call recording available under Events.
- Copy the signing secret and store it with your receiver. You need it to verify
deliveries. Mixmax shows the secret only once; afterwards the page shows its last four
characters. If you lose it, regenerate it. - Click Save, then Send test event to check that your endpoint receives and
verifies a delivery.
Each workspace has one webhook URL. It receives this event for the recorded calls of every
member of the workspace. Mixmax sends nothing until a URL is saved, and it does not send
events for recordings that were ready before then. To stop deliveries, remove the URL: this
deletes the webhook, and saving a URL again sets it up afresh. The
webhook is managed only on this page; it does not appear among your rules.
When it is sent
Once per recorded call, usually within a few minutes of the call ending, when all of these
are true:
- The call was placed with the Mixmax Dialer by a member of your workspace.
- The call was recorded: recording was on and allowed for the call, and the call was
answered. - The recording completed successfully.
It is not sent for calls that were not recorded, for recordings that failed, or for
recordings that were ready before your URL was saved. Mixmax sends no event when a recording
is later deleted or expires.
Download the recording
recordingUrl is always https://api.mixmax.com/v1/calls/{callId}/recording. Request it
with an API token in the X-API-Token header. An API key made on Admin center > API
keys needs the Read calls (calls:read) permission; without it the request is
refused with a 403. A member's token downloads only that member's own calls. To download a
call placed by someone else in the workspace, use a token that acts for a workspace admin
and add ?scope=workspace. The endpoint allows 20 requests per minute per user.
Recordings are not kept forever, so download and store the audio soon after you receive the
event. A 404 means the recording is no longer available, or that your workspace is not in
the Calls API beta.
Delivery
- Request. A JSON
POSTwithContent-Type: application/json. Mixmax waits up to 30
seconds for a response. Reply with any2xxstatus as soon as you have stored the event,
and do the work afterwards. The response body is ignored. - At least once. The same event can arrive more than once, for example when a delivery
is retried after a timeout. Every copy carries the sameeventId: record the ids you have
processed and skip repeats. Do not useX-Mixmax-Webhook-Idfor this, because it changes
on every attempt. - No ordering. Events arrive in no particular order. This event often arrives before the
call:completedwebhook for the same call. - Retries. If your endpoint times out, cannot be reached, or replies with a status other
than2xxor404, Mixmax retries about every 5 minutes, a limited number of times. If
the last attempt fails, Mixmax pauses the webhook and emails your workspace admins. - A
404pauses at once. Mixmax reads a404reply as "this URL no longer exists": it
pauses the webhook without retrying and emails your workspace admins.
After fixing your endpoint, turn the webhook back on in the Webhooks section.
Verify the signature
Every delivery is signed with the webhook's signing secret, which starts with whsec_.
Check the signature before you trust the body:
- Read the raw request body exactly as received, before parsing the JSON.
- Join
X-Mixmax-Webhook-Id,X-Mixmax-Timestampand the raw body, with a.between
each. - Remove the
whsec_prefix from the secret and base64-decode the rest. The result is the
HMAC key. - Compute the HMAC-SHA256 of the joined string with that key, and base64-encode it.
X-Mixmax-Signatureisv1,followed by that value. Compare the two with a
constant-time comparison.- Reject the delivery if
X-Mixmax-Timestamp(Unix seconds) is more than 5 minutes away
from your clock. This blocks replayed deliveries.
In Node.js:
const crypto = require('crypto');
// rawBody: the request body as a Buffer, before JSON parsing.
function verifyMixmaxSignature({ rawBody, headers, secret }) {
const id = headers['x-mixmax-webhook-id'];
const timestamp = headers['x-mixmax-timestamp'];
const signature = headers['x-mixmax-signature'] || '';
if (!id || !timestamp || !signature.startsWith('v1,')) return false;
if (Math.abs(Date.now() / 1000 - Number(timestamp)) > 5 * 60) return false;
const key = Buffer.from(secret.replace(/^whsec_/, ''), 'base64');
const expected = crypto
.createHmac('sha256', key)
.update(`${id}.${timestamp}.`)
.update(rawBody)
.digest();
const received = Buffer.from(signature.slice(3), 'base64');
return received.length === expected.length && crypto.timingSafeEqual(received, expected);
}To test your code, use these placeholder inputs (not a real secret) and skip the timestamp
check. They must produce the signature
v1,97yqJFN2Tx0WlGgkygjuwQaZHhjsIy1E+eP6ejjShHk=.
- Secret:
whsec_AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA= X-Mixmax-Webhook-Id:00000000-0000-4000-8000-000000000000X-Mixmax-Timestamp:1791468381- Raw body, on one line with no trailing newline:
{"eventName":"call:recording-available","eventId":"65f000000000000000000001:recording-available","callId":"65f000000000000000000001","userId":"65f000000000000000000002","workspaceId":"65f000000000000000000003","recordingStartedAt":"2026-10-08T14:03:11.000Z","recordingDurationSeconds":184,"occurredAt":"2026-10-08T14:06:20.412Z","recordingUrl":"https://api.mixmax.com/v1/calls/65f000000000000000000001/recording"}When an admin regenerates the signing secret, the next delivery is signed with the new
secret. Update your receiver right away; until you do, deliveries fail verification.
Test events
Send test event posts one sample event to your endpoint, signed with your secret like a
real delivery. It has "test": true and placeholder ids, holds no real call data, and its
recordingUrl does not lead to a recording. Reply to it with a 2xx and do not process it
further.
200Any 2xx status, sent within 30 seconds, acknowledges the delivery. The response body is ignored.
404Read as "this URL no longer exists". Mixmax pauses the webhook at once, without retrying, and emails your workspace admins.
defaultAny other status, no response within 30 seconds, or a connection error. Mixmax retries about every 5 minutes, a limited number of times, and pauses the webhook if the last attempt fails.
