DocsUpdated 2026-10-27View as Markdown

Errors

Every /api error has the same JSON shape and a matching HTTP status. Match on code: codes are stable and are the closed list on this page. type groups them for coarse handling. hint says what to do next, written for an agent to act on. request_id is also in the Request-Id header of every response; quote it when reporting a problem.

{
  "error": {
    "type": "invalid_request_error",
    "code": "INVALID_VISIBILITY",
    "message": "visibility must be one of: workspace, link, public",
    "param": "visibility",
    "hint": "visibility must be 'workspace' (default), 'link' or 'public'. Only use 'public' if the user asked to share outside the company.",
    "doc_url": "https://ollav.app/docs/errors#INVALID_VISIBILITY",
    "request_id": "req_6f1c2a9b0d4e8f7a1b2c3d4e"
  }
}
FieldMeaning
typeOne of the 7 types below
codeWhat went wrong, machine-readable and stable
messageWhat went wrong, for people and models
paramThe field or header at fault, when there is one
hintWhat to do next
doc_urlThis page, at the code's anchor
request_idThe request, for support (also the Request-Id header)
docsOlder link to the agent quickstart's error section, kept for existing clients

Plan-limit errors (402) also carry limit and plan. MCP tool errors carry the same code and doc_url in the tool result.

Retrying safely

Send an Idempotency-Key header (any unique string, e.g. a UUID) on every POST, PATCH and DELETE. If a request times out or fails with an api_error or rate_limit_error, retry it with the same key and body: it runs at most once, and a retry of a finished request returns the first response with Idempotent-Replayed: true. An error response doesn't use the key up: fix the request, or wait, and retry with the same key. Keys last 24 hours. Don't retry invalid_request_error, authentication_error, permission_error or not_found_error unchanged: they fail the same way again.

Types

TypeMeaning
invalid_request_errorThe request is wrong: a missing or invalid field, header or body. Fix the request; retrying it unchanged fails again.
authentication_errorNo valid credential. Check the API key.
permission_errorThe caller may not do this: a scope, role, plan limit or a browser-session-only action. Tell the user; don't retry.
not_found_errorNo such resource for this caller. Missing and forbidden look the same, on purpose.
rate_limit_errorToo many requests. Wait, then retry once.
idempotency_errorAn Idempotency-Key problem: invalid, reused for a different request, or still in use.
api_errorSomething failed on Ollav's side. Retry once with the same Idempotency-Key; quote request_id if it persists.

invalid_request_error

The request is wrong: a missing or invalid field, header or body. Fix the request; retrying it unchanged fails again.

CSRF_REQUIRED

HTTP 403 · param X-Ollav-Client

Cause. A state-changing request (POST, PATCH, DELETE) arrived without the X-Ollav-Client header.

Fix. Add the header 'X-Ollav-Client: agent' to every POST, PATCH and DELETE request, then retry.

LOGIN_CODE_INVALID

HTTP 400 · param code

Cause. The sign-in code is wrong, expired (codes last 15 minutes), already used, replaced by a newer email, or spent by 5 wrong tries. These look the same on purpose.

Fix. Check the code in the latest sign-in email. If it still fails, send a new code from https://app.ollav.app/signin.

BAD_JSON

HTTP 400

Cause. The body isn't valid JSON.

Fix. The request body isn't valid JSON. Send Content-Type: application/json with a JSON object.

BAD_REQUEST

HTTP 400

Cause. The request is malformed; the message says how.

Fix. The request is malformed. Check the body against https://ollav.app/openapi.json.

VALIDATION_ERROR

HTTP 400

Cause. A field has an invalid value; the message and error.param name it.

Fix. A field has an invalid value; the message names it. Valid shapes are in https://ollav.app/openapi.json.

MISSING_HTML

HTTP 400 · param html

Cause. A JSON upload has no 'html' field.

Fix. JSON uploads need an 'html' string field: {"html": "<!DOCTYPE html>..."}. For files on disk use -F file=@page.html instead.

MISSING_FILE

HTTP 400 · param file

Cause. A multipart upload has no 'file' field.

Fix. Multipart uploads need the file in a field named 'file': -F file=@page.html.

MISSING_SLUG

HTTP 400 · param slug

Cause. The document slug is missing.

