LIMEDocs

Reference

API reference

A catalogue of the public LIME operations: the method, path, required header, and purpose of every call, plus the response shape and the error code a client branches on. For AI agents: /docs/all.en.md. Version: 2026-10-15

Conventions#

Every operation lives under https://lime.pics/api/v1. Requests and responses are JSON.

  • Credentials travel only in headers — never in the URL or the query string.
  • There are three credentials: the Agent Token (X-Agent-Token), the Site Token (X-Site-Token), and the MCP passport (Authorization: Bearer).
  • Expensive operations are rate-limited with a Redis sliding window (default 60 s); a denied request does not consume quota. On 429, back off exponentially with jitter and honor Retry-After when present — it is not universal (the OAuth 429 omits it) — and cap retries.
  • Application errors carry a stable machine-readable code. The OAuth token endpoint is the exception for its RFC 6749 outcomes: a successful body is raw {access_token, token_type, expires_in}, and protocol errors (invalid_request, invalid_client, invalid_grant, unsupported_grant_type) are raw {error, error_description}, with no envelope. Its rate-limit and unavailability responses (429, 503) still use the standard {ok:false,error:{code,message,detail}} envelope.
  • The default mint request carries no grant_type; the field appears only on the separate authorization-code form path.
  • A machine-readable OpenAPI description of the public operations is published at https://lime.pics/.well-known/openapi/v1.json.
  • Datetime fields (expires_at, created_at, approved_at, delivered_at) serialize as ISO-8601 UTC with a +00:00 offset (not Z), or as JSON null when unset.
  • Every API response carries X-Request-Id and X-Trace-ID headers (static assets do not).
  • Only /api/v1 is published; there is no deprecation or sunset mechanism, and no other API version exists.

Credentials#

CredentialHeaderUsed by these calls
Agent TokenX-Agent-Token (LIME_AGENT_TOKEN)Mint an MCP passport, approve a sign-in, read its own profile
Site TokenX-Site-Token (LIME_SITE_TOKEN)Create sign-in and binding requests, read events, exchange a code
MCP passportAuthorization: BearerProving identity to a service
none—JWKS, OAuth metadata, the public profile and login request

The Agent Token and Site Token authenticate to LIME only: never send either to a service. The only credential a service ever sees is the MCP passport, and it verifies it locally via JWKS. The full comparison lives in Credentials.

Operations#

FlowOperationCredentialPurpose
MCPPOST POST /api/v1/modules/oauth/tokenX-Agent-TokenMint an aud=mcp passport for 300 seconds
MCPGET GET /api/v1/modules/oauth/.well-known/oauth-authorization-servernoneRead token_endpoint and jwks_uri
Sign-inPOST POST /api/v1/modules/agent-login/requestsX-Site-TokenCreate a PENDING login request
Sign-inGET GET /api/v1/modules/agent-login/requests/{request_id}X-Site-TokenPoll a login request's status
Sign-inGET GET /api/v1/modules/agent-login/eventsX-Site-TokenSSE stream of login events
AgentGET GET /api/v1/auth/requests/{request_id}noneRead a login request and its PoW challenge
AgentPOST POST /api/v1/modules/agent-login/requests/{request_id}/approveX-Agent-TokenApprove a sign-in with a pow_nonce
AgentGET GET /api/v1/core/agents/me/profileX-Agent-TokenRead the authenticated agent's profile
BindingPOST POST /api/v1/modules/bindings/requestsX-Site-TokenCreate a binding request and get a connect_url
BindingPOST POST /api/v1/modules/bindings/exchangeX-Site-TokenExchange a binding_code for a binding passport
CoreGET GET /api/v1/core/.well-known/jwks.jsonnonePublic keys for local passport verification
CoreGET GET /api/v1/agents/{agent_id}nonePublic agent profile after verification

Sequences#

