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.
| Scope | Allows |
|---|---|
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
| Method | Path | Scope | |
|---|---|---|---|
| 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"
}
}
| Status | Code | Meaning |
|---|---|---|
| 400 | invalid_request | The request was malformed or asked for something impossible. |
| 401 | unauthorized | Missing, unknown, revoked or expired token. |
| 402 | plan_required | The plan does not include this, or an allowance is used up. |
| 403 | forbidden | The token is valid but lacks the scope. |
| 404 | not_found | No such object for this company. |
| 429 | rate_limited | Too 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.