Fix. Pass the document slug as ?slug=... (from the upload response or list_documents).

EMPTY_BODY

HTTP 400 · param html

Cause. The HTML is empty.

Fix. The HTML was empty. Send the file with -F file=@page.html, a JSON body {"html": "..."}, or a text/html body.

EMPTY

HTTP 400 · param file

Cause. The uploaded file is empty.

Fix. The upload was empty. Send a non-empty file.

NOT_HTML

HTTP 400 · param html

Cause. The content doesn't look like HTML.

Fix. Send an HTML document (it should contain tags such as <html>, <body> or <div>). Upload a .html file with -F file=@page.html, a JSON body {"html": "..."}, or a text/html body.

INVALID_VISIBILITY

HTTP 400 · param visibility

Cause. visibility isn't workspace, link or public.

Fix. visibility must be 'workspace' (default), 'link' or 'public'. Only use 'public' if the user asked to share outside the company.

INVALID_EMAIL

HTTP 400 · param email

Cause. The email address isn't valid.

Fix. Send a valid email address.

UNSUPPORTED_TYPE

HTTP 400 · param file

Cause. The image isn't PNG, JPEG, WebP or GIF (checked by content; SVG is refused).

Fix. Comment images must be PNG, JPEG, WebP or GIF (SVG is refused), up to 5 MB.

UNSUPPORTED_CONTENT_TYPE

HTTP 400 · param Content-Type

Cause. The upload's Content-Type isn't multipart/form-data, application/json or text/html.

Fix. Use multipart/form-data (-F file=@page.html), application/json ({"html": ...}) or text/html.

TOO_LARGE

HTTP 413

Cause. Over the size limit: 15 MB per HTML document, 5 MB per image.

Fix. Ollav hosts one HTML file up to 15 MB. Shrink it: convert inlined PNG screenshots to WebP, downscale images wider than about 2000px, or load large media from absolute https:// URLs. Then upload again.

TOO_MANY_FILES

HTTP 400 · param file

Cause. A multipart upload had more than one file. Each upload is one self-contained HTML file.

Fix. Send exactly one file in the 'file' field (-F file=@page.html). Inline the other assets into the HTML, or upload each page as its own document.

TOO_MANY_REACTIONS

HTTP 400 · param reaction

Cause. The comment already has 20 different reactions.

Fix. React with an emoji the comment already has, or leave it.

CONFIRMATION_REQUIRED

HTTP 400 · param confirm

Cause. A deletion wasn't confirmed by typing the name.

Fix. Deletions need a signed-in session in the web UI and typing the name to confirm; an API key can't do them.

CONFLICT

HTTP 409

Cause. The request clashes with the current state; the message says how (e.g. the comment already has an image).

Fix. The thing already exists or the request clashes with the current state; the message says which. Don't retry unchanged.

SOLE_OWNER

HTTP 409

Cause. The account is the only owner of a workspace other people use.

Fix. Transfer ownership first (POST /api/workspaces/{id}/transfer in the web UI), then delete the account.

CONSUMER_DOMAIN

HTTP 400 · param domain

Cause. Public email providers (gmail.com and the like) can't be claimed as a workspace domain.

Fix. Claim your organisation's own domain instead.

TOO_MANY_MEMBERS

HTTP 409

Cause. The chosen plan is for fewer members than the workspace has.

Fix. Choose a plan that fits the workspace's members (Team is billed per member).

ALREADY_SUBSCRIBED

HTTP 409

Cause. The workspace already has a subscription.

Fix. Change plans from Manage billing in the web UI instead of starting a new checkout.

NO_SUBSCRIPTION

HTTP 409

Cause. The workspace has no billing account yet.

Fix. Upgrade first; the billing portal exists once there is a subscription.

METHOD_NOT_ALLOWED

HTTP 405

Cause. Wrong HTTP method for this path.

Fix. Wrong HTTP method for this path. See https://ollav.app/openapi.json.

INVALID_API_VERSION

HTTP 400 · param Ollav-Version

Cause. The Ollav-Version header names no API version.

Fix. Send one of the versions listed at https://ollav.app/docs/changelog in Ollav-Version, or leave the header out to use your key's pinned version.

authentication_error

No valid credential. Check the API key.

AUTH_REQUIRED

HTTP 401 · param Authorization

