From audio to minutes

The long half of this API is asynchronous. You upload, we work, you poll. Here is exactly what happens in between.

There are no webhooks yet, so a client follows a meeting by reading its status. The status route asks you not to poll more often than every thirty seconds, and says so in its Cache-Control header.

1. Ask for an upload target

The audio never travels through this API. You ask for a one-time URL and PUT the file to it yourself. The URL is signed for exactly the content_type you named.

curl -X POST 'https://api.notulai.nl/v1/meetings/mtg_b39d31c7/upload-url' \
  -H 'Authorization: Bearer nla_live_...' \
  -H 'Content-Type: application/json' \
  -d '{
  "content_type": "audio/mpeg"
}'

Then PUT the file to the URL you got back. No Authorization header: the signed URL carries its own. Once the file lands, the meeting moves on by itself.

# UPLOAD_URL is the upload_url from the previous response.
curl -X PUT "$UPLOAD_URL" \
  -H 'Content-Type: audio/mpeg' \
  --data-binary '@meeting.mp3'

Send the same Content-Type you asked for

This one PUT goes straight to storage, so it is the only call in the whole API that does not answer in our error envelope. A mismatch is refused by the storage layer with an XML body and a signature error. Match the header exactly and the problem disappears.

2. Follow the status

These are the values a meeting moves through when all goes well.

1
created

The meeting exists. No audio yet, and participants can still be changed.

2
audio_uploaded

The recording arrived and is being prepared.

3
transcribing

Speech to text is running.

4
transcribed

The transcript is available.

5
generating_minutes

The minutes are being written from the transcript.

6
minutes_ready

Done. Both the transcript and the minutes can be fetched.

And these end it

409
transcription_failedTranscription did not complete. The recording stays; create a new meeting to try again.
409
minutes_failedThe transcript exists but the minutes could not be written.
409
transcribe_blockedThere was not enough transcription quota when the audio arrived. Topping up does not restart it: create the meeting again.
409
audio_rejectedThe file was refused, most often because it is over one gigabyte.

Asking for a transcript or minutes before they exist is not an error in your code: it answers 409 TRANSCRIPT_NOT_READY or 409 MINUTES_NOT_READY, which means come back later. A 404 on the same route means the meeting is gone or was never yours to see.

3. Collect the result

curl -X GET 'https://api.notulai.nl/v1/meetings/mtg_b39d31c7/minutes' \
  -H 'Authorization: Bearer nla_live_...'

Names are in the minutes, not in the transcript

The transcript keeps unattributed speaker labels such as spk_0: they say that speakers differ, not who they are. Attribution to named people happens while the minutes are written, which is why participants are worth setting before you upload. Nothing in this API maps a spk_ label to a member id.

Participants are set before the audio

Their names improve how names are spelled in the transcript and make speaker attribution possible in the minutes. Pass them when you create the meeting, or replace them while the meeting still has no audio. Once a recording is in, changing them would change nothing, so it is refused with 409 PARTICIPANTS_LOCKED. Delete the meeting and create it again to start over with a different set.

Everything else stays editable

Participants are the exception, not the rule. The title, the date, the start time and the project can be changed at any point in a meeting's life, including long after the minutes are ready, because none of them steers the processing. A meeting created under a placeholder title does not have to be thrown away.

curl -X PATCH 'https://api.notulai.nl/v1/meetings/mtg_b39d31c7' \
  -H 'Authorization: Bearer nla_live_...' \
  -H 'Content-Type: application/json' \
  -d '{
  "date": "2026-09-01",
  "project_id": "proj_5b28e4",
  "time": "09:30",
  "title": "Weekly stand-up"
}'

Only the fields you send are touched. Sending time or project_id as an empty string clears it; leaving a field out keeps it as it is. Changing the title, the date or the project re-indexes the meeting for search, which costs a credit once its minutes have been indexed. See limits and quota.

See the reference for the exact shape of every call above.