API documentation

Let an agent organize your Litlas library

Create and remove boards, manage papers on a board, and read or update paper notes through an account-scoped API.

Quick start

All endpoints use JSON and are rooted at the following base path.

API base path/api/v1/agent

In an interactive Bash shell, keep the one-time key in a non-exported variable and pass it to curl only through a private configuration file descriptor. This prevents the key from entering shell history, child-process environments, or curl arguments:

unset LITLAS_API_KEY
builtin printf 'Litlas API key: ' >&2
IFS= read -r -s LITLAS_API_KEY
builtin printf '\n' >&2
litlas_curl() {
  if [[ ! ${LITLAS_API_KEY-} =~ ^litlas_sk_[A-Za-z0-9_-]{43}$ ]]; then
    builtin printf 'Invalid Litlas API key.\n' >&2
    return 64
  fi
  command curl --config <(
    builtin printf 'header = "Authorization: Bearer %s"\n' "$LITLAS_API_KEY"
  ) "$@"
}

Authentication and API keys

Send your Litlas API key in the Authorization header on every request.

Authorization: Bearer <YOUR_LITLAS_API_KEY>

Open Settings to issue or rotate an API key

The complete key is shown only once, immediately after it is issued or rotated. Litlas stores only what it needs to verify the key and cannot show the complete value again.

Rotating the key invalidates the previous key immediately. Update the agent or secret store before its next request; requests using the old key return 401.

Treat the key like a password. Do not put it in a URL, source code, chat transcript, log, shell history, or repository. Never type the complete value directly into a shell command. After pasting it into the intended secret store, overwrite your clipboard and hide the key in Settings.

When finished, clear the key and helper function from the current shell:

unset LITLAS_API_KEY
unset -f litlas_curl

Identifiers and note behavior

board_id

An opaque board identifier returned by the board list and create endpoints. Copy it exactly; do not derive it from the board name.

item_count

The total number of live items attached to the board: papers plus documents. It can differ from papers.length returned by the board papers endpoint, which lists papers only.

paper_id

An opaque Litlas paper identifier returned with a paper. Copy it exactly instead of parsing it or deriving it from a title, DOI, or board position. The API removes surrounding whitespace but preserves every internal character: repeated spaces remain distinct, and a paper_id can contain /, ?, #, or % characters.

URL-encode paper_id in paths

Encode the complete identifier as one path segment. Do not split it at slashes and do not encode the entire URL.

const paperId = 'SOURCE/ID';
encodeURIComponent(paperId); // 'SOURCE%2FID'

A note belongs to the paper

A paper has one account-level note shared across every board. Updating it through any board context changes the same note everywhere; removing a paper from a board does not delete that note. GET and PATCH require the paper to be an active saved item in the account library; otherwise they return 404.

Input limits

  • Board name: 1–120 characters, counted before whitespace is normalized. Litlas then trims surrounding whitespace, collapses internal whitespace, and rejects a case-insensitive duplicate name in the same account with 400.
  • paper_id: 1–120 characters after surrounding whitespace is removed. Internal whitespace is preserved, not collapsed; do not case-fold or otherwise normalize this opaque identifier.
  • note: at most 2000 characters. An empty string clears the note.
  • expected_updated_at: 1–64 characters and cannot be blank.
  • Unknown fields in a JSON request body are rejected with 422.

Update notes without overwriting another agent

expected_updated_at is required. Use optimistic concurrency for every note edit:

  1. GET the note and retain its updated_at value.
  2. PATCH the edited note with that exact value as expected_updated_at.
  3. On 409 note_conflict, do not overwrite or silently merge. Read current_note and current_updated_at from the conflict, reconcile explicitly, then send a new PATCH.
{
  "detail": {
    "code": "note_conflict",
    "paper_id": "P43NTWMNFA70",
    "current_note": "The note saved by another client.",
    "current_updated_at": "2026-09-03T12:34:00Z"
  }
}

Endpoints

Commands with a JSON body pass the displayed example to curl on standard input instead of an argument. If the real body contains sensitive content, do not replace the example inside an interactive shell command or history; have the agent generate the JSON on standard input without logging it.

