Bavlio is honest about its current state. Three error envelope shapes coexist today (a unification migration is parked as a follow-up). Idempotency-Key middleware is not yet implemented. Here’s how to deal with both.Documentation Index
Fetch the complete documentation index at: https://docs.bavlio.com/llms.txt
Use this file to discover all available pages before exploring further.
Error envelope shapes
Match on HTTP status code first; treat the body as supplementary detail. Newer endpoints conform to ADR-010 (flat shape with stable code); older endpoints still emit FastAPI defaults; validation failures emit a Pydantic-shaped envelope.EpicHTTPError (flat)
Used by newer endpoints. Thedetail field is a stable machine-readable code; message is a human-readable explanation.
Example
/api/v1/personalize, /api/v1/personalize/preview, /api/v1/email-finder/* (most routes).
Legacy HTTPException
Older FastAPI default. Thedetail field is a human-readable string; no separate code field. Treat detail as opaque text — match on status code and endpoint, not on detail content.
Example
Pydantic request validation
Returned automatically when request bodies fail Pydantic validation. Thedetails array contains structured per-field errors; path is the request path; message is constant.
Example
Status codes & retry rules
| Status | Meaning | Retry? |
|---|---|---|
| 200 | OK | n/a |
| 201 | Created | n/a |
| 202 | Accepted (async work queued) | n/a |
| 400 | Bad request — fix the body / params | no |
| 401 | Authentication failed | no (re-auth) |
| 403 | Authenticated but not allowed | no |
| 404 | Not found | no |
| 409 | Conflict (duplicate, state change race) | maybe (after read) |
| 422 | Validation failed | no |
| 429 | Rate limited | yes (after Retry-After) |
| 500 | Server error | yes (exponential backoff) |
| 502 | Bad gateway | yes |
| 503 | Service unavailable | yes |
| 504 | Gateway timeout | yes |
Retry-After, retry 5xx with exponential backoff (max 5 attempts), surface everything else immediately.
Retry pattern
Drop-in helper that respectsRetry-After and backs off exponentially.
Python
Idempotency
Workaround until the middleware ships: after a failed mutation, follow up with a read before retrying. For example, after a failedPOST /api/v1/campaigns/, call GET /api/v1/campaigns/?name=<your-name> to check whether your campaign was already created. Match on a stable client-side correlation field (campaign name, lead-set hash, etc.).
Roadmap: a FastAPI middleware that accepts Idempotency-Key on POST/PUT/PATCH/DELETE, hashes the request, stores the response in Redis for 24 hours, and returns the cached response on retry.
FAQ
How do I tell if I should retry?
How do I tell if I should retry?
Retry on 429 (after waiting
Retry-After seconds) and on 5xx server errors with exponential backoff. Do NOT retry on 4xx (other than 429) — fix the request and try once. Check error.detail or error.message for the specific cause.What is the error envelope shape?
What is the error envelope shape?
Three envelopes coexist today. Newer endpoints use a flat shape:
{ detail: <code>, message: <text> } per ADR-010 (EpicHTTPError). Older endpoints use the FastAPI default: { detail: <text> }. Pydantic validation failures return { error, details, path, message }. Match on status code first.What is the difference between 401 and 403?
What is the difference between 401 and 403?
401 means the API key is missing, malformed, or revoked. Re-authenticate. 403 means the key is valid but lacks permission for the operation — for example, attempting to mint a new API key with an API key (only Supabase JWTs can mint keys).