The operations form three flows, and the order differs between them.

  1. An MCP call, no browser. The agent mints a passport with POST POST /api/v1/modules/oauth/token, then calls the MCP server with Authorization: Bearer. The server verifies the signature itself, fetching GET GET /api/v1/core/.well-known/jwks.json; it never calls LIME.
  2. Agent site login. The backend creates a request (POST POST /api/v1/modules/agent-login/requests); the agent reads it (GET GET /api/v1/auth/requests/{request_id}) and approves it (POST POST /api/v1/modules/agent-login/requests/{request_id}/approve); the backend waits for the passport on GET GET /api/v1/modules/agent-login/events. Polling with GET GET /api/v1/modules/agent-login/requests/{request_id} is the fallback.
  3. Binding. The backend creates a request (POST POST /api/v1/modules/bindings/requests) and sends the human to connect_url; after the return with ?binding_code=, the backend exchanges the code (POST POST /api/v1/modules/bindings/exchange) and verifies the passport.

An MCP server never receives an Agent Token or a Site Token: it sees only the passport in the Authorization header.

Response shapes#

Application responses arrive in a LIME envelope, while discovery and OAuth documents are raw RFC JSON. A successful call, for example POST POST /api/v1/modules/agent-login/requests:

json
{
  "ok": true,
  "data": {
    "login_request_id": "<LOGIN_REQUEST_ID>",
    "status": "PENDING",
    "expires_at": "<ISO-8601>"
  }
}

The mint answers with RFC 6749, with no envelope and with Cache-Control: no-store; expires_in is 300 seconds:

json
{
  "access_token": "<PASSPORT>",
  "token_type": "Bearer",
  "expires_in": 300
}

An application error always carries a code:

json
{
  "ok": false,
  "error": {
    "code": "REQUEST_VALIDATION_ERROR",
    "message": "request validation failed",
    "detail": { "errors": [ { "loc": ["body", "domain"], "msg": "<REASON>" } ] }
  }
}

Branch on error.code, not on the message text. Common statuses: 401 for a missing or wrong credential (MISSING_AGENT_TOKEN when X-Agent-Token is absent, INVALID_AGENT_TOKEN when it is wrong or carried in Authorization: Bearer, SITE_TOKEN_MISSING for a site call with no X-Site-Token), 422 for a body that failed validation (REQUEST_VALIDATION_ERROR), 403 for an inactive agent (AGENT_INACTIVE), and 429 when a limit is exceeded (RATE_LIMIT_EXCEEDED). The mint is the exception: a bad credential or body answers RFC 6749 (invalid_client 401, invalid_request 400) with no envelope.

Every application error is {"ok": false, "error": {"code", "message", "detail"}}: REQUEST_VALIDATION_ERROR (422) puts the per-field messages in detail.errors, and HTTP_500 (500) carries an empty detail: {}. Response headers are X-Request-Id and X-Trace-ID. correlation_id and invariant_id are nested inside detail and appear only on CAPTCHA errors — a 400 CAPTCHA rejection carries detail.correlation_id, and 503 CAPTCHA_UNAVAILABLE carries detail.invariant_id plus detail.correlation_id; those CAPTCHA codes are portal-facing and not part of the public integrator catalogue.

The data payload of each operation:

OperationSuccess data keys
POST POST /api/v1/modules/agent-login/requestslogin_request_id, status, expires_at
GET GET /api/v1/modules/agent-login/requests/{request_id}status
GET GET /api/v1/auth/requests/{request_id}request_id, status, pow_challenge, pow_difficulty, expires_at
POST POST /api/v1/modules/agent-login/requests/{request_id}/approverequest_id, site_id, webhook_url, status, created_at, expires_at, approved_agent_id, approved_at, delivered_at, create_agent_id, create_idempotency_key
POST POST /api/v1/modules/oauth/tokenRFC 6749 body: access_token, token_type, expires_in
GET GET /api/v1/core/agents/me/profileagent_id, user_id, display_name, avatar_url, description
GET GET /api/v1/agents/{agent_id}agent_id, display_name, avatar_url, description, status
POST POST /api/v1/modules/bindings/requestsbinding_id, connect_url, status (PENDING), expires_at
POST POST /api/v1/modules/bindings/exchangepassport, binding_id, redirect_uri

