Loading…

ForeverQR API

A small REST API for your personal codes: list them, read one, rename, pause or retarget a Pro code, and read its scans. The API can't create codes — create them in the app.

Base URL: https://foreverqr.ca/api/public/v1 · OpenAPI 3.1 (openapi.json)

Authentication

Create a key in Settings → API keys (up to 10 active keys). The full key (fqr_ + 8 characters + _ + 32 characters) is shown once; we store only a SHA-256 hash. Send it as Authorization: Bearer fqr_…, or as an x-api-key header. Revoked keys stop working immediately. Keys reach your personal codes only (not workspace codes).

Scopes

  • codes:read — GET /codes, GET /codes/{id}
  • codes:write — PATCH /codes/{id}
  • scans:read — GET /codes/{id}/scans

Rate limits

120 requests per minute per key. Over that you get 429 rate_limited; wait a minute and retry. CORS is open (*).

Endpoints

MethodPathScopeWhat it does
GET/codescodes:readList your codes, newest first
GET/codes/{id}codes:readGet one code
PATCH/codes/{id}codes:writeRename, pause/resume, or retarget a Pro code
GET/codes/{id}/scansscans:readRecent scans for one code, newest first

List codes — GET /codes

Query: limit (1–200, default 50), offset (default 0), folder (exact). Returns { data: Code[], limit, offset }.

curl -H "Authorization: Bearer $FQR_KEY" \
  "https://foreverqr.ca/api/public/v1/codes?limit=20"

Get a code — GET /codes/:id

Returns { data: Code }. A Code has id, name, kind ("pro" | "static"), short_code, short_link (Pro: https://foreverqr.ca/r/…; static: null), destination_url, is_active, folder, created_at, updated_at.

curl -H "Authorization: Bearer $FQR_KEY" \
  "https://foreverqr.ca/api/public/v1/codes/CODE_ID"

Update a code — PATCH /codes/:id

JSON body with any of: name (1–120 chars), is_active (boolean; false pauses the code), destination_url (http/https, Pro codes only — static codes return 409 static_code). Destination changes appear in the code's audit log. Returns the updated { data: Code }.

curl -X PATCH -H "Authorization: Bearer $FQR_KEY" \
  -H "Content-Type: application/json" \
  -d '{"destination_url":"https://example.com/spring-menu"}' \
  "https://foreverqr.ca/api/public/v1/codes/CODE_ID"
curl -X PATCH -H "x-api-key: $FQR_KEY" \
  -H "Content-Type: application/json" \
  -d '{"is_active":false}' \
  "https://foreverqr.ca/api/public/v1/codes/CODE_ID"

Scans — GET /codes/:id/scans

Query: since (ISO date-time), limit (1–1000, default 100). Returns { data: [{ created_at, country, city, device, os, browser }], limit }, newest first. Bot and link-preview hits aren't recorded as scans.

curl -H "Authorization: Bearer $FQR_KEY" \
  "https://foreverqr.ca/api/public/v1/codes/CODE_ID/scans?since=2026-10-01T00:00:00Z&limit=500"

Errors

Errors are JSON: { "error": { "code": "…", "message": "…" } }.

StatuscodeWhen
400bad_requestPATCH body isn't JSON, has an invalid field, or has nothing to update.
401unauthorizedMissing, malformed, unknown or revoked API key.
403forbiddenThe key lacks the scope this endpoint needs.
404not_foundNo such code, or it isn't one of your personal codes (also for a non-UUID id).
409static_codePATCH destination_url on a static code — static codes encode their content directly.
429rate_limitedMore than 120 requests in a minute for this key.
500server_errorWe couldn't list, read or update right now. Retry later.
503not_readyThe API is temporarily unavailable.

Not in the API

Creating or deleting codes, workspace codes, and styling. Scan webhooks are set per code on its page in the app.