Cause. No valid credential: the API key is missing, wrong or revoked, or its user left the key's workspace.

Fix. Send the API key as 'Authorization: Bearer ol_live_...' (or ol_test_...). If you did, the key is wrong, revoked (rotating a key revokes the old one), or its user left the key's workspace: ask the user for a new key from https://app.ollav.app/keys.

ONBOARDING_EXPIRED

HTTP 401

Cause. The sign-up step timed out (it lasts 30 minutes).

Fix. Sign in again at https://app.ollav.app/signin to finish setting up the account.

SIGNATURE_INVALID

HTTP 400

Cause. A Stripe webhook's signature didn't verify.

Fix. Only Stripe calls this endpoint. Check STRIPE_WEBHOOK_SECRET matches the endpoint in the Stripe dashboard.

permission_error

The caller may not do this: a scope, role, plan limit or a browser-session-only action. Tell the user; don't retry.

SCOPE_REQUIRED

HTTP 403

Cause. The API key doesn't have the scope this endpoint needs (upload, read or comment).

Fix. This API key lacks a required scope. Ask the user to create a key with upload, read and comment at https://app.ollav.app/keys.

ORIGIN_FORBIDDEN

HTTP 403 · param Origin

Cause. A browser sent the request from a foreign Origin.

Fix. Server-side clients should send no Origin header (curl and agents don't by default). Browsers may only call the API from https://app.ollav.app.

ROLE_REQUIRED

HTTP 403

Cause. The caller's role in the workspace (owner > admin > member > guest) doesn't allow this.

Fix. The user's role in this workspace doesn't allow this. Tell the user rather than retrying; a workspace admin can change roles.

FORBIDDEN

HTTP 403

Cause. This caller may not do this; the message says why (often: it needs a signed-in browser session, not an API key).

Fix. This caller isn't allowed to do that; the message says why. Tell the user rather than retrying.

NOT_ALLOWED

HTTP 403

Cause. Sign-up is invite-only for this address.

Fix. Ask an existing Ollav member to invite this email address.

NO_WORKSPACE

HTTP 409

Cause. The account isn't a member of any workspace yet.

Fix. This account has no current workspace. The user should sign in at https://app.ollav.app once to finish setup.

TERMS_REQUIRED

HTTP 400

Cause. The current Terms and Privacy Policy haven't been accepted.

Fix. The user must accept the current terms by signing in at https://app.ollav.app.

PLAN_LIMIT

HTTP 402

Cause. The workspace's plan doesn't allow this: a document, member, storage or daily API-call limit. error.limit says which.

Fix. This workspace has reached a limit of its plan; the message and 'limit' say which. Tell the user and don't retry.

PLAN_READ_ONLY

HTTP 402

Cause. The workspace is over its plan's limits, so it is read-only until it upgrades or makes room.

Fix. This workspace is over its plan's limits and is read-only: reading still works, adding doesn't. Tell the user and don't retry.

PLAN_REQUIRED

HTTP 403

Cause. The feature (e.g. domain auto-join) isn't on the workspace's plan.

Fix. Tell the user this needs a plan that includes it; see https://app.ollav.app/pricing.

ACCOUNT_SUSPENDED

HTTP 403

Cause. The account was suspended by Ollav for breaking the Terms of Service: sign-in, its API keys and MCP connections are all refused.

Fix. This account is suspended. Stop and tell the user; retrying or using another of its keys won't work.

not_found_error

No such resource for this caller. Missing and forbidden look the same, on purpose.

NOT_FOUND

HTTP 404

Cause. No such resource for this caller. Missing, forbidden and other-workspace resources all look the same.

Fix. Check the slug or id. Slugs come from the upload response or list_documents (GET /api/dashboard). An API key only sees its own workspace, so documents elsewhere also return 404, and test and live are separate: an ol_test_ key sees only its own test documents (deleted 30 days after upload), a live key never sees them.

ARCHIVED

HTTP 404

Cause. The document is archived (manually or because it expired).

Fix. The document is archived. Restore it with POST /api/documents/{slug}/unarchive (upload scope), or share a new one.

VERSION_NOT_FOUND

HTTP 404

Cause. The version id doesn't belong to this document.

Fix. That version id doesn't belong to this document. List versions with GET /api/documents/{slug}/versions.

DOCUMENT_REMOVED

HTTP 410

Cause. The document was taken down by Ollav for breaking the Terms of Service. Its page answers 410 and it takes no new versions.

Fix. This document was taken down and can't be published to or restored through the API. Tell the user; don't retry.

NO_VERSION

HTTP 404

Cause. The document has no current version.

Fix. The document has no current version. Publish one with POST /api/documents/{slug}/versions.

rate_limit_error

Too many requests. Wait, then retry once.

RATE_LIMITED

HTTP 429

Cause. Too many requests of this kind in the window: more than 30 publishes or 120 comments an hour per account (looser limits apply per IP address), more than 10 abuse reports an hour per IP, too many exports, or more than 60 requests a minute with one test (ol_test_) key.

Fix. Too many requests. Wait error.retry_after seconds (also the Retry-After header; the message says it in words), then retry once with the same Idempotency-Key; don't retry in a loop. A test key allows 60 requests a minute.

TOO_MANY_REQUESTS

HTTP 429

Cause. Too many requests of this kind in a short window.

Fix. Too many requests. Wait before retrying and don't retry in a loop.

SIGNIN_RATE_LIMITED

HTTP 429

Cause. Too many sign-in emails or codes: more than 5 emails to one address, 20 emails from one network, or 30 code tries from one network in an hour.

Fix. Wait an hour before asking for another sign-in email (Retry-After says how long, in seconds), or sign in with Google.

idempotency_error

An Idempotency-Key problem: invalid, reused for a different request, or still in use.

IDEMPOTENCY_KEY_INVALID

HTTP 400 · param Idempotency-Key

Cause. The Idempotency-Key header is empty, longer than 255 characters, or has characters outside printable ASCII.

Fix. Send 1-255 printable ASCII characters in Idempotency-Key, e.g. a UUID: Idempotency-Key: 5f0c3b1e-....

IDEMPOTENCY_KEY_REUSED

HTTP 409 · param Idempotency-Key

Cause. This Idempotency-Key was already used in the last 24 hours with a different method, path or body.

Fix. Use a new Idempotency-Key for a new request. Reuse a key only to retry the identical request.

IDEMPOTENCY_KEY_IN_USE

HTTP 409 · param Idempotency-Key

Cause. A request with this Idempotency-Key is still being processed.

Fix. Wait a second and retry with the same key and body; you will get the first request's result.

api_error

Something failed on Ollav's side. Retry once with the same Idempotency-Key; quote request_id if it persists.

R2_MISSING

HTTP 404

Cause. The document's stored file is missing from storage.

Fix. Publish the document again (POST /api/documents/{slug}/versions). If it keeps happening, tell the user and include the request id.

INTERNAL_ERROR

HTTP 500

Cause. Something went wrong on Ollav's side.

Fix. Retry once after a few seconds with the same Idempotency-Key. If it keeps failing, tell the user and include error.request_id.

PARSE_ERROR

HTTP 502

Cause. The MCP server got a non-JSON response from the REST layer.

Fix. Retry once. If it keeps failing, tell the user and include the request id.

PROCESSING_FAILED

HTTP 500

Cause. A Stripe webhook event couldn't be processed; Stripe retries it.

Fix. Nothing to do: Stripe retries the event.

STRIPE_ERROR

HTTP 502

Cause. The payment provider couldn't be reached.

Fix. Try again in a minute.

BILLING_CANCEL_FAILED

HTTP 502

Cause. The subscription couldn't be cancelled before deleting, so nothing was deleted.

Fix. Try again in a minute; if it persists, cancel the plan from Manage billing first.

BILLING_NOT_CONFIGURED

HTTP 503

Cause. Billing isn't set up on this server.

Fix. Nothing to buy here: this server has no plans or limits.

EMAIL_SEND_FAILED

HTTP 502

Cause. The sign-in email couldn't be sent: the email provider refused it or couldn't be reached.

Fix. Try again in a minute, or sign in with Google.

EMAIL_NOT_CONFIGURED

HTTP 503

Cause. Email sign-in isn't set up on this server.

Fix. Sign in with Google instead.

GOOGLE_NOT_CONFIGURED

HTTP 404

Cause. Google sign-in isn't set up on this server.

Fix. Use another sign-in method.