Errors
The shape of an error
Section titled “The shape of an error”An error answers with a JSON body like this one:
{ "error": { "code": "TOKEN_DAILY_CAP", "message": "This token's daily minute cap is reached", "details": { "capMinutes": 60, "usedMinutes": 55, "requiredMinutes": 12 } }}-
codeis the contract. Build your logic on it. -
messageis for people. Log it or show it, but never match on it: it may change. The500for an unexpected failure,INTERNAL_ERROR, carriescodealone. -
detailscomes with a few codes only, and holds numbers you can act on:Code details402 INSUFFICIENT_MINUTESrequiredMinutes, andbalanceMinutesfor a token holdingaccount:read429 TOKEN_DAILY_CAPcapMinutes,usedMinutes,requiredMinutes409 PRICE_ABOVE_MAXminutes,maxMinutes409 NOT_READY(the bundle)status, besideerrorrather than inside it -
The HTTP status alone isn’t enough. Several codes share one status: many different refusals are a
409. -
A path under
/api/that isn’t an API route answers a plain-text404 Not Found, not this JSON. Check the path against the reference.
Two exceptions, as they are
Section titled “Two exceptions, as they are”The bundle’s 409 NOT_READY
Section titled “The bundle’s 409 NOT_READY”GET /recordings/{id}/bundle on a meeting that isn’t
READY puts details beside error, not inside it:
{ "error": { "code": "NOT_READY", "message": "The meeting is not ready yet" }, "details": { "status": "TRANSCRIBING" }}This answer is older than the rule, and moving details would break the scripts that read it, so
it stays: read details.status at the top level here. It says where the meeting is: wait for
READY, unless it is FAILED, in which case see FAILED and retry.
402 INSUFFICIENT_MINUTES
Section titled “402 INSUFFICIENT_MINUTES”details.requiredMinutes is always there. details.balanceMinutes comes only to a token holding
account:read: a token without it is never told the balance. What that balance counts is in
Minutes, caps and limits.
Codes that look alike
Section titled “Codes that look alike”TOKEN_SCOPE and SCOPE_DISABLED
Section titled “TOKEN_SCOPE and SCOPE_DISABLED”Both are 403.
TOKEN_SCOPE: the token holds none of the scopes this route needs, and never will, since a token’s scopes are fixed when it is made. Don’t retry: the owner makes a token with that scope.SCOPE_DISABLED: the token holds the scope, but Lobbe has switched that kind of access off for now. The token is fine: try again later.
A token without the scope always gets TOKEN_SCOPE, switch or no switch. So SCOPE_DISABLED always
means the token has what it needs and only has to wait.
TOKEN_INVALID and DEVELOPER_MODE_OFF
Section titled “TOKEN_INVALID and DEVELOPER_MODE_OFF”TOKEN_INVALID(401): the token itself is no good. It is unknown, revoked or expired, one code for all three, and the answer never says which. Don’t retry: the owner makes a new token in Connections.DEVELOPER_MODE_OFF(403): the token is fine, but its owner has turned Developer mode off, which pauses every token of the account. Nothing is revoked: once it is back on, the same token works again. Don’t treat it as a bad token, and don’t retry it in a loop. Tell the person to turn Developer mode back on: in the app, Account, under Developers.
A revoked or expired token answers TOKEN_INVALID either way: the token is checked before the mode.
Every code
Section titled “Every code”The table lists every code, its HTTP status, what it means and what to do next. It starts with the checks every token request goes through, in the order Lobbe makes them.
The last column says what your tool does next:
- Don’t repeat it: the same request won’t work later on its own. Something has to change first, often on the person’s side.
- Wait for
Retry-After: wait the seconds it gives, then send the same request. - Change the request: the request itself is wrong, and
messageusually says how. - Try again later: the request is fine. Something on Lobbe’s side, or a switch, is in the way for now.
When you send a request again, send it with the same Idempotency-Key: see
Idempotency and retries.
| Code | HTTP status | What it means | Next step |
|---|---|---|---|
UNAUTHORIZED |
401 |
No token came with the request (a missing Authorization header, or one that isn’t Bearer lobbe_…, reads as a signed-out caller): send Authorization: Bearer lobbe_…. |
Change the request |
TOKEN_NOT_ACCEPTED |
401 |
This route doesn’t take API tokens: call only the routes in the API reference. | Change the request |
TOKENS_DISABLED |
401 |
Lobbe has switched API tokens off for everyone for now, and your token is kept: try again later. | Try again later |
TOKEN_INVALID |
401 |
The token is unknown, revoked or expired, and the answer never says which: don’t retry it; the owner makes a new token in Connections. | Don’t repeat it |
DEVELOPER_MODE_OFF |
403 |
The token is fine, but its owner has turned Developer mode off in the app’s settings (Developers), which pauses every token of the account until they turn it back on: don’t treat it as a bad or revoked token and don’t retry in a loop; tell the person to turn Developer mode on. | Don’t repeat it |
RATE_LIMITED |
429 |
Too many requests came in a short time, from this token or from your IP address: wait the seconds Retry-After gives, then send again. |
Wait for Retry-After |
TOKEN_SCOPE |
403 |
This token doesn’t hold the scope this route needs: don’t retry it; the owner makes a token that has it. | Don’t repeat it |
SCOPE_DISABLED |
403 |
The token holds the scope, but Lobbe has switched that kind of access off for now: try again later. | Try again later |
PENDING_APPROVAL |
403 |
The owner’s account is still waiting for approval, so it can’t be used yet: try again once it is approved. | Try again later |
SCOPE_UNAVAILABLE |
400 |
The app won’t make a token with a scope that is switched off for now: pick from the scopes the create screen offers, and make another token once the rest are on. | Try again later |
TOKEN_REQUIRED |
400 |
whoami describes an API token, and this request came from a signed-in browser instead: call it with Authorization: Bearer lobbe_…. |
Change the request |
VALIDATION |
400 |
The request is malformed (a field, parameter or header is missing, unknown or out of range), and message says which: fix it before sending again. |
Change the request |
NOT_FOUND |
404 |
Nothing this token can reach has that id (unknown, deleted, someone else’s or out of the token’s reach all look the same): don’t retry the same id. | Don’t repeat it |
INTERNAL |
500 |
Lobbe couldn’t finish a step on its side (storing the audio, or saving an answer): try again later, and with the same Idempotency-Key a retry is never charged twice. |
Try again later |
INTERNAL_ERROR |
500 |
Something unexpected failed on Lobbe’s side: try again later, and with the same Idempotency-Key a retry is never charged twice. |
Try again later |
INVALID_AUDIO |
400 |
The file isn’t audio Lobbe can read: send it in another format, or from another export. | Change the request |
AUDIO_INCOMPLETE |
400 |
The audio file is cut short (an MP4-family file whose end is missing, usually a copy or upload that stopped early): send the complete file. | Change the request |
UPLOAD_TOO_LARGE |
413 |
The file is larger than the upload limit, which whoami gives as limits.maxUploadMb: send a smaller file. |
Change the request |
RECORDING_TOO_LONG |
413 |
The audio is longer than one meeting may be, which whoami gives as limits.maxRecordingMinutes: split it and send the parts. |
Change the request |
VIDEO_UPLOAD_DISABLED |
409 |
Video upload is switched off: send the meeting’s audio instead. | Change the request |
VIDEO_UNAVAILABLE |
503 |
Lobbe can’t take the audio out of a video right now: try again later, or send the audio itself. | Try again later |
VIDEO_NO_AUDIO |
400 |
The video has no audio track, so there is nothing to transcribe: send a file with sound. | Change the request |
VIDEO_UNREADABLE |
400 |
The video can’t be read: send it in another format, or send its audio. | Change the request |
ATTACHMENT_TYPE |
400 |
The attachment isn’t a PDF, PPTX or DOCX file: send one of those, or no attachment. | Change the request |
ATTACHMENT_TOO_LARGE |
413 |
The attachment is larger than 10 MB: send a smaller file. | Change the request |
DAILY_LIMIT_REACHED |
429 |
The owner has reached Lobbe’s own daily limit (on meetings, or on Asks over the last 24 hours), which no token raises: try again later, after Retry-After when it is sent. |
Try again later |
INSUFFICIENT_MINUTES |
402 |
The owner’s balance is short of what this costs: details.requiredMinutes says how many minutes it needs, plus details.balanceMinutes only for a token holding account:read; don’t retry until the owner buys minutes. |
Don’t repeat it |
TOKEN_DAILY_CAP |
429 |
This would take the token past its daily minute cap (details gives capMinutes, usedMinutes and requiredMinutes): wait for Retry-After; when it is absent, waiting can’t help and the owner raises the cap in Connections. |
Wait for Retry-After |
IDEMPOTENCY_KEY_IN_USE |
409 |
Another token of this account, or the app, already used this Idempotency-Key, and a key is the owner’s for good (across the account for a meeting, on that meeting or series for an Ask): send a new random key, never a hash of the file. |
Change the request |
UPLOAD_IN_PROGRESS |
503 |
An earlier request with this Idempotency-Key is still storing its audio: wait for Retry-After and send the same request again, which then answers with that meeting. |
Wait for Retry-After |
NOT_NAMING |
409 |
The meeting isn’t waiting at the speaker-naming step (it is already past it, or hasn’t reached it): read its status instead of skipping again. | Don’t repeat it |
NOT_FAILED |
409 |
Only a failed meeting can be retried, and this one hasn’t failed: read its status instead. | Don’t repeat it |
NOT_READY |
409 |
The meeting isn’t ready yet; on the bundle, details sits beside error, not inside it, and details.status says where the meeting is: poll its status and read the bundle once it is READY. |
Try again later |
ASK_DISABLED |
409 |
Ask is switched off in Lobbe for now: try again later. | Try again later |
ASK_NOT_READY |
409 |
The meeting has no Lobbe yet, so there is nothing to ask: wait until its status is READY. |
Try again later |
PRICE_ABOVE_MAX |
409 |
The answer would cost more than your maxMinutes (details gives minutes and maxMinutes), and nothing was charged: raise maxMinutes, or don’t ask. |
Change the request |
SERIES_ASK_TOO_FEW |
409 |
A series needs at least 2 finished meetings to ask across: try again once it has them. | Try again later |
GENERATE_FAILED |
502 |
The model couldn’t answer this time, and nothing was charged: try again later with the same Idempotency-Key. |
Try again later |
SERIES_LIMIT |
409 |
The owner already has 50 series, the most one account may have: don’t retry; the owner deletes one in the app first. | Don’t repeat it |
SERIES_NAME_TAKEN |
409 |
The owner already has a series with this name (capitals don’t make a name different): choose another name. | Change the request |
MEMORY_DISABLED |
409 |
Memory is switched off in Lobbe, so a note can’t be edited or deleted (reading answers an empty list): try again later. | Try again later |
MEMORY_DUPLICATE |
409 |
The owner already has this note in the same category: edit or delete that one instead. | Change the request |
CURSOR_EXPIRED |
410 |
The cursor is older than the 30 days the feed keeps, or comes from before a restore of Lobbe’s data: take a new cursor with after=latest first, then catch up from listReadyRecordings, then read on from that new cursor. |
Change the request |