API reference

Change where a printed QR code points, from your own systems. Create codes in bulk, read scan figures, and keep everything in step with whatever you already run — without anyone opening a dashboard.

The thing worth knowing first

A dynamic QR code encodes a short link, not your destination. The symbol on the packaging never changes; what it resolves to is a row in a database. So repointing a code that is already in the world is a single request, and it takes effect for every copy of it at once — the recalled batch, the seasonal menu, the campaign that moved.

Authentication

Every request carries a bearer token. Create one under API tokens; it is shown once and stored only as a hash, so it cannot be recovered later.

curl https://lynkarr.com/api/v1/me \
  -H "Authorization: Bearer lyn1_your_token_here"

API access is included with Enterprise. A token belonging to a company on another plan authenticates but is refused with 402 and a plan_required code.

Scopes

A token holds exactly the scopes it was given. A write scope does not imply the matching read scope — being explicit means a token's permissions are what the list says, with nothing inferred.

ScopeAllows
codes:read List codes and read their settings.
codes:write Create codes and change where they point. This is what lets a printed code be redirected.
links:read List short links and their destinations.
links:write Create short links and change their destinations.
analytics:read Scan counts, geography, devices and timing.

Endpoints

MethodPathScope
GET /api/v1/me any Confirm a token works and see what it may do.
GET /api/v1/codes codes:read List QR codes. Paginated with limit and offset.
GET /api/v1/codes/:id codes:read One code, including its short link and scan count.
POST /api/v1/codes codes:write Create a code. Returns the URL to encode into the symbol.
PATCH /api/v1/codes/:id codes:write Change where a code points, rename it, or pause it.
POST /api/v1/codes/:id/archive codes:write Stop a code resolving. History is kept.
GET /api/v1/links links:read List short links.
POST /api/v1/links links:write Create a short link, optionally with a chosen ending.
GET /api/v1/analytics/codes/:id analytics:read Scans, unique visitors, daily series and countries.

Repointing a code

The request most integrations exist to make.

curl -X PATCH https://lynkarr.com/api/v1/codes/code_abc123 \
  -H "Authorization: Bearer lyn1_your_token_here" \
  -H "Content-Type: application/json" \
  -d '{"url": "https://example.com/recall-notice"}'

The short link and the scan history are unchanged. Only the destination moves. A static code refuses this with 400, because its destination is encoded in the printed symbol and genuinely cannot change.

Creating a code

curl -X POST https://lynkarr.com/api/v1/codes \
  -H "Authorization: Bearer lyn1_your_token_here" \
  -H "Content-Type: application/json" \
  -d '{"name": "Batch 4471", "url": "https://example.com/batch/4471"}'

The response includes encode — the value to render into the QR symbol. Rendering happens on your side or in our designer; the API stores the definition and owns the redirect.

Reading scans

curl "https://lynkarr.com/api/v1/analytics/codes/code_abc123?days=30" \
  -H "Authorization: Bearer lyn1_your_token_here"

Aggregated rather than raw: totals, unique visitors, a daily series and a country breakdown. Individual scan records are never returned, and no IP address is stored to return.

Errors

Every failure returns the same shape. Match on code, never on the sentence — the wording will be improved, the code will not change.

{
  "error": {
    "code": "forbidden",
    "message": "This token does not have the `codes:write` scope.",
    "docs": "https://lynkarr.com/docs/api"
  }
}
StatusCodeMeaning
400invalid_requestThe request was malformed or asked for something impossible.
401unauthorizedMissing, unknown, revoked or expired token.
402plan_requiredThe plan does not include this, or an allowance is used up.
403forbiddenThe token is valid but lacks the scope.
404not_foundNo such object for this company.
429rate_limitedToo many requests. Honour Retry-After.

A revoked token, an unknown token and one belonging to a suspended company all return the same 401. Distinguishing them would make this endpoint a way to test whether a guessed token exists.

Rate limit

120 requests per minute per token. Exceeding it returns 429 with Retry-After in seconds. The limit protects the platform rather than metering you; if a legitimate integration needs more, ask.

Versioning

The version is in the path. /api/v1 will not change shape beneath you: fields may be added, but nothing that exists today will be removed or repurposed. A breaking change would arrive as /api/v2.

Create a token →