Meetings
Every path is relative to https://app.lobbe.ai/api/v1, and every request sends Authorization: Bearer $LOBBE_TOKEN. The API reference lists every route and explains the x-lobbe-* fields.
GET /recordings/ready
Section titled “GET /recordings/ready”List finished meetings, newest first.
| Property | Value |
|---|---|
| Scopes | recordings:read |
| Spends minutes | No |
| Rate limit | read: 600 requests per 10 minutes per token |
| Success | 200 |
Parameters
Section titled “Parameters”| Name | In | Type | Required | Description |
|---|---|---|---|---|
limit |
query |
integer |
No | How many meetings to return, 1–100 (default 20) |
cursor |
query |
string |
No | The nextCursor of the previous page, passed back as is; absent = the newest page, and any other value is a 400 |
seriesId |
query |
string |
No | Only the meetings of this series, one you own or joined (a series you can’t read is a 404); absent = your own meetings |
Response 200
Section titled “Response 200”A page of finished meetings, newest first.
| Field | Type | Always present | Description |
|---|---|---|---|
schemaVersion |
1 |
Yes | Always 1 within API v1 |
items[] |
object[] |
Yes | The meetings on this page: READY ones only, the meetings a bundle can be read for |
items[].id |
string |
Yes | The meeting’s id |
items[].title |
string | null |
Yes | The meeting’s title: the one its owner or the submitting token gave it, else its Lobbe’s; null when it has none |
items[].kind |
"MEETING" | "LECTURE" | "INTERVIEW" | "VOICE_MEMO" | "CALL" |
Yes | The kind of recording, which picks the Lobbe’s template: MEETING, LECTURE, INTERVIEW, VOICE_MEMO or CALL |
items[].createdAt |
string |
Yes | When Lobbe received the meeting (ISO 8601, UTC); listReadyRecordings is ordered by it |
items[].recordedAt |
string |
Yes | When the meeting happened (ISO 8601, UTC): the recordedAt it was submitted with, else when Lobbe received it |
items[].durationSeconds |
integer | null |
Yes | The audio’s length in seconds, as Lobbe measured it; null if unknown |
items[].url |
string (uri) |
Yes | The meeting’s page in the web app |
items[].bundleUrl |
string (uri) |
Yes | GET it with a token holding recordings:read for the whole meeting (getRecordingBundle) |
items[].series |
object | null |
Yes | The series the meeting is filed in; null when it is in none, or while series are switched off |
items[].series.id |
string |
Yes | The series’ id, as seriesId takes it |
items[].series.name |
string |
Yes | The series’ name |
items[].displayTitle |
string | null |
Yes | The title as the app shows it: in a series titled by date, the series’ name and the meeting’s date in the Lobbe’s language; otherwise the meeting’s title, else its Lobbe’s; null when there is none |
nextCursor |
string | null |
Yes | Pass it back as cursor for the next, older page; null on the last page |
Errors answer { error: { code, message, details? } }, and code is the contract: see Errors.
GET /recordings/search
Section titled “GET /recordings/search”Search the owner’s finished meetings by their words, newest meeting first.
| Property | Value |
|---|---|
| Scopes | recordings:query |
| Spends minutes | No |
| Rate limit | search: 120 requests per 10 minutes per token |
| Success | 200 |
Parameters
Section titled “Parameters”| Name | In | Type | Required | Description |
|---|---|---|---|---|
q |
query |
string |
Yes | The words to find, 1–200 characters. A meeting matches when every word appears in its title, transcript or Lobbe, even inside a longer word; case, diacritics, the half-space (ZWNJ) and Arabic letter forms are ignored, and Persian digits match Latin ones. A q with no word in it is a 400 |
seriesId |
query |
string |
No | Only the meetings of this series, one you own or joined (a series you can’t read is a 404); absent = your own meetings |
kind |
query |
"MEETING" | "LECTURE" | "INTERVIEW" | "VOICE_MEMO" | "CALL" |
No | Only meetings of this kind: MEETING, LECTURE, INTERVIEW, VOICE_MEMO or CALL |
from |
query |
string (date-time) |
No | Only meetings that happened at or after this time (recordedAt), ISO 8601 with an offset |
to |
query |
string (date-time) |
No | Only meetings that happened at or before this time (recordedAt), ISO 8601 with an offset |
limit |
query |
integer |
No | How many meetings to return, 1–50 (default 20) |
cursor |
query |
string |
No | The nextCursor of the previous page, passed back as is; absent = the first page, and a malformed value is a 400 |
Response 200
Section titled “Response 200”Finished meetings whose words match, newest meeting first (by recordedAt); searching is free.
| Field | Type | Always present | Description |
|---|---|---|---|
schemaVersion |
1 |
Yes | Always 1 within API v1 |
items[] |
object[] |
Yes | The meetings on this page: READY ones only, each a listReadyRecordings item plus its snippet |
items[].id |
string |
Yes | The meeting’s id |
items[].title |
string | null |
Yes | The meeting’s title: the one its owner or the submitting token gave it, else its Lobbe’s; null when it has none |
items[].kind |
"MEETING" | "LECTURE" | "INTERVIEW" | "VOICE_MEMO" | "CALL" |
Yes | The kind of recording, which picks the Lobbe’s template: MEETING, LECTURE, INTERVIEW, VOICE_MEMO or CALL |
items[].createdAt |
string |
Yes | When Lobbe received the meeting (ISO 8601, UTC); listReadyRecordings is ordered by it |
items[].recordedAt |
string |
Yes | When the meeting happened (ISO 8601, UTC): the recordedAt it was submitted with, else when Lobbe received it |
items[].durationSeconds |
integer | null |
Yes | The audio’s length in seconds, as Lobbe measured it; null if unknown |
items[].url |
string (uri) |
Yes | The meeting’s page in the web app |
items[].bundleUrl |
string (uri) |
Yes | GET it with a token holding recordings:read for the whole meeting (getRecordingBundle) |
items[].series |
object | null |
Yes | The series the meeting is filed in; null when it is in none, or while series are switched off |
items[].series.id |
string |
Yes | The series’ id, as seriesId takes it |
items[].series.name |
string |
Yes | The series’ name |
items[].displayTitle |
string | null |
Yes | The title as the app shows it: in a series titled by date, the series’ name and the meeting’s date in the Lobbe’s language; otherwise the meeting’s title, else its Lobbe’s; null when there is none |
items[].snippet |
string | null |
Yes | Up to 200 characters around the first match, from the transcript, else from the Lobbe when only it matched; null when the words were found in the title alone |
nextCursor |
string | null |
Yes | Pass it back as cursor for the next, older page; null on the last page |
Errors answer { error: { code, message, details? } }, and code is the contract: see Errors.
GET /recordings/{id}/ask/quote
Section titled “GET /recordings/{id}/ask/quote”What asking this meeting a question will cost, in minutes.
| Property | Value |
|---|---|
| Scopes | recordings:query |
| Spends minutes | No |
| Rate limit | search: 120 requests per 10 minutes per token |
| Success | 200 |
Parameters
Section titled “Parameters”| Name | In | Type | Required | Description |
|---|---|---|---|---|
id |
path |
string |
Yes | The meeting’s id |
Response 200
Section titled “Response 200”What one question to this meeting costs; reading the quote is free.
| Field | Type | Always present | Description |
|---|---|---|---|
schemaVersion |
1 |
Yes | Always 1 within API v1 |
minutes |
integer |
Yes | The price in minutes: what askRecording charges, worked out the same way from the meeting’s transcript, so the two agree while the transcript is unchanged |
Errors answer { error: { code, message, details? } }, and code is the contract: see Errors.
POST /recordings/{id}/ask
Section titled “POST /recordings/{id}/ask”Ask one meeting a question, answered from its transcript and Lobbe; each answer is charged.
| Property | Value |
|---|---|
| Scopes | recordings:query |
| Spends minutes | Yes |
| Rate limit | ask: 30 requests per 10 minutes per token |
| Success | 200 |
This route can spend minutes: send an Idempotency-Key header, and a retry with the same key returns the first outcome instead of charging again. See Idempotency and retries.
Parameters
Section titled “Parameters”| Name | In | Type | Required | Description |
|---|---|---|---|---|
id |
path |
string |
Yes | The meeting’s id |
Idempotency-Key |
header |
string |
No | Recommended. Make one key (8–64 characters, a random UUID say) once per new meeting or question, and send that same key unchanged on every retry of that request; never reuse it for a different request. A replay returns the original outcome and never charges twice. Keys are the owner’s for good: they never expire. |
Request body (application/json)
Section titled “Request body (application/json)”The question, with a price ceiling if you want one. A field not listed here is a 400.
| Field | Type | Required | Description |
|---|---|---|---|
question |
string |
Yes | The question, trimmed, 1–500 characters |
maxMinutes |
integer |
No | The most you agree to pay, in minutes (at least 1): when the price is higher, the question is refused with 409 PRICE_ABOVE_MAX (its details carry minutes and maxMinutes) and nothing is charged; absent = no ceiling |
idempotencyKey |
string |
No | The idempotency key in the body, instead of the Idempotency-Key header (8–64 characters); the header wins when both are sent |
Response 200
Section titled “Response 200”The answer, already charged; a replay with the same idempotency key returns it again and never charges twice.
| Field | Type | Always present | Description |
|---|---|---|---|
schemaVersion |
1 |
Yes | Always 1 within API v1 |
answer |
string |
Yes | The answer. [n] marks the claim that citations[n - 1] supports, numbered 1, 2, … in reading order |
citations[] |
object[] |
Yes | Where the meeting says it: at most 3 turns, in the order the answer first cites them |
citations[].startMs |
number |
Yes | When the cited turn starts, in milliseconds from the start of the meeting’s audio |
citations[].speaker |
string | null |
Yes | Who spoke it: the turn’s speaker label as transcribed (e.g. speaker_0), never a name given to the voice; null when the turn has none |
minutes |
integer |
Yes | What this answer charged, in minutes; a refusal is charged too |
refused |
boolean |
Yes | true when Lobbe declines to answer from what was said: answer says why, and it is still charged |
Errors answer { error: { code, message, details? } }, and code is the contract: see Errors.
GET /recordings/{id}/bundle
Section titled “GET /recordings/{id}/bundle”One finished meeting: its transcript, its Lobbe and its stats; ?format=markdown returns one Markdown document.
| Property | Value |
|---|---|
| Scopes | recordings:read |
| Spends minutes | No |
| Rate limit | read: 600 requests per 10 minutes per token |
| Success | 200 |
Add ?format=markdown and it answers text/markdown: the same content as one Markdown document.
Parameters
Section titled “Parameters”| Name | In | Type | Required | Description |
|---|---|---|---|---|
id |
path |
string |
Yes | The meeting’s id |
format |
query |
"json" | "markdown" |
No | json (the default) or markdown, which answers one text/markdown document of the whole meeting; any other value is a 400 |
Response 200
Section titled “Response 200”One finished meeting: its transcript, its Lobbe and its stats.
| Field | Type | Always present | Description |
|---|---|---|---|
schemaVersion |
1 |
Yes | Always 1 within API v1 |
recording |
object |
Yes | The meeting itself |
recording.id |
string |
Yes | The meeting’s id |
recording.title |
string | null |
Yes | The meeting’s title: the one its owner or the submitting token gave it, else its Lobbe’s; null when it has none |
recording.kind |
"MEETING" | "LECTURE" | "INTERVIEW" | "VOICE_MEMO" | "CALL" |
Yes | The kind of recording, which picks the Lobbe’s template: MEETING, LECTURE, INTERVIEW, VOICE_MEMO or CALL |
recording.source |
"IN_APP" | "UPLOAD" |
Yes | How it reached Lobbe: IN_APP (recorded in the app) or UPLOAD (a file, a token’s submission included) |
recording.createdAt |
string |
Yes | When Lobbe received the meeting (ISO 8601, UTC) |
recording.recordedAt |
string |
Yes | When the meeting happened (ISO 8601, UTC): the recordedAt it was submitted with, else when Lobbe received it |
recording.durationSeconds |
integer | null |
Yes | The audio’s length in seconds, as Lobbe measured it; null if unknown |
recording.url |
string (uri) |
Yes | The meeting’s page in the web app |
recording.series |
object | null |
Yes | The series the meeting is filed in; null when it is in none, or while series are switched off |
recording.series.id |
string |
Yes | The series’ id, as seriesId takes it |
recording.series.name |
string |
Yes | The series’ name |
recording.access |
"owner" | "member" |
Yes | owner when the meeting is yours; member when it belongs to a series you joined |
recording.displayTitle |
string | null |
Yes | The title as the app shows it: in a series titled by date, the series’ name and the meeting’s date in the Lobbe’s language; otherwise the meeting’s title, else its Lobbe’s; null when there is none |
recording.subtitle |
string | null |
Yes | In a series titled by date, the meeting’s own title, else its Lobbe’s; null otherwise |
transcript |
object |
Yes | What was said, turn by turn |
transcript.language |
string |
Yes | The language detected in the audio, as an ISO 639-1 code (fa, en, …) |
transcript.revision |
integer |
Yes | Goes up by one with each owner edit and each time the meeting is checked again for missed voices; 0 = neither has happened |
transcript.edited |
boolean |
Yes | Whether the transcript has changed since it was first made, by an owner edit or by checking the meeting again (revision above 0) |
transcript.speakers[] |
object[] |
Yes | One row per person who spoke; voices given the same name are one person |
transcript.speakers[].label |
string |
Yes | The person’s label (e.g. speaker_0), as segments[].speaker and stats carry it |
transcript.speakers[].name |
string | null |
Yes | The name given to this voice; null when nobody named it |
transcript.segments[] |
object[] |
Yes | The turns, in order |
transcript.segments[].speaker |
string |
Yes | Who spoke: a speakers[].label |
transcript.segments[].startMs |
integer |
Yes | When the turn starts, in milliseconds from the start of the audio |
transcript.segments[].endMs |
integer |
Yes | When the turn ends, in milliseconds from the start of the audio |
transcript.segments[].text |
string |
Yes | What was said, with the owner’s corrections |
transcript.segments[].edited |
boolean |
Yes | Whether the owner corrected this turn |
transcript.segments[].lowConfidence |
boolean |
Yes | Whether transcription may have misheard this turn; always false on a turn the owner corrected |
transcript.text |
string |
Yes | The whole transcript as plain text, with no speakers or times |
transcript.markdown |
string |
Yes | The transcript as Markdown, one **name** [mm:ss]: text line per turn ([h:mm:ss] from one hour on) and no heading; a voice nobody named shows its label |
lobbe |
object |
Yes | The meeting’s Lobbe: the AI write-up, in its latest version |
lobbe.id |
string |
Yes | The Lobbe’s id |
lobbe.type |
"MEETING_MOM" | "LECTURE_NOTES" | "INTERVIEW_QA" | "VOICE_MEMO" | "CALL_NOTES" |
Yes | Its template, set by the meeting’s kind: MEETING_MOM, LECTURE_NOTES, INTERVIEW_QA, VOICE_MEMO or CALL_NOTES |
lobbe.language |
"en" | "fa" |
Yes | The language the Lobbe is written in: fa or en |
lobbe.promptVersion |
string |
Yes | Which prompt made it: an opaque tag that starts with the prompt’s version (e.g. v8), for comparing Lobbes, not for parsing |
lobbe.transcriptRevision |
integer | null |
Yes | The transcript revision the Lobbe was made from, so below transcript.revision means the transcript was edited since; null on Lobbes made before transcript editing (read it as 0) |
lobbe.content |
object |
Yes | The Lobbe’s structured content |
lobbe.content.version |
2 |
Yes | The content format’s version: always 2 |
lobbe.content.title |
string |
Yes | The Lobbe’s own title, 1–200 characters |
lobbe.content.sections[] |
object[] |
Yes | The sections the Lobbe is made of, at least one, in reading order; which kinds appear depends on the meeting |
lobbe.content.sections[] (kind: "summary") |
object |
When kind is "summary", the fields below |
|
lobbe.content.sections[].kind |
"summary" |
Yes | The section’s kind, which decides its other fields: summary, decision_list, action_item_list, key_point_list, qa_list, topic_notes, follow_up_list or free_text |
lobbe.content.sections[].text |
string |
Yes | The section’s prose (summary and free_text) |
lobbe.content.sections[] (kind: "decision_list") |
object |
When kind is "decision_list", the fields below |
|
lobbe.content.sections[].kind |
"decision_list" |
Yes | The section’s kind, which decides its other fields: summary, decision_list, action_item_list, key_point_list, qa_list, topic_notes, follow_up_list or free_text |
lobbe.content.sections[].items[] |
object[] |
Yes | The section’s entries (decision_list, action_item_list, key_point_list, qa_list and follow_up_list) |
lobbe.content.sections[].items[].text |
string |
Yes | The entry itself: a decision, an action, a key point or a follow-up |
lobbe.content.sections[].items[].sourceMs |
number | null |
No | Where the meeting supports the entry: the startMs of that transcript turn, in milliseconds; null when no single turn does, absent on Lobbes made before citations, and on Lobbes made before 6 September 2026 it may be the second shown to the model rather than the turn’s exact start |
lobbe.content.sections[] (kind: "action_item_list") |
object |
When kind is "action_item_list", the fields below |
|
lobbe.content.sections[].kind |
"action_item_list" |
Yes | The section’s kind, which decides its other fields: summary, decision_list, action_item_list, key_point_list, qa_list, topic_notes, follow_up_list or free_text |
lobbe.content.sections[].items[] |
object[] |
Yes | The section’s entries (decision_list, action_item_list, key_point_list, qa_list and follow_up_list) |
lobbe.content.sections[].items[].text |
string |
Yes | The entry itself: a decision, an action, a key point or a follow-up |
lobbe.content.sections[].items[].owner |
string | null |
Yes | Who takes the action, by a spoken name or a speaker label; null when the meeting does not make it clear |
lobbe.content.sections[].items[].due |
string | null |
Yes | When the action is due, as it was said (never an invented date); null when no time was said |
lobbe.content.sections[].items[].sourceMs |
number | null |
No | Where the meeting supports the entry: the startMs of that transcript turn, in milliseconds; null when no single turn does, absent on Lobbes made before citations, and on Lobbes made before 6 September 2026 it may be the second shown to the model rather than the turn’s exact start |
lobbe.content.sections[] (kind: "key_point_list") |
object |
When kind is "key_point_list", the fields below |
|
lobbe.content.sections[].kind |
"key_point_list" |
Yes | The section’s kind, which decides its other fields: summary, decision_list, action_item_list, key_point_list, qa_list, topic_notes, follow_up_list or free_text |
lobbe.content.sections[].items[] |
object[] |
Yes | The section’s entries (decision_list, action_item_list, key_point_list, qa_list and follow_up_list) |
lobbe.content.sections[].items[].text |
string |
Yes | The entry itself: a decision, an action, a key point or a follow-up |
lobbe.content.sections[] (kind: "qa_list") |
object |
When kind is "qa_list", the fields below |
|
lobbe.content.sections[].kind |
"qa_list" |
Yes | The section’s kind, which decides its other fields: summary, decision_list, action_item_list, key_point_list, qa_list, topic_notes, follow_up_list or free_text |
lobbe.content.sections[].items[] |
object[] |
Yes | The section’s entries (decision_list, action_item_list, key_point_list, qa_list and follow_up_list) |
lobbe.content.sections[].items[].question |
string |
Yes | A question asked in the meeting (qa_list) |
lobbe.content.sections[].items[].answer |
string |
Yes | The answer the meeting gave it |
lobbe.content.sections[] (kind: "topic_notes") |
object |
When kind is "topic_notes", the fields below |
|
lobbe.content.sections[].kind |
"topic_notes" |
Yes | The section’s kind, which decides its other fields: summary, decision_list, action_item_list, key_point_list, qa_list, topic_notes, follow_up_list or free_text |
lobbe.content.sections[].topics[] |
object[] |
Yes | A topic_notes section’s topics |
lobbe.content.sections[].topics[].topic |
string |
Yes | The topic |
lobbe.content.sections[].topics[].notes |
string |
Yes | What was said about it |
lobbe.content.sections[] (kind: "follow_up_list") |
object |
When kind is "follow_up_list", the fields below |
|
lobbe.content.sections[].kind |
"follow_up_list" |
Yes | The section’s kind, which decides its other fields: summary, decision_list, action_item_list, key_point_list, qa_list, topic_notes, follow_up_list or free_text |
lobbe.content.sections[].items[] |
object[] |
Yes | The section’s entries (decision_list, action_item_list, key_point_list, qa_list and follow_up_list) |
lobbe.content.sections[].items[].text |
string |
Yes | The entry itself: a decision, an action, a key point or a follow-up |
lobbe.content.sections[].items[].sourceMs |
number | null |
No | Where the meeting supports the entry: the startMs of that transcript turn, in milliseconds; null when no single turn does, absent on Lobbes made before citations, and on Lobbes made before 6 September 2026 it may be the second shown to the model rather than the turn’s exact start |
lobbe.content.sections[] (kind: "free_text") |
object |
When kind is "free_text", the fields below |
|
lobbe.content.sections[].kind |
"free_text" |
Yes | The section’s kind, which decides its other fields: summary, decision_list, action_item_list, key_point_list, qa_list, topic_notes, follow_up_list or free_text |
lobbe.content.sections[].title |
string | null |
Yes | A free_text section’s own heading; null = the default heading |
lobbe.content.sections[].text |
string |
Yes | The section’s prose (summary and free_text) |
lobbe.markdown |
string |
Yes | The Lobbe as one standalone Markdown document, headed by the meeting’s title as an H1 |
stats |
object | null |
Yes | The meeting’s stats, counted from the transcript (no AI) under the labels of transcript.speakers; this route always fills it |
stats.version |
2 |
Yes | The stats format’s version: always 2 |
stats.meetingDurationSeconds |
number |
Yes | The meeting’s length in seconds |
stats.spokenSeconds |
number |
Yes | Everyone’s speaking time added up, in seconds to one decimal |
stats.speakerCount |
integer |
Yes | How many people spoke |
stats.wordCount |
integer |
Yes | Words spoken in the meeting |
stats.turnCount |
integer |
Yes | Turns in the meeting: runs of one person speaking without another in between |
stats.questionCount |
integer |
Yes | Question marks (؟ or ?) across all turns |
stats.longestSilence |
object | null |
Yes | The longest gap between two turns, when it is at least 3 seconds; null otherwise |
stats.longestSilence.seconds |
number |
Yes | The gap’s length in seconds, to one decimal |
stats.longestSilence.afterSpeaker |
string |
Yes | Who spoke last before it: a transcript.speakers[].label |
stats.longestSilence.afterQuote |
string |
Yes | The end of that person’s line before the gap, at most 80 characters, cut at a word |
stats.speakers[] |
object[] |
Yes | One row per person, most speaking time first |
stats.speakers[].speaker |
string |
Yes | The person: a transcript.speakers[].label |
stats.speakers[].talkSeconds |
number |
Yes | Their speaking time in seconds, to one decimal |
stats.speakers[].sharePct |
number |
Yes | Their share of all speaking time, 0–100, to one decimal |
stats.speakers[].wordCount |
integer |
Yes | Words they spoke |
stats.speakers[].wordsPerMinute |
number | null |
Yes | Their pace in words per minute; null under 5 seconds of speech |
stats.speakers[].longestMonologueSeconds |
number |
Yes | Their longest turn in seconds, to one decimal |
stats.speakers[].turnCount |
integer |
Yes | How many turns they took |
stats.speakers[].avgTurnSeconds |
number |
Yes | Their average turn in seconds, to one decimal; 0 when they took no timed turn |
stats.speakers[].questionCount |
integer |
Yes | Question marks (؟ or ?) in their turns |
Errors answer { error: { code, message, details? } }, and code is the contract: see Errors.
GET /recordings/{id}/status
Section titled “GET /recordings/{id}/status”One meeting’s progress; at NAMING, the link that asks the owner to name the speakers.
| Property | Value |
|---|---|
| Scopes | recordings:read or recordings:write |
| Spends minutes | No |
| Rate limit | read: 600 requests per 10 minutes per token |
| Success | 200 |
Parameters
Section titled “Parameters”| Name | In | Type | Required | Description |
|---|---|---|---|---|
id |
path |
string |
Yes | The meeting’s id |
Response 200
Section titled “Response 200”One meeting’s progress.
| Field | Type | Always present | Description |
|---|---|---|---|
schemaVersion |
1 |
Yes | Always 1 within API v1 |
id |
string |
Yes | The meeting’s id |
status |
"QUEUED" | "TRANSCRIBING" | "NAMING" | "GENERATING" | "READY" | "FAILED" |
Yes | Where the meeting is: QUEUED, TRANSCRIBING, NAMING (waiting for the speakers’ names), GENERATING (the Lobbe is being made), then READY; or FAILED |
title |
string | null |
Yes | The meeting’s title: the one it was submitted with or its owner gave it, else its Lobbe’s once that is made; null until then |
error |
object | null |
Yes | Why the meeting failed; null unless status is FAILED |
error.code |
string |
Yes | The failure’s code, e.g. NO_SPEECH or AUDIO_CORRUPT; FAILED when there is no more specific one |
error.retryable |
boolean |
Yes | Whether retryRecording can help: false for a meeting with no speech or damaged audio |
naming |
object | null |
Yes | Naming the speakers; null unless status is NAMING |
naming.url |
string |
Yes | Send it to the owner: it opens the meeting’s naming sheet, for the owner alone, signed in |
naming.deadline |
string | null |
Yes | The earliest time Lobbe goes ahead without names (ISO 8601, UTC); null = it waits until the owner names the speakers or skips |
bundleUrl |
string | null |
Yes | The meeting’s bundle once it is READY, for a token holding recordings:read; null otherwise |
url |
string |
Yes | The meeting’s page in the web app |
Errors answer { error: { code, message, details? } }, and code is the contract: see Errors.
POST /recordings
Section titled “POST /recordings”Submit a meeting’s audio (multipart); answers 202 with its status link.
| Property | Value |
|---|---|
| Scopes | recordings:write |
| Spends minutes | Yes |
| Rate limit | submit: 30 requests per 10 minutes per token |
| Success | 202 |
This route can spend minutes: send an Idempotency-Key header, and a retry with the same key returns the first outcome instead of charging again. See Idempotency and retries.
Parameters
Section titled “Parameters”| Name | In | Type | Required | Description |
|---|---|---|---|---|
Idempotency-Key |
header |
string |
No | Recommended. Make one key (8–64 characters, a random UUID say) once per new meeting or question, and send that same key unchanged on every retry of that request; never reuse it for a different request. A replay returns the original outcome and never charges twice. Keys are the owner’s for good: they never expire. |
Request body (multipart/form-data)
Section titled “Request body (multipart/form-data)”A field not listed here is a 400.
| Field | Type | Required | Description |
|---|---|---|---|
recordedAt |
string (date-time) |
No | When the meeting happened, ISO 8601 with an offset, at most 5 minutes ahead and not before 2000; absent = now |
title |
string |
No | One line: whitespace runs collapse to a space and the ends are trimmed, then 1–120 characters |
seriesId |
string |
No | A series you own to file the meeting in; any other id files it in your personal archive, never an error |
kind |
"MEETING" | "LECTURE" | "INTERVIEW" | "VOICE_MEMO" | "CALL" |
No | What was recorded, which picks the Lobbe’s template: MEETING, LECTURE, INTERVIEW, VOICE_MEMO or CALL (default MEETING) |
outputLanguage |
"MATCH" | "FA" | "EN" |
No | The Lobbe’s language: MATCH (the meeting’s own), FA or EN (default MATCH) |
useKeyterms |
"on" | "off" |
No | on (the default) transcribes with your vocabulary, and the series’ when the meeting is filed in one, adding a 20% surcharge whenever there are words to use; off uses none |
naming |
"default" | "skip" |
No | default (the default) follows your speaker-naming setting; skip makes the Lobbe without waiting for names, unnamed voices taking Lobbe’s suggestions |
audio |
file |
Yes | The meeting’s audio file |
attachment |
file |
No | One PDF, PPTX or DOCX the Lobbe reads with the meeting |
Response 202
Section titled “Response 202”The meeting, stored; replaying the request with the same Idempotency-Key answers the same meeting.
| Field | Type | Always present | Description |
|---|---|---|---|
schemaVersion |
1 |
Yes | Always 1 within API v1 |
recording |
object |
Yes | The meeting you submitted |
recording.id |
string |
Yes | The meeting’s id |
recording.status |
"QUEUED" | "TRANSCRIBING" | "NAMING" | "GENERATING" | "READY" | "FAILED" |
Yes | Where the meeting is: QUEUED, TRANSCRIBING, NAMING (waiting for the speakers’ names), GENERATING (the Lobbe is being made), then READY; or FAILED |
recording.minutes |
integer |
Yes | What the meeting costs, in minutes: its measured length with the vocabulary surcharge, plus the attachment |
recording.recordedAt |
string |
Yes | When the meeting happened (ISO 8601, UTC): the recordedAt it was submitted with, else when Lobbe received it |
recording.statusUrl |
string |
Yes | GET it with the same token to follow the meeting (getRecordingStatus) |
recording.bundleUrl |
string | null |
Yes | The meeting’s bundle when it is already READY (a replay) and the token holds recordings:read; null otherwise |
recording.url |
string |
Yes | The meeting’s page in the web app |
Errors answer { error: { code, message, details? } }, and code is the contract: see Errors.
POST /recordings/{id}/naming
Section titled “POST /recordings/{id}/naming”Skip naming the speakers: unnamed voices take Lobbe’s suggestions and the Lobbe is made.
| Property | Value |
|---|---|
| Scopes | recordings:write |
| Spends minutes | No |
| Rate limit | submit: 30 requests per 10 minutes per token |
| Success | 200 |
Parameters
Section titled “Parameters”| Name | In | Type | Required | Description |
|---|---|---|---|---|
id |
path |
string |
Yes | The meeting’s id |
Request body (application/json)
Section titled “Request body (application/json)”The only naming action a token may take. A field not listed here is a 400.
| Field | Type | Required | Description |
|---|---|---|---|
action |
"skip" |
Yes | Always skip: make the Lobbe now, unnamed voices taking Lobbe’s suggestions |
Response 200
Section titled “Response 200”One meeting’s progress.
| Field | Type | Always present | Description |
|---|---|---|---|
schemaVersion |
1 |
Yes | Always 1 within API v1 |
id |
string |
Yes | The meeting’s id |
status |
"QUEUED" | "TRANSCRIBING" | "NAMING" | "GENERATING" | "READY" | "FAILED" |
Yes | Where the meeting is: QUEUED, TRANSCRIBING, NAMING (waiting for the speakers’ names), GENERATING (the Lobbe is being made), then READY; or FAILED |
title |
string | null |
Yes | The meeting’s title: the one it was submitted with or its owner gave it, else its Lobbe’s once that is made; null until then |
error |
object | null |
Yes | Why the meeting failed; null unless status is FAILED |
error.code |
string |
Yes | The failure’s code, e.g. NO_SPEECH or AUDIO_CORRUPT; FAILED when there is no more specific one |
error.retryable |
boolean |
Yes | Whether retryRecording can help: false for a meeting with no speech or damaged audio |
naming |
object | null |
Yes | Naming the speakers; null unless status is NAMING |
naming.url |
string |
Yes | Send it to the owner: it opens the meeting’s naming sheet, for the owner alone, signed in |
naming.deadline |
string | null |
Yes | The earliest time Lobbe goes ahead without names (ISO 8601, UTC); null = it waits until the owner names the speakers or skips |
bundleUrl |
string | null |
Yes | The meeting’s bundle once it is READY, for a token holding recordings:read; null otherwise |
url |
string |
Yes | The meeting’s page in the web app |
Errors answer { error: { code, message, details? } }, and code is the contract: see Errors.
POST /recordings/{id}/retry
Section titled “POST /recordings/{id}/retry”Retry a failed meeting: the step that failed runs again, at no new charge.
| Property | Value |
|---|---|
| Scopes | recordings:write |
| Spends minutes | No |
| Rate limit | submit: 30 requests per 10 minutes per token |
| Success | 200 |
Parameters
Section titled “Parameters”| Name | In | Type | Required | Description |
|---|---|---|---|---|
id |
path |
string |
Yes | The meeting’s id |
Response 200
Section titled “Response 200”One meeting’s progress.
| Field | Type | Always present | Description |
|---|---|---|---|
schemaVersion |
1 |
Yes | Always 1 within API v1 |
id |
string |
Yes | The meeting’s id |
status |
"QUEUED" | "TRANSCRIBING" | "NAMING" | "GENERATING" | "READY" | "FAILED" |
Yes | Where the meeting is: QUEUED, TRANSCRIBING, NAMING (waiting for the speakers’ names), GENERATING (the Lobbe is being made), then READY; or FAILED |
title |
string | null |
Yes | The meeting’s title: the one it was submitted with or its owner gave it, else its Lobbe’s once that is made; null until then |
error |
object | null |
Yes | Why the meeting failed; null unless status is FAILED |
error.code |
string |
Yes | The failure’s code, e.g. NO_SPEECH or AUDIO_CORRUPT; FAILED when there is no more specific one |
error.retryable |
boolean |
Yes | Whether retryRecording can help: false for a meeting with no speech or damaged audio |
naming |
object | null |
Yes | Naming the speakers; null unless status is NAMING |
naming.url |
string |
Yes | Send it to the owner: it opens the meeting’s naming sheet, for the owner alone, signed in |
naming.deadline |
string | null |
Yes | The earliest time Lobbe goes ahead without names (ISO 8601, UTC); null = it waits until the owner names the speakers or skips |
bundleUrl |
string | null |
Yes | The meeting’s bundle once it is READY, for a token holding recordings:read; null otherwise |
url |
string |
Yes | The meeting’s page in the web app |
Errors answer { error: { code, message, details? } }, and code is the contract: see Errors.