API Authentication
How to authenticate against the LinkTrail REST API: SDK API keys for the event endpoints, access tokens and X-Workspace-Id for the management endpoints.
The LinkTrail REST API is served at https://backend.linktrail.io/v1 and speaks JSON. It is the same API the dashboard and the SDKs use, and it takes two kinds of credential depending on the endpoint.
| Endpoints | Credential | Headers |
|---|---|---|
SDK endpoints — /events/install, /events/open, /events, /events/click/{token} | A workspace API key (lt_live_…) | X-Api-Key: lt_live_… or Authorization: Bearer lt_live_… |
Link resolution — /links/resolve | None (public, read-only) | — |
| Management — apps, domains, links, stats, webhooks, API keys | A user access token, plus the workspace to act in | Authorization: Bearer <accessToken> and X-Workspace-Id: <id> |
API keys
API keys authenticate the SDK endpoints. Create one in the dashboard (Settings → API Keys → Create key) or with POST /api-keys. The full key is returned once — store it then; afterwards only its prefix is shown. Revoke a key with DELETE /api-keys/:keyId; the dashboard shows when each key was last used.
{ "id": 1, "name": "iOS production", "prefix": "lt_live_3f9a1b", "key": "lt_live_3f9a1b…", "lastUsedAt": null, "createdAt": "2026-06-28T00:00:00Z" }The key decides the workspace. Every API key belongs to one workspace, and so do apps, domains and links. An install sent with a key is recorded in that key's workspace, so the app's identifier must be registered there — the same identifier in another workspace answers 404.
curl -H 'X-Api-Key: lt_live_…' \
https://backend.linktrail.io/v1/events/click/460ce0f7bf4e840eb18da7fb793eb5f7Access tokens
The management endpoints authenticate a user, not a key. Exchange an email and password for tokens with POST /auth/login. The access token lasts about 30 minutes; get a new one with POST /auth/refresh-tokens and the refresh token. API keys are not accepted on these endpoints, and there is no long-lived server-to-server credential for them.
{ "email": "you@acme.com", "password": "…" }{ "user": { /* … */ },
"tokens": { "access": { "token": "eyJ…", "expires": "…" },
"refresh": { "token": "eyJ…", "expires": "…" } } }- With two-factor authentication on, login returns
{ "twoFactorRequired": true, "challengeToken": "eyJ…" }instead. Send the challenge and the 6-digit code toPOST /auth/login/totpfor the tokens. - Login fails with 403 until the account's email address is verified.
- A missing or expired token gets
{"code":401,"message":"Please authenticate"}.
Workspaces
Most management endpoints act inside a workspace: send its id in X-Workspace-Id. GET /workspaces lists the workspaces you belong to (a plain array, not paginated). A workspace you are not a member of answers 404, not 403, and a missing header answers 400.
[ { "id": 1, "name": "Acme Inc", "timezone": "Asia/Karachi", "region": "us", "defaultAttribution": null } ]What a call may do depends on your role in that workspace. Every member can read apps, domains, links and stats. Managing links needs editor, admin or owner; managing apps and domains needs developer, admin or owner. API keys and webhooks — reading them included — need developer, admin or owner. A call without the right answers 403.
Conventions
- Pagination. The paginated lists — links, apps and domains — take
?page=1&limit=10&sortBy=createdAt:descand answer{ "results": [ /* … */ ], "page": 1, "limit": 10, "totalPages": 4, "totalResults": 37 }./workspaces,/api-keysand/webhooksanswer a plain array. - Errors. Errors are JSON of the shape
{ "code": 400, "message": "Human-readable reason" }: 400 validation, 401 bad or missing credentials, 402 the plan does not include the feature, 403 the role does not allow it, 404 not found or not a member, 429 rate-limited. The one exception is the per-IP 429 on the SDK endpoints and/links/resolve, whose body is plain text (Too many requests, please try again later.), not JSON. The SDK-facing statuses are on Errors. - Rate limits. The SDK endpoints and
/links/resolveare limited per IP address (currently 100 requests a minute); over the limit they answer 429. Failed sign-ins are limited too. - Idempotency. A
POSTmay carryIdempotency-Key: <uuid>; repeating it with the same key returns the original response instead of acting twice.
{"code":401,"message":"Invalid or missing API key"} on an SDK endpoint means the key is wrong, revoked or missing — check it in Settings → API Keys.