call:recording-available

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:

  1. Open Admin center > API keys (https://app.mixmax.com/dashboard/admin/api-tokens)
    and find the Webhooks section.
  2. Enter your endpoint URL and select Call recording available under Events.
  3. 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.
  4. 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 POST with Content-Type: application/json. Mixmax waits up to 30
    seconds for a response. Reply with any 2xx status 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 same eventId: record the ids you have
    processed and skip repeats. Do not use X-Mixmax-Webhook-Id for this, because it changes
    on every attempt.
  • No ordering. Events arrive in no particular order. This event often arrives before the
    call:completed webhook for the same call.
  • Retries. If your endpoint times out, cannot be reached, or replies with a status other
    than 2xx or 404, 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 404 pauses at once. Mixmax reads a 404 reply 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:

  1. Read the raw request body exactly as received, before parsing the JSON.
  2. Join X-Mixmax-Webhook-Id, X-Mixmax-Timestamp and the raw body, with a . between
    each.
  3. Remove the whsec_ prefix from the secret and base64-decode the rest. The result is the
    HMAC key.
  4. Compute the HMAC-SHA256 of the joined string with that key, and base64-encode it.
  5. X-Mixmax-Signature is v1, followed by that value. Compare the two with a
    constant-time comparison.
  6. 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-000000000000
  • X-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.

Payload

The JSON body of a call:recording-available webhook delivery. It identifies the call and nothing more: no audio, phone numbers, names, email addresses, transcript text or telephony provider links. Mixmax may add fields later, so ignore fields you do not recognize.

string
enum
required

The event type. Always call:recording-available.

Allowed:
string
required

Identifies the event. Every copy of the same event carries the same eventId, including retries and duplicates, so use it to skip events you have already processed. Treat it as an opaque string.

string
required

The call's _id, the same value GET /v1/calls returns. Pass it to GET /v1/calls/{id}/recording.

string
required

The Mixmax user who placed the call.

string

The Mixmax workspace the call belongs to. Absent on some calls, so do not depend on it.

date-time
required

When the recording started, in ISO 8601 (UTC).

integer
required
≥ 0

Length of the recording, in whole seconds.

date-time
required

When Mixmax learned that the recording was complete, in ISO 8601 (UTC).

uri
required

Where to download the audio: always https://api.mixmax.com/v1/calls/{callId}/recording, a Mixmax API endpoint, never a direct media link. Request it with your API token in the X-API-Token header; it does not work without one. An API key made on Admin center > API keys needs the Read calls (calls:read) permission. It returns the whole MP3 file; byte-range requests are not supported, so download it once.

boolean

true only on the sample event that Send test event posts. Real deliveries do not include this field.

Headers
string
required

v1, followed by the base64-encoded HMAC-SHA256 of <X-Mixmax-Webhook-Id>.<X-Mixmax-Timestamp>.<raw request body>, keyed with your signing secret. Verify it before you trust the body.

string
required

When Mixmax signed this delivery attempt, in Unix seconds. It is part of the signed content. Reject a delivery whose timestamp is more than 5 minutes away from your clock.

uuid
required

A new UUID for every delivery attempt, including each retry. It is part of the signed content. Do not use it to detect duplicates; each webhook's description says what to use.

Responses
200

Any 2xx status, sent within 30 seconds, acknowledges the delivery. The response body is ignored.

404

Read as "this URL no longer exists". Mixmax pauses the webhook at once, without retrying, and emails your workspace admins.

default

Any 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.

LoadingLoading…
Psst... like working with APIs? Come join us!