GET/api/v1/agent/boards

List boards

Returns the boards owned by the API-key account. Litlas verifies stale owner-membership mirrors before calculating item_count. If that verification needs the paper catalog and it is unavailable, the whole request returns 502 paper_metadata_unavailable; if stored identities are ambiguous, it returns 409 library_identity_ambiguous. It never returns a partial board list or a possibly inaccurate count.

litlas_curl --fail-with-body --silent --show-error \
  --request GET \
  'https://litlas.ai/api/v1/agent/boards'
Success response (HTTP 200)
{
  "boards": [
    {
      "board_id": "board_01JABCDEF",
      "name": "Reading list",
      "item_count": 0,
      "created_at": "2026-09-03T12:30:00Z",
      "updated_at": "2026-09-03T12:30:00Z"
    }
  ]
}
Retry and idempotency

Safe to repeat. It may perform the membership self-healing described above. On 502, retry only after the paper catalog has recovered. On an identity-related 409, follow the retryable contract below instead of guessing.

POST/api/v1/agent/boards

Create a board

Creates a board for the account and returns it under board.

JSON request body
{
  "name": "Reading list"
}
litlas_curl --fail-with-body --silent --show-error \
  --request POST \
  --header 'Content-Type: application/json' \
  --data-binary @- \
  'https://litlas.ai/api/v1/agent/boards' <<'JSON'
{"name":"Reading list"}
JSON
Success response (HTTP 201)
{
  "board": {
    "board_id": "board_01JABCDEF",
    "name": "Reading list",
    "item_count": 0,
    "created_at": "2026-09-03T12:30:00Z",
    "updated_at": "2026-09-03T12:30:00Z"
  }
}
Retry and idempotency

Not idempotent. Re-sending the same name returns 400 instead of creating a duplicate. After an uncertain result, list boards before deciding what to do.

DELETE/api/v1/agent/boards/{board_id}

Delete a board

Deletes the selected board and detaches its papers and documents. Saved papers and their notes remain in the account library; attached document records also remain, with only the board link removed.

litlas_curl --fail-with-body --silent --show-error \
  --request DELETE \
  'https://litlas.ai/api/v1/agent/boards/board_01JABCDEF'
Success response (HTTP 200)
{
  "board_id": "board_01JABCDEF",
  "deleted": true
}
Retry and idempotency

After a successful delete, repeating the request returns 404. After an uncertain result, list boards before retrying.

GET/api/v1/agent/boards/{board_id}/papers

List papers on a board

Returns the papers currently attached to the selected board. Before returning them, Litlas verifies stale owner-membership mirrors. If that verification needs an unavailable paper catalog, the whole request returns 502 paper_metadata_unavailable; if stored identities are ambiguous, it returns 409 library_identity_ambiguous. Metadata resolution for the returned papers can also produce 502. It never returns a 200 response with unverified membership or only the papers whose metadata resolved.

litlas_curl --fail-with-body --silent --show-error \
  --request GET \
  'https://litlas.ai/api/v1/agent/boards/board_01JABCDEF/papers'
Success response (HTTP 200)
{
  "board_id": "board_01JABCDEF",
  "papers": [
    {
      "paper_id": "P43NTWMNFA70",
      "title": "Example paper title",
      "authors": [
        "Ada Lovelace",
        "Alan Turing"
      ],
      "year": 2026
    }
  ]
}
Retry and idempotency

Safe to repeat. It may perform the membership self-healing described above. On 502, retry only after the paper catalog has recovered. On an identity-related 409, follow the retryable contract below instead of guessing.

POST/api/v1/agent/boards/{board_id}/papers

Add a paper to a board

Attaches the paper identified by paper_id to the selected board. If it is not already an active saved paper, this operation also saves it to the account library. A valid catalog lookup that finds no metadata returns 404 only when no snapshot owned by that account can resolve it; if the paper catalog cannot be reached or its response cannot be validated and no owned snapshot can resolve the metadata, the request returns 502 with detail.code paper_metadata_unavailable.

