Skip to main content
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.

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. The detail field is a stable machine-readable code; message is a human-readable explanation.
Example
Seen on: /api/v1/personalize, /api/v1/personalize/preview, /api/v1/email-finder/* (most routes).

Legacy HTTPException

Older FastAPI default. The detail 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
Seen on: older endpoints that have not yet migrated to EpicHTTPError.

Pydantic request validation

Returned automatically when request bodies fail Pydantic validation. The details array contains structured per-field errors; path is the request path; message is constant.
Example
Seen on: any endpoint with a Pydantic request body.

Status codes & retry rules

A safe default agent: retry 429 honoring Retry-After, retry 5xx with exponential backoff (max 5 attempts), surface everything else immediately.

Retry pattern

Drop-in helper that respects Retry-After and backs off exponentially.
Python

Idempotency

The Idempotency-Key header is not yet honored by Bavlio. Sending it has no effect server-side. A retried POST after a network failure will create a duplicate.
Workaround until the middleware ships: after a failed mutation, follow up with a read before retrying. For example, after a failed POST /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

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.
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.
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).