Create and remove boards, manage papers on a board, delete papers from the library, 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:
Bash — set up the key
unset LITLAS_API_KEYbuiltin printf 'Litlas API key: ' >&2IFS= read -r -s LITLAS_API_KEYbuiltin printf '\n' >&2litlas_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.
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:
Bash — clear the key
unset LITLAS_API_KEYunset -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.
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. A PATCH appends to the existing note by default (mode "append"), because an agent only ever holds the note as it read it and replacing by default would drop whatever was written in between. Send mode "replace" to overwrite the whole note, which is also the only way to clear it. GET and PATCH require the paper to be an active saved item in the account library; otherwise they return 404.
Deleting is separate from removing from a board
Removing a paper from a board only detaches it; the paper and its note stay in the library. Deleting the paper from the library is the separate DELETE on the paper itself. By default it moves the paper to the library trash, where it stays restorable from the Library trash screen until its retention period expires, and its board memberships are kept so a restore puts it back where it was. Adding permanent=true deletes the paper, its note, and its saved snapshot outright, whether or not it is in the trash. That cannot be undone from the app or the API.
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. In mode "replace" an empty string clears the note; in mode "append" a blank note is rejected with 422 instead of silently doing nothing. When appending makes the stored note exceed the limit the request fails with 422 note_too_long and nothing is written; the note is never truncated.
expected_updated_at: 1–64 characters and cannot be blank.
mode: one of append, replace. Defaults to append.
permanent: a boolean query parameter on the library delete. Defaults to false, which moves the paper to the trash.
Unknown fields in a JSON request body are rejected with 422.
Unknown query parameters are rejected with 422, so a misspelled parameter cannot silently run with its default.
Update notes without overwriting another agent
expected_updated_at is required. Use optimistic concurrency for every note edit:
GET the note and retain its updated_at value.
PATCH with that exact value as expected_updated_at. Send only the new text to append it, or the complete new note with mode "replace" to overwrite.
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.
Never reuse a version that already succeeded: because a version is consumed once, a blindly repeated append cannot double-write.
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"}}
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.
Upload a PDF
POST/api/v1/agent/library/upload-pdf
Send multipart/form-data with exactly one of pdf (up to 50 MiB) or source_url (a public PDF URL), and optional board_ids (a JSON array). URL downloads validate every redirect and fail if the result is not a PDF. Metadata is extracted from the PDF. Papers outside the catalog are stored privately in your library with their PDF. Existing board memberships and notes are preserved. The response example shows the principal fields; paper and item include the standard library metadata.
{"ok":true,"created":true,"pdf_uploaded":true,"paper":{"id":"browser_import:example","title":"Example paper title"},"item":{"paper_id":"browser_import:example","board_ids":["board_01JABCDEF"]}}
Retry and idempotency
Check pdf_uploaded and the returned paper.id. Re-uploading the same identified paper updates its PDF; it does not create another library item. After a timeout, check the destination board before retrying. Invalid PDF returns 400, oversized file 413, and unavailable storage 503.
Upload a BibTeX file
POST/api/v1/agent/library/import-bibtex
Send multipart/form-data with bib (UTF-8, at most 500,000 characters) and optional board_ids (a JSON array). The shared library importer saves entries missing from the catalog as private records. It does not download PDFs. Check imported_count, unmatched_count and note_conflict_count; imported_paper_ids contains at most 20 IDs. Without board_ids, board declarations in the file replace memberships, as in the library import UI. The response example shows the principal fields.
Existing papers are matched again and personal notes are preserved. Specifying board_ids adds those memberships. Invalid or empty BibTeX returns 400; oversized files return 413. Resolve reported unmatched entries and note conflicts explicitly.
List boards
GET/api/v1/agent/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.
Example request
litlas_curl --fail-with-body --silent --show-error \ --request GET \ 'https://litlas.ai/api/v1/agent/boards'
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.
Create a board
POST/api/v1/agent/boards
Creates a board for the account and returns it under board.
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 a board
DELETE/api/v1/agent/boards/{board_id}
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.
After a successful delete, repeating the request returns 404. After an uncertain result, list boards before retrying.
List papers on a board
GET/api/v1/agent/boards/{board_id}/papers
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.
Example request
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.
Add a paper to a board
POST/api/v1/agent/boards/{board_id}/papers
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.
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.
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.
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.
Read a paper note
GET/api/v1/agent/papers/{paper_id}/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.
Example request
litlas_curl --fail-with-body --silent --show-error \ --request GET \ 'https://litlas.ai/api/v1/agent/papers/P43NTWMNFA70/note'
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.
Update a paper note
PATCH/api/v1/agent/papers/{paper_id}/note
Writes the account-level note only if expected_updated_at still matches. The default mode "append" keeps the stored note and adds the submitted text after it, separated by a blank line; if the note is empty the submitted text becomes the note. Mode "replace" overwrites the whole note and an empty string clears it. Appending past the note length limit fails with 422 note_too_long and writes nothing. 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","mode":"append"}
{"paper_id":"P43NTWMNFA70","note":"Initial note.\n\nKey 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. That version check is what makes a repeated append safe, so never reuse a version that already succeeded. For an identity-related 409, follow the retryable contract below.
Delete a paper from the library
DELETE/api/v1/agent/papers/{paper_id}
Deletes the paper from the account library. This is not the board-membership delete: it removes the saved paper itself. By default the paper is moved to the library trash, so it keeps its note and its board memberships and stays restorable from the Library trash screen until the retention period expires. With permanent=true the paper, its note, its board memberships, and its saved snapshot are deleted outright, whether or not it was in the trash, and cannot be restored. 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 anything is deleted. A paper that is not in this account library returns 404.
Repeating the default delete is idempotent: a paper already in the trash stays trashed and its retention period does not restart. Repeating a permanent=true delete returns 404 because the paper is gone. On 502, retry only after the paper catalog has recovered. For an identity-related 409, follow the retryable contract below. Verify before escalating to a permanent delete: it cannot be undone.
Errors
A request succeeds only with a 2xx response. Do not treat an error body as a successful result.
Status
Meaning
400
The request is malformed or cannot be applied.
401
The Bearer key is missing, invalid, or was invalidated by rotation.
403
The key is valid, but this account is not permitted to perform the operation.
404
The board or paper does not exist in this account scope.
409
The 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.
422
A path parameter, query parameter, or JSON field does not match the documented schema. detail.code is note_too_long when appending would push the note past its length limit; nothing is written in that case.
502
Required 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 5xx
Litlas 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.
{"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"}}
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.
Only the query parameters documented for an endpoint are accepted. Anything else is a 422, so a typo cannot quietly fall back to a default.
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.