Reference

Errors

HTTP status codes, stable error codes, and the per-surface response body shapes.

Every error carries the appropriate HTTP status. Branch on the HTTP status first, then treat the stable string code as the machine signal. Do not string-match human-readable messages — they can change without notice.

HTTP status codes

StatusMeaning
400Validation error — a field is missing, malformed, or has an unknown value.
401Authentication failed — missing or invalid API key.
402Insufficient credits — the tenant balance is exhausted.
403Feature not enabled — the tenant is not entitled to the module or tool you called.
404Not found — the resource does not exist or is not visible to this tenant.
409Duplicate — the resource already exists (for example, a conflicting idempotent create).
429Rate limited — too many requests; back off and retry. See rate limits.

Note

403 and 404 deliberately look similar from the outside. A resource that belongs to another tenant returns 404, not 403 — the API never confirms that a resource exists for a tenant it doesn't belong to. 403 is reserved for your own tenant hitting a feature it isn't entitled to run (see standalone tools).

Stable error codes

These string codes are stable machine signals. Match on them rather than on message text, since message copy can be reworded at any time:

CodeTypical statusMeaning
insufficient_credits402The tenant has no credits left to create a session or run a tool.
feature_not_enabled403The tenant's plan/config doesn't include this module or standalone tool.
not_found404The requested resource does not exist for this tenant.
invalid_status400An update-status call passed a value that is not a valid session status.
type is immutable400A Lists API PATCH tried to change a list's type after creation.

Note

A unified { "error": { "code", "message" } } envelope across all surfaces is planned. Until it ships, the body shape depends on which surface you hit — covered below. Branching on HTTP status keeps your handling portable across the transition.

Per-surface body shapes

The JSON body currently differs by surface.

Success is a bare object or array. Errors are a single detail string carrying the code or message:

{ "detail": "insufficient_credits" }
{ "detail": "not_found" }
{ "detail": "feature_not_enabled" }

Handling patterns

Edge cases worth handling explicitly

Example: robust create handling

const res = await fetch(`${BASE}/v3/session/`, {
  method: "POST",
  headers: { "x-api-key": KEY, "content-type": "application/json",
             "idempotency-key": key },
  body: JSON.stringify(body),
});

switch (res.status) {
  case 201: return res.json();                       // ok (or idempotent replay)
  case 402: throw new TemporaryOutage("insufficient_credits");
  case 403: throw new ConfigError((await res.json()).detail);
  case 429: return retryWithBackoff();                // honour the bucket
  case 400: throw new BadRequest(await res.json());   // fix the request
  default:  throw new Error(`obsidian ${res.status}: ${await res.text()}`);
}
import requests

resp = requests.post(
    f"{BASE}/v3/session/",
    headers={"x-api-key": KEY, "Idempotency-Key": key},
    json=body,
    timeout=15,
)

if resp.status_code == 201:
    session = resp.json()
elif resp.status_code == 402:
    raise TemporaryOutage("insufficient_credits")
elif resp.status_code == 403:
    raise ConfigError(resp.json().get("detail"))
elif resp.status_code == 429:
    session = with_backoff(lambda: requests.post(...))
elif resp.status_code == 400:
    raise BadRequest(resp.json())
else:
    resp.raise_for_status()
resp, err := client.Do(req)
if err != nil {
    return nil, err
}
defer resp.Body.Close()

switch resp.StatusCode {
case 201:
    return decodeSession(resp.Body)
case 402:
    return nil, ErrInsufficientCredits
case 403:
    return nil, ErrFeatureNotEnabled
case 429:
    return withBackoff(createSession)
case 400:
    return nil, decodeValidationError(resp.Body)
default:
    return nil, fmt.Errorf("obsidian %d", resp.StatusCode)
}

Warning

Never build a code path that treats an unrecognised status as success. New status codes can be introduced for new failure modes; a default branch that raises (as above) is safer than one that falls through to "ok."

FAQ