Authentication

Token types, scopes, and how access is enforced
View as Markdown

All API requests authenticate with a Bearer token in the Authorization header:

Authorization: Bearer <token>

Token types

TypePrefixScopeUse for
Personal access tokenlct_pat_Every workspace the user belongs toPersonal scripts, Claude Desktop, Cursor, ChatGPT, MCP clients
Workspace tokenlct_live_One workspace, bound to the accountE-shop / accounting integrations that act as the workspace itself

A personal access token spans multiple workspaces, so you always pass the workspace_id in the path. A workspace token is bound to one workspace — the workspace_id in the path must match, or the request returns 403.

Scopes

Tokens carry granular scopes in three tiers, per resource:

  • read:<resource> — list and get
  • write:<resource> — read + create + update
  • manage:<resource> — write + custom actions (issue, mark paid, send, delete)

Resource keys are invoices, quotes, proformas, credit_notes, documents, expenses, contacts, bank_accounts, transactions, projects, company_profile, and document_settings. A wildcard like read:* or manage:* grants the tier across all resources.

Scopes are capped at creation by the issuing user’s own permissions — a token can never grant more than the user has. If the user later loses a role, the token’s effective power shrinks with it.

The user’s current workspace role also limits record visibility. For example, the built-in employee role can work with its own documents and expenses but cannot use a broad token to read another member’s records. A suspended membership is treated as absent and returns 404 for that workspace.

Some authenticated endpoints are not delegated to API keys. Push-device registration under /devices requires a signed-in JWT session.

Errors

A missing or invalid token returns 401:

1{
2 "error": "unauthenticated",
3 "message": "Authentication required.",
4 "request_id": "8f9e4c2b-...",
5 "docs_url": "https://docs.lucanto.eu/api/v1/errors/unauthenticated"
6}

A valid token without the required scope returns 403 (forbidden). See Errors.