JSON request body
{
  "paper_id": "P43NTWMNFA70"
}
litlas_curl --fail-with-body --silent --show-error \
  --request POST \
  --header 'Content-Type: application/json' \
  --data-binary @- \
  'https://litlas.ai/api/v1/agent/boards/board_01JABCDEF/papers' <<'JSON'
{"paper_id":"P43NTWMNFA70"}
JSON
Success response (HTTP 200)
{
  "board_id": "board_01JABCDEF",
  "paper_id": "P43NTWMNFA70",
  "added": true
}
Retry and idempotency

Idempotent for one board and paper: a repeat succeeds with added set to false. On paper_metadata_unavailable, first GET the target board's papers. Retry the add at most once only if that read succeeds and confirms the paper is absent; if the read fails or cannot confirm absence, stop and surface the error. For an identity-related 409, follow the retryable contract below.

DELETE/api/v1/agent/boards/{board_id}/papers/{paper_id}

Remove a paper from a board

Removes only this board membership. It does not delete an existing saved paper from the account library or its account-level note. Litlas needs no catalog lookup when the supplied paper_id exactly matches a membership on this board, or when it exactly matches a saved paper and the board holds no other paper; an exact saved paper alone is not enough. Otherwise Litlas resolves the identifier through the paper catalog first, and returns 502 with detail.code paper_metadata_unavailable before applying a membership change when the catalog cannot be reached or its response cannot be validated and no snapshot owned by that account can resolve it. A valid catalog lookup that finds no metadata, or an identifier that names no membership on this board, is not an error: the request succeeds with removed set to false.

litlas_curl --fail-with-body --silent --show-error \
  --request DELETE \
  'https://litlas.ai/api/v1/agent/boards/board_01JABCDEF/papers/P43NTWMNFA70'
Success response (HTTP 200)
{
  "board_id": "board_01JABCDEF",
  "paper_id": "P43NTWMNFA70",
  "removed": true
}
Retry and idempotency

Idempotent for one board and paper: if the membership is already absent, removed is false. On paper_metadata_unavailable, do not loop blindly: first GET the target board's papers. Retry the same DELETE at most once only if that read succeeds and contains the exact opaque paper_id; if the read fails, the exact id is absent, or the retry fails, stop and surface the error instead of guessing an alias. For an identity-related 409, follow the retryable contract below.

GET/api/v1/agent/papers/{paper_id}/note

Read a paper note

Returns the account-level note and the updated_at version required by PATCH. An exact local paper identity needs no catalog lookup. When only a possible alias can identify the saved paper, an unavailable catalog returns 502 paper_metadata_unavailable and ambiguous stored identities return 409 library_identity_ambiguous; no partial 200 response is returned. A confirmed missing or inactive item returns 404.

litlas_curl --fail-with-body --silent --show-error \
  --request GET \
  'https://litlas.ai/api/v1/agent/papers/P43NTWMNFA70/note'
Success response (HTTP 200)
{
  "paper_id": "P43NTWMNFA70",
  "note": "Initial note.",
  "updated_at": "2026-09-03T12:31:00Z"
}
Retry and idempotency

Read-only. On 502, retry only after the paper catalog has recovered. On an identity-related 409, follow the retryable contract below instead of guessing.

PATCH/api/v1/agent/papers/{paper_id}/note

Update a paper note

Replaces the account-level note only if expected_updated_at still matches. An exact local paper identity needs no catalog lookup. When only a possible alias can identify the saved paper, an unavailable catalog returns 502 paper_metadata_unavailable and ambiguous stored identities return 409 library_identity_ambiguous before the note is changed. A confirmed missing or inactive item returns 404.

JSON request body
{
  "note": "Key finding and follow-up questions.",
  "expected_updated_at": "2026-09-03T12:31:00Z"
}
litlas_curl --fail-with-body --silent --show-error \
  --request PATCH \
  --header 'Content-Type: application/json' \
  --data-binary @- \
  'https://litlas.ai/api/v1/agent/papers/P43NTWMNFA70/note' <<'JSON'
{"note":"Key finding and follow-up questions.","expected_updated_at":"2026-09-03T12:31:00Z"}
JSON
Success response (HTTP 200)
{
  "paper_id": "P43NTWMNFA70",
  "note": "Key finding and follow-up questions.",
  "updated_at": "2026-09-03T12:35:00Z"
}
Retry and idempotency

