← Blog · 26 October 2026 · Lire en français

Audio-ready webhooks: stop polling and let the API tell you

When your CMS sends an article to a text-to-speech API, the audio is built in the background. Here is how our webhook tells you it is ready: the payload, the three statuses, how to verify the HMAC signature, what happens when your server is down, and what the webhook deliberately does not do.

Every developer who wires a CMS to a text-to-speech API hits the same question within the first hour: the article is published, the audio is not there yet, so what does the page show, and how does the CMS find out when the file exists? Most first integrations answer with a loop that asks the server every few seconds. It works on a demo and becomes noise in production.

Webhooks are the cleaner answer. This post describes ours exactly as the code implements it, including the edges where it stops.

Three ways to wait for audio

There are three patterns, and our API supports all of them.

  • Block on the response. Send the generation call with wait set to true, and the response only comes back once synthesis is finished, with the audio URL inside. The article goes live already narrated. The catch: this only applies up to 20,000 characters. Above that, wait is ignored and the call returns immediately.
  • Poll the status. Without wait, the API answers 202 with the article and job identifiers, and you query the article until it is ready. Simple, but you will pick an interval that is either too eager or too lazy.
  • Get called back. Pass a webhook_url in the generation call. When the audio is done, we POST the result to that address.

If you have not made a first call yet, start with the minimal request and its response. The webhook is one extra field on top of it.

What the payload looks like

The callback is a JSON POST. The body carries article_id (ours), external_id (yours, if you sent one, so you can match the article without a lookup table), status, audio_url, duration_s, version and voice.

The interesting field is status, because it has three values rather than one:

  • ready: the audio is live and audio_url is filled in.
  • pending_review: your account requires a human to approve audio before it is served. The file exists, but audio_url is empty and a review_url points to the dashboard. This is the automated side of reviewing audio before it goes out.
  • scheduled: the article has a future publication date. The audio is built but must not be served yet, so the body carries publish_at and, again, no file URL.

The rule behind the last two is worth stating plainly: audio that is not meant to be heard yet does not travel, not even to your own server.

When a corrected article is re-narrated only on the sentences that changed, the body also includes partial: true along with the new version number.

Verifying the signature

A webhook endpoint is an open door into your CMS. Anyone who finds the URL can post a fake "audio ready". So every call is signed, with three headers:

  • X-WeDispatch-Timestamp: when the call was sent, in seconds.
  • X-WeDispatch-Signature: sha256= followed by an HMAC-SHA256 of the string "timestamp, a dot, the raw body", keyed with your signing secret.
  • X-WeDispatch-Delivery: a unique identifier for this delivery.

On your side: compute the HMAC over the raw body exactly as received (before any JSON parsing reformats it), compare in constant time, and reject timestamps that are too old so a captured request cannot be replayed hours later. It is the same scheme the large payment platforms use, which means your framework very likely ships a helper for it already.

The signing secret is specific to your account. It is shown in the dashboard, on the sites page, and only to the account owner and administrators. If someone else on the team is building the integration, they need to ask one of them.

When your server is down

Your endpoint might be mid-deploy at the exact moment the audio finishes. Here is what happens:

  • On a network error, a timeout, a 5xx or a 429, we retry, up to three attempts in total, with a short and growing pause between them.
  • A 2xx ends it, as you would expect.
  • Any other 4xx also ends it. A 404 will not turn into a 200 by insisting.
  • Each attempt waits ten seconds at most, and redirects are not followed.

All attempts of the same delivery carry the same X-WeDispatch-Delivery value. If your server processed a call but the response got lost, the retry arrives with an identifier you have already seen. Store the identifiers you accept and ignore duplicates; that is the whole of idempotency here.

After three failed attempts, the webhook is not replayed later. The audio is still there, though, and the article status remains available through the API. A nightly job that checks recent articles is the right safety net for anything your endpoint missed.

What the webhook does not do

Better to know this before you write the integration:

  • There is no account-level webhook. The URL is passed on every generation call and never stored. If you are looking for the settings screen, there isn't one, and our public documentation says so in the same place.
  • The URL must be public HTTPS. Localhost, private IPs and hostnames that resolve to internal addresses are rejected with a 400 at call time, and checked again at send time.
  • It does not report production failures. If an audio cannot be built, the account is notified by email, not through the webhook.
  • Quota alerts (warning threshold, quota reached) go to the same URL when one is provided, but with a single attempt: the next alert will follow anyway.

Do you need one?

On WordPress, you don't: the plugin handles the waiting for you, and the WordPress page explains what it does. Webhooks are for teams wiring a custom, headless or automation-driven CMS themselves, as in our API integration guide. For them, it is the difference between an integration that keeps asking and one that sits still until there is something to do.

Give your articles a voice with WeDispatch

This blog is itself voiced by WeDispatch. Curious how it sounds on your content?

Book a demo

Read next