The participant API
On this page
These are the endpoints dembrane’s own portal uses to record a conversation into a project. A participant has no account, so none of them needs a sign-in. If you build your own recorder or kiosk, this is what you call.
Important
What you can rely on: dembrane's own portal depends on these paths, bodies and error codes, and v3 kept them for that reason. What you cannot rely on: they are not declared a public contract. There is no version to pin and no reference beyond this page. Other routes the portal and the dashboard call, such as those under /api/v2/bff/*, are internal.
The API allows cross-origin browser calls from the dashboard and the portal only. A recorder that runs in a browser on your own domain cannot call the hosted API directly. Call it from a server or a native app.
The flow#
GET /api/participant/projects/:project_id the project's portal settings
POST /api/participant/projects/:project_id/conversations/initiate a conversation and its token
for each piece of audio:
POST /api/participant/conversations/:conversation_id/get-upload-url
POST <upload_url> the bytes, straight to storage
POST /api/participant/conversations/:conversation_id/confirm-upload
POST /api/participant/conversations/:conversation_id/finish
1. Start a conversation#
POST /api/participant/projects/:project_id/conversations/initiate
Content-Type: application/json
{ "name": "Group 3", "pin": "" }
nameandpinare required strings. The handler does not readpin: send an empty string, as the portal does.- Optional:
email,user_agent,tag_id_list,source,visitor_id. A tag is attached only when it belongs to the project.
The response body is the conversation. The response header x-participant-token carries the token for this conversation.
2. Keep the token#
Send the token back in the x-participant-token request header on every later call for this conversation. A call with a wrong token is refused with 403 and the code participant.token_invalid.
3. Upload audio#
Ask for an upload URL:
POST /api/participant/conversations/:conversation_id/get-upload-url
Content-Type: application/json
{ "filename": "chunk-1.webm", "content_type": "audio/webm", "conversation_id": "<conversation id>" }
The answer has chunk_id, upload_url, fields and file_url. This is a presigned form upload, not a presigned PUT. Send a form POST to upload_url: add every entry of fields first, then the file in a field named file, last.
Then confirm the upload:
POST /api/participant/conversations/:conversation_id/confirm-upload
Content-Type: application/json
{ "chunk_id": "<chunk_id>", "file_url": "<file_url>", "timestamp": "2026-10-13T09:30:00Z" }
file_url must be the one you were given for this conversation and chunk. An optional source defaults to PORTAL_AUDIO. Confirming creates the chunk and starts its transcription.
4. Or send typed text#
POST /api/participant/conversations/:conversation_id/upload-text
Content-Type: application/json
{ "timestamp": "2026-10-13T09:30:00Z", "content": "What the participant typed." }
An optional source defaults to PORTAL_TEXT. The text becomes a chunk that already has its transcript.
5. Finish#
POST /api/participant/conversations/:conversation_id/finish
When the conversation is finished and every chunk is transcribed, dembrane merges the audio and writes the summary. A conversation that gets no new chunk for 5 minutes is finished automatically. Audio that arrives after that reopens the conversation, and the next finish processes it again.
To follow the result, subscribe to webhooks for conversation.transcribed and conversation.summarized.
Limits#
| Limit | Value |
|---|---|
| Upload URLs | 40 per minute per conversation |
| File size | 2 GB per file |
| Time to finish one upload | 1 hour |
| Audio formats | aac, wav, mp3, ogg, flac, webm, opus, m4a, mp4, mpeg |
| Participant replies | 20 per minute per conversation |
| Report subscriptions | 20 per 10 minutes per address |
get-upload-urlanswers500with the codeupload.url_failedwhen the rate limit is hit and when the project is closed. It does not answer429.- A file under 1024 bytes is kept as a chunk marked “Audio not playable”.
- A project that is closed to new conversations refuses the other participant calls with
403and the codeconversation.not_open.
All the endpoints#
| Method and path | What it does |
|---|---|
GET /api/participant/projects/:project_id |
The portal settings of an open project: language, tags, portal texts, the verify and reply switches, legal basis, privacy policy URL, organiser name, logo. |
POST /api/participant/projects/:project_id/conversations/initiate |
Starts a conversation and returns the token. |
GET /api/participant/projects/:project_id/conversations/:conversation_id |
The participant’s view of the conversation. |
GET /api/participant/projects/:project_id/conversations/:conversation_id/chunks |
The conversation’s chunks, newest first. |
GET /api/participant/projects/:project_id/conversations/:conversation_id/replies |
Replies under the conversation. |
DELETE /api/participant/projects/:project_id/conversations/:conversation_id/chunks/:chunk_id |
Removes one chunk. |
POST /api/participant/conversations/:conversation_id/check-s3 |
Returns probe_url, a presigned URL the portal tries before recording to learn that storage is reachable. |
POST /api/participant/conversations/:conversation_id/get-upload-url |
A presigned form upload for one new chunk. |
POST /api/participant/conversations/:conversation_id/confirm-upload |
Confirms the upload and creates the chunk. |
POST /api/participant/conversations/:conversation_id/upload-text |
Adds typed text. |
POST /api/participant/conversations/:conversation_id/finish |
The finish signal. |
POST /api/participant/conversations/:conversation_id/ping |
A liveness ping for the host’s live monitor. |
POST /api/conversations/:conversation_id/get-reply |
Streams a reply to the participant. Body: language. |
GET /api/participant/:project_id/report/latest and the other /api/participant/.../report/... routes |
The participant’s report, its views and metrics, subscribe and unsubscribe. |
There is no multipart upload-chunk route. Audio goes through the presigned upload only.
Comments