API reference

Error codes

2 min read

The API uses standard HTTP status codes. The body always carries { success: false, error: "...", details? }.

2xx — success

| Code | Meaning | |------|---------| | 200 | Read succeeded; body is the resource. | | 201 | Resource created (e.g. bookmark added). Idempotent operations also return 201 even when no row was inserted. | | 202 | Job accepted; poll the returned jobId for completion. |

4xx — client errors

| Code | Meaning | What to do | |------|---------|------------| | 400 | Validation failed | Look at details.fieldErrors; fix the input shape. | | 401 | Unauthenticated | Sign in or pass Authorization: Bearer .... | | 403 | Authenticated but not allowed | The action requires a higher tier (Pro/Team) or a different scope. | | 404 | Not found, or you don't own it | We return 404 for owner-only resources you don't own — same response as missing resources to avoid leaking existence. | | 409 | Conflict | E.g. trying to create a resource that already exists with a different shape. Rare. | | 429 | Rate limit hit | Read Retry-After; wait; retry. See Rate limits. |

5xx — server errors

| Code | Meaning | What to do | |------|---------|------------| | 500 | Internal server error | Retry once; if it persists, file an issue with the request id from X-Request-Id. | | 503 | Service temporarily unavailable | Often a Redis or DB blip. Exponential backoff up to 5 min, then file an issue. |

Validation error shape

POST /api/jobs with a malformed body:

HTTP/1.1 400 Bad Request
 
{
  "success": false,
  "error": "Invalid job payload",
  "details": {
    "formErrors": [],
    "fieldErrors": {
      "input.url": ["Invalid URL"]
    }
  }
}

details.fieldErrors is keyed by the dotted path so you can route the error back to the right form field.

Idempotency

Mutating endpoints (POST /api/bookmarks/:slug, POST /api/library/:slug/reviews) are idempotent — repeating the same request with the same body produces the same final state. Safe to retry on network failure.