Do not blindly retry. On 502, retry only after the paper catalog has recovered. Reusing a version after another update returns 409; GET the note again and reconcile explicitly. For an identity-related 409, follow the retryable contract below.

Errors

A request succeeds only with a 2xx response. Do not treat an error body as a successful result.

StatusMeaning
400The request is malformed or cannot be applied.
401The Bearer key is missing, invalid, or was invalidated by rotation.
403The key is valid, but this account is not permitted to perform the operation.
404The board or paper does not exist in this account scope.
409The resource state conflicts with the request, including a stale note version, a free-plan capacity limit, a concurrent library identity change, or stored identities that cannot be resolved unambiguously.
422A path parameter or JSON field does not match the documented schema.
502Required paper metadata or catalog verification was unavailable. detail.code is paper_metadata_unavailable and detail.paper_id identifies the paper. Board listings never return a partial list or a possibly inaccurate count; board-paper listings do not return 200 with unverified membership or a partial metadata result; paper addition does not report success; paper removal and note PATCH apply no requested change before this response.
500 / other 5xxLitlas could not complete the request. The result is not confirmed; inspect the response before retrying a write.

Error response schema

Every error has a top-level detail field. Depending on the error, detail is a string, a machine-readable object, or the standard validation array. Branch on the HTTP status first and, when detail is an object, on detail.code. Never assume one detail shape for every non-2xx response.

String detail example

{
  "detail": "Board not found"
}

Free-plan capacity exceeded (409)

{
  "detail": {
    "code": "free_capacity_exceeded",
    "kind": "items",
    "limit": 100,
    "used": 100
  }
}

Library identity changed (409)

{
  "detail": {
    "code": "library_identity_changed",
    "message": "The library changed concurrently. Retry the request.",
    "retryable": true
  }
}

Stored library identity is ambiguous (409)

{
  "detail": {
    "code": "library_identity_ambiguous",
    "message": "Stored library identities cannot be resolved unambiguously. No changes were applied.",
    "retryable": false
  }
}

Stale note conflict (409)

{
  "detail": {
    "code": "note_conflict",
    "paper_id": "P43NTWMNFA70",
    "current_note": "The note saved by another client.",
    "current_updated_at": "2026-09-03T12:34:00Z"
  }
}

Paper metadata or catalog unavailable (502)

{
  "detail": {
    "code": "paper_metadata_unavailable",
    "paper_id": "P43NTWMNFA70"
  }
}

Safe retries for library identity conflicts

Requests that resolve stored paper identities can return one of these 409 responses after a concurrent identity change or when stored identities cannot be resolved safely. Use retryable as an explicit instruction:

  • library_identity_changed with retryable: true: start the same request again from the beginning so Litlas re-resolves current identity. Retry at most once; if it repeats, stop and surface the error.
  • library_identity_ambiguous with retryable: false: do not guess, modify stored data, or retry automatically. No mutation was confirmed; stop and surface the error. Ask the account owner or Litlas support to repair the stored identity conflict, and retry only after they explicitly confirm that it is resolved.
  • Other 409 responses have their own recovery rules. In particular, reconcile note_conflict explicitly and do not retry free_capacity_exceeded unchanged.

The examples use curl --fail-with-body so HTTP errors produce a non-zero exit status while preserving the response body for diagnosis.

Request conventions

  • Send Content-Type: application/json when a request has a JSON body.
  • Path identifiers must be URL-encoded if they are inserted programmatically.
  • Every Agent API response, including errors, sends Cache-Control: private, no-store and Pragma: no-cache. Do not persist account data in an intermediary cache.
  • Never guess that a timed-out write succeeded or failed. Read the resource before deciding whether to retry.
API documentation | Litlas