Two identifiers name the same login request: login_request_id in the create response equals request_id in the approve and status responses — one UUID. In the approve data, create_agent_id and create_idempotency_key are always null today (reserved fields, not the login UUID). The Idempotency-Key header is ignored on the site-login endpoints (not required, not rejected). GET GET /api/v1/core/agents/me/profile returns user_id, which is the SDK AgentProfile.owner_id.

The site status read (GET GET /api/v1/modules/agent-login/requests/{request_id}) returns only status — one of PENDING, APPROVED, DELIVERING, DELIVERED, EXPIRED, CANCELLED — whereas the public read (GET GET /api/v1/auth/requests/{request_id}) returns request_id, status, pow_challenge, pow_difficulty and expires_at. In the approve data, webhook_url is a reserved field: LIME stores and echoes it back, and delivers over SSE rather than an HTTP webhook. The public agent profile (GET GET /api/v1/agents/{agent_id}) carries status as a string with the values active or inactive; there is no suspended agent status.

Edge cases#

  • A secret in the wrong header. X-Agent-Token on a site call returns SITE_TOKEN_MISSING, and X-Site-Token on an agent call returns INVALID_AGENT_TOKEN. An agent call that omits X-Agent-Token returns MISSING_AGENT_TOKEN; a wrong token, or one carried in Authorization: Bearer, returns INVALID_AGENT_TOKEN. Match the header to the credentials table.
  • An Agent Token sent to a service. The mint is the only call that needs it; an MCP server must never receive it. If a token leaked, rotate it in the portal.
  • A body that failed validation. The default mint body is exactly {"domain":"<BARE_HOST>"} with Content-Type: application/json, and it carries no grant_type; unsupported_grant_type applies only to the separate authorization-code form-urlencoded path, not to this body. A scheme or path in domain is stripped before matching, so a bare example.com and https://example.com/path resolve the same; invalid_request (400) is returned for an IP-literal, a reserved host, or an empty domain; for a missing Content-Type: application/json; for malformed or non-object JSON; for extra JSON keys beside domain; for a non-string domain; and for a leftover ?domain= query parameter.
  • An unknown `kid` at the server. The server rejects the passport even with a valid signature until it refreshes GET GET /api/v1/core/.well-known/jwks.json; refresh the cache and retry.
  • Retrying too often. The mint and public reads are rate-limited; the OAuth 429 from POST POST /api/v1/modules/oauth/token carries no Retry-After, so back off with jitter and do not retry back to back, or the RATE_LIMIT_EXCEEDED (429) only deepens.
  • Creating an agent or a site, minting or rotating a token. All portal-only (UI) — no public API mints an Agent Token or a Site Token, and no public sandbox or staging host exists for the LIME API (https://lime.pics is its only public origin); the public demo MCP resource server at https://demo.lime.pics/mcp is a separate host. See the value reference.

Codes and errors#

Branch on error.code, never on the message text. The most common public codes:

CodeHTTPCauseAction
MISSING_AGENT_TOKEN401An agent call with no X-Agent-TokenSend the Agent Token header
INVALID_AGENT_TOKEN401The X-Agent-Token is wrong, or sent in Authorization: BearerMint an Agent Token, then send it in X-Agent-Token
SITE_TOKEN_MISSING401A site call without X-Site-TokenSend the Site Token header
REQUEST_VALIDATION_ERROR422The body failed validationFix the payload to match the schema
AGENT_INACTIVE403The agent is inactiveReactivate it in the portal
BINDING_REQUEST_EXPIRED410The binding request expiredCreate a new request
RATE_LIMIT_EXCEEDED429Too many callsBack off with jitter; honor Retry-After when present (the OAuth 429 omits it)
JWKS_UNAVAILABLE503JWKS is unreachable and no cached key verifies the passportRetry after a short delay

This is the complete public integrator catalogue (59 codes); CAPTCHA, owner-session, registration and credential-rotation codes are portal-facing and intentionally excluded. The full catalogue is in the value reference.

Next steps#

Was this page helpful?

API reference · LIME