23 endpoints, 28 operations. For each one: what it reads, what it returns, and the errors you have to handle. Everything is unfolded, because you arrive here looking for a field name.
6 features of the product have no public route. Finding that out in the middle of a build costs half a day; reading it here costs two minutes.
Create a shareable clip
The feature exists, in the dashboard. It has no public route: a clip is made by hand, by listening, and that is precisely what a program cannot do for you.
Search across your whole audio archive
Searching WITHIN ONE ARTICLE works by key, since 28/08/2026. Searching your whole archive does not: that would require a search index, which does not exist. Query your articles one by one, or build your index from the subtitles.
Create or list your sites
A key BELONGS TO a site, it does not manage sites. The attachment happens when the key is created, in the dashboard or through the code exchange.
Register a webhook once and for all
There is no account-level webhook setting. The address is passed ON EVERY generation call, and it is not kept. If you were looking for where to configure it, the answer is: nowhere, and that is deliberate.
Declare an RSS feed to watch
Feed discovery exists as an integration mode, but it is configured in the dashboard, not through the API.
Filter or paginate the article list
`GET /podcasts` reads no parameter and returns the last thirty. No status filter, no cursor, no date. It is the limit most likely to surprise you, so it is written here and on the route itself.
Getting started
What you need to have understood before the first call.
Authentication
Header on every request
One key per site, sent as a header. No route is served without it, except the voice catalogue and the code exchange, both flagged as such.
Authorization: Bearer wd_live_xxxxxxxxxxxxxxxx
Worth knowing: With the WordPress plugin you will never handle this key: it is exchanged server to server. The key in clear text only exists for bespoke integrations, and it is never shown again after it is created.
Language of error messages
Accept-Language header
Error messages are returned in French by default, in English if you ask. The HTTP CODE never changes: branch your logic on the code, not on the wording.
Accept-Language: en
Worth knowing: Two routes do not yet translate their messages, the recommendations among them. Handle the code, not the sentence.
Rate limits
Three routes carry one
Ten generations per minute, four batches per minute, thirty voice previews per hour. The other fourteen routes have no rate limit. Going over returns a 429.
429 { "error": "…maximum 10 générations par minute…" }
Worth knowing: Two of those three counters live in instance memory: they are safeguards, not contractual guarantees. Only the generation limit is counted in the database.
The refusal codes
What you need to handle
Ten codes cover everything the API can refuse. Branch your logic on them, never on the text of the message.
400 requête mal formée, champ absent ou hors bornes
401 clé absente, inconnue ou révoquée
402 la retouche dépasse le budget inclus et n'a pas été confirmée
403 le compte est bloqué, ou la capacité n'est pas ouverte
404 la ressource visée n'existe pas, ou n'est pas la vôtre
409 l'état actuel interdit l'opération demandée
429 une limite de débit ou le volume mensuel est atteint
500 un défaut de notre côté, à signaler
502 un service dont nous dépendons a répondu de travers
503 indisponibilité temporaire, réessayez plus tard
Worth knowing: A 409 IS NOT AN ERROR ON YOUR SIDE: it says the order of operations is wrong, a regeneration already running, audio produced before word-level alignment. Replaying it unchanged later often works. A 400 replayed unchanged, never.
Producing
Send a text and get audio back. Synchronous mode publishes the article already voiced.
POST
Generate the audio
/api/v1/podcasts
The main route. With `wait` set to true, the response waits for synthesis to finish and contains the file address: the article publishes already voiced, with no visible delay.
What the route reads
textrequiredThe full text, from 100 to 100,000 characters.
titleoptionalUsed by the podcast feed and the provenance page.
canonical_urloptionalThe article's address on your side.
external_idoptionalYour own identifier. This is what guarantees deduplication: the same one sent twice produces a single audio.
rubriqueoptionalDetermines the voice, the branding and the per-section statistics. Accepted alias: `section`.
authoroptionalFeeds the per-byline statistics. Accepted alias: `byline`.
voiceoptionalVoice identifier. An unknown voice is refused with a 400, never silently replaced.
triggeroptional`auto` to submit to the account's switch, `manual` to force it.
publish_atoptionalFuture ISO date: prepares the audio for a scheduled publication.
webhook_urloptionalPublic https address, called when the audio is ready. It applies TO THIS CALL and is never kept as a setting.
auto_regenerateoptionalRegenerate automatically when the text changes.
waitoptionalTrue to receive the audio in the response. No effect beyond 20,000 characters.
Idempotency-Key Replaying the same call with the same key does not produce a second audio.
POST https://wedispatch.fr/api/v1/podcasts
Content-Type: application/json
{
"text": "Le conseil municipal a adopte hier soir...",
"title": "Le budget 2027 adopte",
"external_id": "id-dans-votre-cms",
"rubrique": "Politique",
"wait": true
}
What it returns: `article_id`, `job_id`, `status`, `embed`, and with `wait`: `audio_url`, `duration_s`, `version`, `engine`, `voice`, `fallback`.
To handle: 400 invalid text, voice, language or date · 403 account blocked · 429 monthly quota reached, or ten per minute exceeded
Worth knowing: An article over 1,000 characters costs one more credit per started block, and a started block is spent.
POST
Batch generation
/api/v1/podcasts/batch
Up to twenty-five articles in one request, useful for an archive. Every per-item field is accepted, and `language`, `trigger` and `auto_regenerate` also accept one value for the whole batch.
What the route reads
itemsrequiredA non-empty array, twenty-five entries at most. Each entry accepts the same fields as a single generation.
What it returns: `accepted`, `total`, and `results[]` where each entry carries its own `status`: `processing`, `ready`, `stale`, `quota`, `blocked`, `skipped` or `error`.
To handle: 400 items missing, empty or over twenty-five · 429 four batches per minute exceeded
Worth knowing: `wait` does NOT exist on this route, and the idempotency header is not read here. The remaining monthly volume bounds the batch before the rate limits do: twenty-five articles with ten credits left processes ten and reports the rest as `quota`.
POST
Regenerate in full
/api/v1/podcasts/{article_id}/regenerate
For an article changed substantially. Costs a credit like a generation, and resets the edit budget, since this is a new audio.
What the route reads
voiceoptionalChange voice on the way through.
languageoptionalChange language on the way through.
publish_atoptionalSchedule when the new version goes live.
webhook_urloptionalCalled when the new version is ready.
POST https://wedispatch.fr/api/v1/podcasts/{article_id}/regenerate
What it returns: `article_id`, `job_id`, `status`, `regens_included`, `regens_used`.
To handle: 404 unknown article · 409 no stored text to regenerate · 429 regeneration ceiling or monthly quota
Worth knowing: This is the leading cause of accidental spending, usually after a small text correction. Check first whether an edit is enough: it only bills the rewritten passage, where a regeneration pays for the whole article again.
POST
Read again only what changed
/api/v1/podcasts/{article_id}/partial
Compares the last text sent through `POST /podcasts` with the text of the audio and reads again only the changed sentences, stitched into the existing audio. Send the new text first: the page is not re-read. Without `accept`, the route studies and gives the price without spending anything; with `accept: true`, it reads. The price is that of an edit: the characters read again, pro rata.
What the route reads
acceptoptional`true` to read. Absent: the study only, free.
max_creditsoptionalThe accepted price. If the article changed since the study and reading costs more, nothing is spent (409).
POST https://wedispatch.fr/api/v1/podcasts/{article_id}/partial
What it returns: Study: `possible`, `reason` (when not possible: `inchange`, `trop`, `alignement`, `texte-choisi`, `sans-horodatage`, `retouche-non-incluse`…), `message`, `passages_count`, `passages` (`before`, `after`, five at most), `chars`, `credits`, `credits_full_regeneration`. Reading: `ok`, `version`, `duration_s`, `status`, `passages_count`, `credits_used`.
To handle: 402 agreement missing · 403 edits not in your plan · 404 unknown article · 409 reading impossible or price exceeded · 502 synthesis or file failed, nothing charged
Worth knowing: Beyond a third of the text changed, or when the changes cannot be located with certainty in the audio, the route answers `possible: false`: regenerate in full instead.
Tracking
The state of one article, and the list of the latest ones.
GET
Track one article
/api/v1/podcasts/{article_id}
The state of an article and, once it is ready, the address of its audio. Call it after sending without `wait`, or when a webhook arrives.
GET https://wedispatch.fr/api/v1/podcasts/{article_id}
What it returns: `article_id`, `title`, `status` (`processing`, `ready`, `stale` or `failed`), `regen_count`, `summary`, `audio` (`null` or the full object with `url`, `duration_s`, `version`, its `chapters` when the article carries any and `midroll_s` when a paragraph boundary was found), `embed`, `subtitles`.
To handle: 404 unknown article · 503 audio link temporarily unavailable
GET
List your articles
/api/v1/podcasts
The last thirty articles on the account, most recently modified first.
GET https://wedispatch.fr/api/v1/podcasts
What it returns: `articles[]`: `id`, `title`, `canonical_url`, `audio_status`, `char_count`, `regen_count`, `updated_at`, and depending on the state of the database `rubrique` and `author`.
Worth knowing: THIS ROUTE ACCEPTS NO PARAMETER. No status filter, no pagination, no date. Thirty rows, always. If you need to find the articles to regenerate, compare `audio_status` yourself on what you receive.
DELETE
Remove an audio
/api/v1/podcasts/{article_id}
Deletes an article's audio files. The article, its source text and its listening statistics are kept.
What it returns: `ok`, `article_id`, `status`, `removed_versions`, and `kept`, which says explicitly what was not deleted.
To handle: 400 invalid identifier · 404 unknown article · 409 a generation is running
Worth knowing: No credit is refunded, and the response says so rather than keeping quiet about it.
GET
Related articles
/api/v1/recommendations
The articles closest to a given one, to offer a next listen.
What the route reads
article_idrequiredAs a query parameter, in UUID format.
limitoptionalFive by default, between one and twenty.
GET https://wedispatch.fr/api/v1/recommendations?article_id={uuid}&limit=5
What it returns: `article_id`, `count`, `recommendations[]` with `id`, `title`, `rubrique`, `created_at`, `player_url`.
To handle: 400 article_id missing or malformed · 404 article not found
Displaying
The player on your page, and the subtitles.
Display the player
One line in your template
The player drops in with one line. Automatic mode finds the audio matching the page on its own, from its canonical address.
<script src="https://wedispatch.fr/player.js" data-article="{article_id}" defer></script>
<!-- ou une seule fois pour tout le site : l'audio est retrouvé depuis l'adresse de la page -->
<script src="https://wedispatch.fr/player.js" data-site="{client_id}" defer></script>
Worth knowing: If your theme filters content through an unusual entry point, automatic insertion may fail. A `<div data-wedispatch>` container placed where you want gives you control over the exact position.
The server-side block
For platforms that forbid iframes
Some newsrooms do not assemble their pages with JavaScript: they compose them server-side from blocks supplied by external services, and their rules forbid iframes. WeDispatch is a block provider. You declare ours, you place it in a template, and your own engine renders the HTML.
GET https://wedispatch.fr/api/blocs/contrat
GET https://wedispatch.fr/api/blocs/lecteur?article={article_id}
GET https://wedispatch.fr/api/blocs/lecteur?externe={votre_id}&site={client_id}
# les deux fichiers que le contrat declare, servis par nous, versionnes avec lui
https://wedispatch.fr/sdk/wedispatch-bloc.js
https://wedispatch.fr/sdk/bloc-texte.css
What it returns: The contract declares two blocks, the player and the synchronised text, with their template, parameters and cache policy. The data endpoint returns `{ internal: { data } }`: title, audio address, duration, chapters, summary and the address of the word-level timing file.
Worth knowing: THE MARKUP IS YOURS, NOT OURS: we declare no stylesheet for the player, and the markup leans on your own design system's components. The contract is readable without a key, so your validation tool can check it without us. An account under embargo is not served through this path: with no visitor token, a signed address handed to a third-party server would be a back door.
The render toolkit
Code to copy, not a dependency
If you want your own player without writing it: an isolated web component, the same logic without any interface for your own rendering, an adapter that drives a player already present in your design system, and two reference implementations, iOS and Android. You copy the files and version them with your own.
<!-- les fichiers, à recopier chez vous -->
https://wedispatch.fr/sdk/wedispatch-player.js le composant web
https://wedispatch.fr/sdk/reference-web.js la logique, sans interface
https://wedispatch.fr/sdk/wedispatch-sipaui.js l'adaptateur d'un lecteur existant
https://wedispatch.fr/sdk/reference-ios.swift
https://wedispatch.fr/sdk/reference-android.kt
https://wedispatch.fr/sdk/LISEZMOI.md le mode d'emploi
What it returns: Playback, word-by-word highlighting, chapters, resume where you left off, reading comfort, sticky bar, keyboard accessibility and listening milestones. No file carries a key or calls this API: your server builds a manifest from `GET /podcasts/{id}` and `GET …/words`.
Worth knowing: THESE FILES DO NOT UPDATE THEMSELVES, and that is the price of independence: a fix on our side only reaches you when you take the file again. Changes are announced in the release notes. Two files in the `/sdk` folder are NOT part of the toolkit and must not be copied, `wedispatch-bloc.js` and `bloc-texte.css`: they are served for the server-side block and versioned with its contract.
GET
Subtitles
/api/v1/podcasts/{article_id}/subtitles
A subtitle file aligned on the ACTUAL reading, not on the source text. That is what avoids drift from the very first spelled-out number.
What the route reads
formatoptional`srt` by default, or `vtt`.
GET https://wedispatch.fr/api/v1/podcasts/{article_id}/subtitles?format=vtt
What it returns: The file itself, as plain text. This is not JSON.
To handle: 400 unknown format · 404 article or alignment not found · 409 audio produced before word-level alignment
GET
The day's selection
/api/v1/selection
The articles voiced in the last twenty-four hours, in listening order, with their duration and their summary. It is the morning edition, as JSON: enough to build a playlist without going through our iframe.
What the route reads
rubriqueoptionalRestricts the selection to one section. Case-insensitive match.
limitoptionalTwenty by default, fifty at most.
window_hoptionalWindow in hours, twenty-four by default, one week at most.
GET https://wedispatch.fr/api/v1/selection?rubrique=Sports&limit=10
What it returns: `mode` (`day` when the day's window returned something, `latest` otherwise), `window_h`, `rubrique`, `count`, `total_duration_s`, `generated_at`, and `items[]` carrying for each track `article_id`, `title`, `summary`, `duration_s`, `published_at`, `audio`, its `chapters` when there are any, `embed` and `subtitles`.
Worth knowing: AN OUT-OF-RANGE VALUE IS CLAMPED, never refused. A site that has published nothing since yesterday gets its last five articles with `mode: latest`: read that field before writing "Morning edition" above the list. The route returns the full files, signed when the account protects them: your server then decides what it hands to each visitor.
GET
Search for a phrase inside the audio
/api/v1/podcasts/{article_id}/search
Where is this phrase spoken? The answer is the exact second and the surrounding excerpt. Enough to build a link that starts at the right second, or an internal search that points at a passage rather than at an article.
What the route reads
qrequiredThe phrase to look for, two hundred characters at most.
GET https://wedispatch.fr/api/v1/podcasts/{article_id}/search?q=la%20piscine
What it returns: `article_id`, `title`, `query`, `duration_s`, `count`, and `results[]` carrying for each occurrence `t`, the exact second, and `excerpt`, six words on each side.
To handle: 400 missing q parameter or invalid identifier · 404 unknown article · 409 no published audio · 409 audio produced before word-level alignment
Worth knowing: THE SEARCH IS STRICT: an exact sequence of words, with no spelling correction and no approximation. A wrong result costs more than no result, since it would send someone to the wrong passage. Twenty occurrences at most, and within ONE article: searching a whole archive would require an index, which does not exist.
Editing
Acting on audio already produced without regenerating all of it.
GET
The position of every word
/api/v1/podcasts/{article_id}/words
The first of the two steps in an edit: a passage is designated by word indexes, so you first need to know where each word falls in the audio.
GET https://wedispatch.fr/api/v1/podcasts/{article_id}/words
What it returns: `article_id`, `title`, `duration_s`, `words[]` with the start and end of each word, and `chapters[]` when the article carries any: the same sidecar file holds them, and returning them here saves a second call to anyone building a player.
To handle: 403 editing is not open on this account · 409 the engine used produces no word-level alignment · 409 audio predates the alignment
POST
Edit a passage
/api/v1/podcasts/{article_id}/correct
You rewrite the faulty passage, that passage alone is resynthesised and stitched back into the existing audio. The file address does not change, so your pages have nothing to update.
What the route reads
from_wordrequiredIndex of the first word to replace, in the array returned by the previous route.
to_wordrequiredIndex of the last word, inclusive.
textrequiredWhat should be heard instead.
pronunciationsoptionalPronunciations valid for this edit only, `[{ "de": "Ploërmel", "vers": "Plo-air-mel" }]`, five at most. They override your lexicon without changing it. The text shown under the player keeps your spelling.
accept_credit_useoptionalConfirms that you accept the spend. An edit is billed pro rata of what it rewrites and is never free: without this field the request is refused with a 402 stating its price.
POST https://wedispatch.fr/api/v1/podcasts/{article_id}/correct
{
"from_word": 142,
"to_word": 149,
"text": "deux virgule quatre millions d'euros"
}
What it returns: `ok`, `version`, `duration_s`, `patch_chars`, `patch_chars_used`, `credits_used`, `review`.
To handle: 402 the spend was not confirmed · 403 editing is not open on this account · 409 audio with no word-level alignment
Worth knowing: The price is returned ON THE REFUSAL, in `credits_required`: the moment you are refused is exactly when you need to know what the request would cost. An edit is billed pro rata of what it rewrites (a thousand characters make one credit, so `credits_required` can be 0.02); there is no such thing as a zero-credit edit. `review` set to true flags a tight stitch worth listening to before publication.
Configuring
The account settings a key can read and write.
GET
Read the settings
/api/v1/profile
Every account setting, grouped by area, plus what your plan actually opens.
GET https://wedispatch.fr/api/v1/profile
What it returns: `player`, `reading` (including the pronunciation lexicon), `sound`, `editorial`, `gate`, `capabilities`, `player_templates`, and `editable`: the exact list of fields a key can write.
Worth knowing: `editable` is the source of truth. Rather than copying the list below into your code, read it: it will tell you what is writable on the day that changes. ALSO READ `editorial.summary_effective`: charter mode switches the summary off even when it is enabled, and this is the only field that answers yes or no to the question that matters. `editorial` is read-only: these are commitments, not display preferences.
GET
Read the pronunciation lexicon
/api/v1/pronunciations
The account's pronunciation corrections, applied to every article before synthesis.
GET https://wedispatch.fr/api/v1/pronunciations
What it returns: `pronunciations` (the `{ de, vers }` list), `count`, `max`.
Worth knowing: READING is not gated: an account that loses the option must still be able to read and export what it entered. Writing is what the plan gates.
POST
Add or correct a pronunciation
/api/v1/pronunciations
Merges into the existing lexicon: what you do not send is left alone. A word already present has its pronunciation replaced, never duplicated.
POST https://wedispatch.fr/api/v1/pronunciations
{ "de": "Ploërmel", "vers": "Plo-air-mel" }
What it returns: The full list after the change, plus `added`, `replaced`, `ignored`.
To handle: 400 no usable entry (`de` and `vers` required, 80 characters at most) · 403 the lexicon is not open on this account · 409 lexicon full (200 entries)
Worth knowing: A call that posted NOTHING returns an error, never a 200: otherwise your interface would show "saved" for a correction that does not exist. Case does not create duplicates, accents do: “Ploërmel” and “Ploermel” are two entries, deliberately.
DELETE
Remove a pronunciation
/api/v1/pronunciations
By source word, case-insensitive. Removing an absent word is not an error.
What it returns: The full list after the change, plus `removed`.
To handle: 400 neither `de`, nor `pronunciations`, nor `all: true`
Worth knowing: Clearing everything REQUIRES `all: true`. A bodyless DELETE is what an HTTP client sends when it calls the wrong thing; letting it erase two hundred unrecoverable entries would be a trap.
PUT
Change the settings
/api/v1/profile
Twenty-five fields are writable by key, including the pronunciation lexicon, the default voice, the voice rules and the player's appearance.
PUT https://wedispatch.fr/api/v1/profile
{ "default_voice": "...", "player_template": "..." }
What it returns: `ok`, plus `ignored[]` if you sent fields that are not writable by key.
To handle: 400 no writable field in the body sent · 403 the requested setting is not open on this account · 409 the setting requires a word-level alignment that is missing
Worth knowing: A non-writable field is not an error: it is set aside and RETURNED in `ignored`, so that you see it instead of assuming it was applied. Conditional access mode is never writable by key, deliberately.
Measuring
Consumption and plays. Counts, never people.
GET
The state of the account
/api/v1/account
The subscribed volume, the consumption of the credit period, and what the plan includes.
GET https://wedispatch.fr/api/v1/account
What it returns: `plan_label`, `volume`, `usage` (including `credits_used`, `credits_quota`, `articles_left`, `period_start`, `period_end`, `resets_at`, `counted_since`), `included`, `access`, `offre`, `urls`.
Worth knowing: `plans` and `next_plan` are empty and will stay so: they date from the old plans and are kept only so no existing integration breaks. The structure of `offre` is not described here, for want of an audit: read it rather than assume it.
GET
The consumption of the period
/api/v1/usage
What has been spent over the current credit period, in credits and in characters. For a paid volume, the period runs from one anniversary of the subscription to the next: `period_end` is the moment the volume starts again.
GET https://wedispatch.fr/api/v1/usage
What it returns: `period`, `quota_credits`, `credits_used`, `credits_left`, `period_start`, `period_end`, `period_source`, `counted_since`, `percent_used`, `warning`, `blocked`, `generations`, `regenerations`, `listens`, `audio_minutes`.
GET
Plays
/api/v1/stats
Aggregate counts, per article, per section and per byline. Never per person: that information does not exist.
GET https://wedispatch.fr/api/v1/stats
What it returns: `generated_at`, `totals`, `last_30_days`, `articles`, `authors`.
Worth knowing: No parameters: no period filter, no pagination. The internal structure of the aggregates is not detailed here, for want of an audit: it is stable, but we would rather have you read it than describe it from memory.
POST
Telling us what went wrong
/api/v1/feedback
A short note, sent by the WordPress plugin when someone deactivates it, and ONLY if they clicked "Send". Documented here because it exists: a route nobody writes down is a route nobody remembers to secure.
What it returns: `ok`. Always 200: the caller is deactivating a plugin, and their action must not depend on any failure of ours.
Worth knowing: Never contains article content, and nothing about a listener. Unknown fields are ignored without error, so that a future version of the plugin does not lose its feedback over a detail of form.
Voices
The catalogue, and an audio preview before you choose.
GET
The voice catalogue
/api/v1/voices
The voices available to your account, including your signature voices.
GET https://wedispatch.fr/api/v1/voices
What it returns: `engine`, `default`, `voices[]` with `id`, `label`, `gender`, `style`, `langs[]`, and `sample` where a clip exists.
Worth knowing: This route is served WITHOUT a key, because the documentation and the demo depend on it. On the other hand, a key that is PRESENTED and refused makes the call fail: it never falls back to the public catalogue, which would hide a dead key.
POST
Preview a voice
/api/v1/voices/preview
An audio clip on your own text, before you choose. Costs no credit.
What the route reads
textrequiredFrom 100 to 3,000 characters.
voiceoptionalOtherwise the account's default voice.
languageoptionalOtherwise the default language.
POST https://wedispatch.fr/api/v1/voices/preview
{ "text": "...", "voice": "..." }
What it returns: The audio file itself. The `X-WeDispatch-Billed` header reads zero, and the `X-WeDispatch-Voice` header says which voice actually spoke.
To handle: 400 text too short or too long · 429 thirty previews per hour exceeded
Worth knowing: An unknown voice is NOT refused here: it is replaced by the default voice. Read the response header to know which one served, otherwise you will believe you listened to the one you asked for.
POST
Twenty seconds before having an account
/api/v1/extrait
What the WordPress plugin does on a site that is not connected yet, when an administrator clicks to hear their latest post. No key: this is precisely the person who does not have one yet.
What the route reads
texterequiredThe opening of the post. Only the first twenty seconds are read, whatever is sent.
langueoptionalOtherwise French.
siteoptionalThe address of the site, to know where the samples come from.
POST https://wedispatch.fr/api/v1/extrait
{ "texte": "...", "langue": "fr", "site": "https://exemple.fr/" }
What it returns: The MP3 file itself.
To handle: 400 text too short · 415 body other than JSON · 429 four samples per hour exceeded · 503 daily cap reached · 502 the voice could not be produced
Worth knowing: The text is not kept. The route is bounded by a daily cap shared by every keyless sample, which refuses when it cannot be read: for regular use, connect the site.
Conditional access
For restricted content: verifying a listening token server side.
GET
The access configuration
/api/v1/access
The account's access mode and the secret used to sign listening tokens.
GET https://wedispatch.fr/api/v1/access
What it returns: `mode`, `preview_seconds`, `token_ttl_s`, `secret`, `client_id`.
Worth knowing: The secret never leaves the server. Call this route from your back office, never from a page.
POST
Verify a listening token
/api/v1/access/check
For restricted content: your server checks that a token really grants access to this article.
What the route reads
articlerequiredThe identifier of the article requested.
tokenoptionalThe token presented. Its absence simply returns a reasoned refusal.
POST https://wedispatch.fr/api/v1/access/check
{ "article": "{article_id}", "token": "..." }
What it returns: `valid`, `status`, `reason`, `detail`, `mode`. The reason says precisely why: token missing, malformed, invalid signature, wrong article, or expired.
To handle: 400 article field missing
POST
Exchange a code for a key
/api/v1/connect/exchange
What the WordPress plugin does on installation: it exchanges a short-lived code for its own key, server to server. The user never sees the key.
What the route reads
coderequiredThe code obtained on the authorisation screen.
secretrequiredYour installation's secret.
POST https://wedispatch.fr/api/v1/connect/exchange
{ "code": "...", "secret": "..." }
What it returns: `api_key`, `plan`, `tier`, `site`, `label`.
To handle: 400 code expired or invalid · 400 maximum of ten active keys reached · 404 account not found
Worth knowing: Along with the voice catalogue and the trial sample, it is one of the few routes that do not ask for a key: its whole purpose is to obtain one.
A case that is not covered?
This documentation describes what the code does, not what we would like it to do. If you are looking for a route that is not here, it probably does not exist: tell us what you were trying to do, it is useful even when the answer is no.