Documentation Index
Fetch the complete documentation index at: https://uselora.dev/llms.txt
Use this file to discover all available pages before exploring further.
Endpoint
The API is REST over HTTPS. Plain HTTP isn’t accepted.Authentication
Send your API key as a Bearer token on every request:API key
Create an API key
Scopes
A key carries scopes that limit what it can do. Pick the narrowest set that lets the integration work. A leaked write-everything key is much worse than a leaked read-only one.| Scope | What it grants |
|---|---|
shortcuts.read | Read shortcuts in the workspace. |
shortcuts.write | Create, update, and delete shortcuts in the workspace. |
tags.read | Read tags in the workspace. |
tags.write | Create, update, and delete tags in the workspace. |
folders.read | Read folders in the workspace. |
folders.write | Create, update, and delete folders in the workspace. |
Errors
Every error response is JSON with the same shape.error is human-readable. requestId correlates with the X-Lora-Request-Id response header; quote it when filing a support ticket. traceId shows up when the request hit a Sentry trace. code is optional, and when present it’s a machine-readable handle you can branch on.
Minimum shape:
What a 401 means
The 401 body is identical whether the header is missing, malformed, the key is unknown, expired, or disabled. The API deliberately doesn’t tell you which one. Distinguishing them on the wire would let an attacker probe key validity:code field on the generic 401. Treat any 401 as “your request wasn’t authenticated” and check the key you sent.
If the key is valid but lacks the scope an endpoint needs, you get 403 with code: "insufficient_scope" and a required_scope field naming what was missing.
Error codes
Every machine-readablecode the public /v1/* routes emit. Codes are stable. A value won’t be repurposed. New codes may be added.
| Code | Status | When |
|---|---|---|
validation_error | 400 | Schema validation failed. The body includes fieldErrors keyed by field name. |
invalid_json | 400 | The request body wasn’t valid JSON, or wasn’t a JSON object. |
reserved_slug | 400 | The submitted shortcut slug is on the reserved list (POST /v1/shortcuts). |
no_default_folder | 400 | The workspace has no live folder for the tag’s side-effect shortcut to land in (POST /v1/tags). |
qr_data_too_long | 400 | The payload exceeds the capacity of the requested error correction level (GET /v1/qr). |
insufficient_scope | 403 | The API key is valid but lacks the scope an endpoint needs. Body includes required_scope, e.g. "shortcuts.write". |
no_permission | 403 | The API key’s user is not a member of the workspace the key is scoped to. |
plan_requires_upgrade | 403 | A requested feature isn’t available on the workspace’s plan. Body includes current_plan and required_capability. Today emitted by GET /v1/qr when an anonymous caller passes hideLogo=true. The removeWatermark capability unlocks on Basic and above; Free keys can’t reach the public API at all. |
folder_not_found | 404 | The supplied folderId doesn’t exist in this workspace (POST /v1/shortcuts). |
tag_not_found | 404 | One or more tagIds don’t exist in this workspace (POST /v1/shortcuts, PATCH /v1/shortcuts/{slug}). |
slug_conflict | 409 | A shortcut with this slug already exists in the workspace (POST /v1/shortcuts). |
tag_name_exists | 409 | A tag with this name already exists in the workspace (POST /v1/tags). |
shortcut_slug_exists | 409 | The tag’s auto-generated shortcut slug collides with an existing shortcut (POST /v1/tags). |
folder_name_exists | 409 | A folder with this name already exists in the workspace (POST /v1/folders). |
rate_limit_exceeded | 429 | The API key’s per-window request budget is exhausted. See the Retry-After header and Rate limits. |
internal_error | 500 | Server-side failure. Safe to retry with backoff. |
Idempotency
GET and DELETE are idempotent by definition. For POST and PATCH, send an Idempotency-Key header with a unique string so retries don’t double-write: