Idempotency and retries
A network can lose the answer to a request that worked. Sending it again must not make a second
meeting or pay for a second Ask. An Idempotency-Key header makes the second try safe.
What takes a key
Section titled “What takes a key”The three routes that spend minutes take an Idempotency-Key header:
| Route | Scope | A replay answers |
|---|---|---|
POST /recordings |
recordings:write |
202 with the same meeting |
POST /recordings/{id}/ask |
recordings:query |
200 with the same answer |
POST /series/{id}/ask |
series:query |
200 with the same answer |
- A key is 8 to 64 characters, counted after spaces at either end are trimmed. Anything else answers
400 VALIDATION. - It is optional, and worth sending on every request to these routes: without one, Lobbe can’t tell a retry from a new request.
- On the two Asks, the key can also go in the body as
idempotencyKey. When both are sent, the header wins. - The other routes spend nothing, so they take no key.
key=$(uuidgen) # one key for this meeting, kept for every retry of itcurl https://app.lobbe.ai/api/v1/recordings \ -H "Authorization: Bearer $LOBBE_TOKEN" \ -H "Idempotency-Key: $key" \ -F "audio=@meeting.m4a;type=audio/mp4"Replays
Section titled “Replays”Send a request again with the same key, and Lobbe doesn’t run it again: it answers with what the first one made.
- A meeting:
202with the same meeting, itsidand its status now. There is no second meeting and no second charge, and the token’s daily cap counts it once. - An Ask:
200with the first answer, and nothing is charged. - A replay is answered even when a limit is reached: Lobbe’s daily limits, or the token’s daily cap.
- Lobbe matches on the key alone. The same key with another file, or another question, still gets the first meeting or the first answer back, and the new one is never sent. A new request needs a new key.
- Only a request that worked is replayed. One that answered an error stored nothing, so sending
it again with the same key runs it for real. That is what you want after a
5xx, or after a402once the owner has bought minutes. 503 UPLOAD_IN_PROGRESSmeans the first request with this key is still storing its audio. Wait the secondsRetry-Aftergives and send the same request again: it then answers with that meeting.
A key is the owner’s, and doesn’t expire
Section titled “A key is the owner’s, and doesn’t expire”- A key doesn’t expire after 24 hours, or a week. A meeting’s key stays with the meeting it made for the life of that meeting, even after it is deleted. An Ask’s key stays with the Asks of that meeting or series.
- A meeting’s key is the account’s. One key makes one meeting, across the app and every token of the account. An Ask’s key belongs to one meeting or one series, so another meeting can use the same key.
- Each token replays only its own requests. A key that another token of the account, or the app,
already used answers
409 IDEMPOTENCY_KEY_IN_USE. The answer names no meeting and no answer: the earlier one never reaches this token.
So send a random UUID for each new request. Never make the key from the file, a hash of the audio
for example: two agents of one account that hash the same file would collide, and the second would
get 409 IDEMPOTENCY_KEY_IN_USE instead of its meeting.
Retrying
Section titled “Retrying”- Make the key once, before the first try, and keep it for every retry of that request. If your tool can restart, store the key with the job.
- Network errors and
5xx: send the same request again with the same key, waiting longer each time (2 seconds, then 4, 8 and so on, up to a limit you choose). If the first try had in fact worked, the retry gets its outcome. 429: wait the secondsRetry-Aftergives, then send the same request with the same key. For a daily limit, that can be hours. A429withoutRetry-Afterwon’t pass by waiting: that is aTOKEN_DAILY_CAPthis request alone is bigger than, or Lobbe’s ownDAILY_LIMIT_REACHEDon a meeting. Stop, and tell the person.- An Ask that fails (
502 GENERATE_FAILED) isn’t charged, but it can count toward Lobbe’s daily limit on Asks: wait before trying again, and don’t loop on it. - Other
4xx: the request as it is won’t work. Change it as its code says: see Errors.409 IDEMPOTENCY_KEY_IN_USEneeds a new key.
A submit that does all of this (it needs uuidgen; any random UUID will do):
key=$(uuidgen)delay=2for attempt in 1 2 3 4 5 6; do rm -f headers.txt body.json code=$(curl -sS -o body.json -D headers.txt -w '%{http_code}' \ https://app.lobbe.ai/api/v1/recordings \ -H "Authorization: Bearer $LOBBE_TOKEN" \ -H "Idempotency-Key: $key" \ -F "audio=@meeting.m4a;type=audio/mp4") || code=000 wait=$(awk -F': *' 'tolower($1) == "retry-after" { print $2 + 0 }' headers.txt 2>/dev/null) case "$code" in 202) cat body.json; break ;; 429) [ -n "$wait" ] || { cat body.json; break; }; sleep "$wait" ;; 5?? | 000) sleep "${wait:-$delay}"; delay=$((delay * 2)) ;; *) cat body.json; break ;; esac [ "$attempt" = 6 ] && { echo "gave up after 6 tries"; cat body.json 2>/dev/null; }done000 is a network error: curl got no answer. The loop sends the same key every time, so however
many tries it takes, there is one meeting.