QRTY

QRTY API

A REST API for dynamic multi-link QR codes, organizations, SSO/SCIM provisioning, webhooks, and scan analytics. Base URL: https://api.qrty.page

30-second quickstart

  1. Create an API key in Settings > API Keys.
  2. Copy the full key (shown once) — it starts with qrty_live_.
  3. Make your first request (see the code panel →).

Authentication

Protected endpoints accept a Bearer token in the Authorization header. QRTY supports three credential types.

API keys

Keys look like qrty_live_ followed by 32 hex characters. Only the prefix and a SHA-256 hash are stored server-side — you'll see the full key once.

Each key carries one or more permissions:

  • read — list and fetch
  • create — create QR codes and folders
  • update — edit metadata, style, links, tags
  • delete — delete resources
  • analytics — read scan analytics

Session JWT

Issued to users signed in via the web app (Auth0 or enterprise OIDC). Required for managing orgs, SSO, SCIM, and webhooks.

SCIM bearer token

A per-org token used only by your IdP against /scim/v2/*. Rotatable from the org SSO settings.

QR codes

The core resource. A QR code points to one or more links; the short URL stays the same even when you edit or reorder targets.

POST/api/qrapi-key · create

Create a QR code

Creates a dynamic QR code with one or more target links. The `links` array is required; everything else is optional.

Headers

NameTypeDescription
AuthorizationrequiredstringBearer qrty_live_<32-hex>
Content-Typerequiredstringapplication/json

Body parameters

NameTypeDescription
linksrequiredarrayArray of link objects ({ url, title?, isActive?, scheduledStart?, scheduledEnd? }).
titlestringDisplay title shown in your dashboard.
ttlnumberTime-to-live in seconds. Omit for permanent.
folderIdstringPlace the QR inside an existing folder.
tagsstring[]Tag labels.
styleobject{ foregroundColor, backgroundColor, dotStyle }.
Request
curl -X POST https://api.qrty.page/api/qr \
  -H "Authorization: Bearer qrty_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "links": [{"url": "https://example.com", "title": "Example"}],
    "ttl": 2592000,
    "title": "Campaign Q2",
    "style": {
      "foregroundColor": "#1a1a1a",
      "backgroundColor": "#ffffff",
      "dotStyle": "rounded"
    }
  }'
Returns the created QR resource. `qrDataUrl` is a base64-encoded SVG data URL you can embed directly; `viewUrl` is the public short URL. `expiresAt` is present only when `ttl` is set.
{
  "id": "abc12345",
  "qrUrl": "https://api.qrty.page/api/qr/abc12345",
  "qrDataUrl": "data:image/svg+xml;base64,PHN2ZyB4bWxucz0...",
  "viewUrl": "https://api.qrty.page/l/abc12345",
  "expiresAt": "2026-06-01T09:25:46.011Z"
}
GET/api/qr/:idpublic

Get QR image

Public. Returns the QR code rendered as an SVG.

Path parameters

NameTypeDescription
idrequiredstringShort QR id.

Query parameters

NameTypeDescription
sizenumberPixel width/height. Default 256.
Request
curl https://api.qrty.page/api/qr/abc12345?size=512 -o qr.svg
SVG image of the QR code.
<svg ...>...</svg>
PATCH/api/qr/:idapi-key · update

Update QR metadata

Headers

NameTypeDescription
AuthorizationrequiredstringBearer qrty_live_<32-hex>

Path parameters

NameTypeDescription
idrequiredstringQR id.

Body parameters

NameTypeDescription
titlestringNew title.
ttlnumberNew TTL in seconds.
folderIdstringMove to folder.
Request
curl -X PATCH https://api.qrty.page/api/qr/abc12345 \
  -H "Authorization: Bearer qrty_live_..." \
  -H "Content-Type: application/json" \
  -d '{ "title": "Renamed" }'
Updated QR resource.
{ "id": "abc12345", "title": "Renamed" }
DELETE/api/qr/:idapi-key · delete

Delete a QR code

Headers

NameTypeDescription
AuthorizationrequiredstringBearer qrty_live_<32-hex>

Path parameters

NameTypeDescription
idrequiredstringQR id.
Request
curl -X DELETE https://api.qrty.page/api/qr/abc12345 \
  -H "Authorization: Bearer qrty_live_..."
Deleted.
{ "success": true }
GET/api/qr/:id/analyticsapi-key · analytics

Scan & click analytics

Headers

NameTypeDescription
AuthorizationrequiredstringBearer qrty_live_<32-hex>

Path parameters

NameTypeDescription
idrequiredstringQR id.
Request
curl https://api.qrty.page/api/qr/abc12345/analytics \
  -H "Authorization: Bearer qrty_live_..."
QR metadata plus an `analytics` block with per-day scans, per-country counts, and per-link click counts.
{
  "id": "abc12345",
  "title": "Campaign Q2",
  "links": [{ "url": "https://example.com", "title": "Example" }],
  "createdAt": "2026-05-02T10:00:00Z",
  "expiresAt": "2026-06-01T10:00:00Z",
  "analytics": {
    "totalScans": 128,
    "scansByDay": [{ "date": "2026-05-02", "count": 42 }],
    "scansByCountry": [{ "country": "US", "count": 60 }],
    "clicksByLink": [{ "linkIndex": 0, "url": "https://example.com", "clicks": 94 }]
  }
}
PATCH/api/qr/:id/styleapi-key · update

Update QR style

Headers

NameTypeDescription
AuthorizationrequiredstringBearer qrty_live_<32-hex>

Path parameters

NameTypeDescription
idrequiredstringQR id.

Body parameters

NameTypeDescription
foregroundColorstringHex color, e.g. #1a1a1a.
backgroundColorstringHex color.
dotStyle'square' | 'rounded' | 'dots'Module style.
Updated style.
POST/api/qr/:id/passwordapi-key · update

Set password

Require a password before the short URL resolves to the link list.

Headers

NameTypeDescription
AuthorizationrequiredstringBearer qrty_live_<32-hex>

Body parameters

NameTypeDescription
passwordrequiredstringPlaintext; hashed server-side.
Password set.
DELETE/api/qr/:id/passwordapi-key · update

Remove password

Headers

NameTypeDescription
AuthorizationrequiredstringBearer qrty_live_<32-hex>
Removed.
{ "success": true }
POST/api/qr/:id/verify-passwordpublic

Verify password

Public. On success returns the unlocked link list directly — no token is issued.

Body parameters

NameTypeDescription
passwordrequiredstringUser-supplied password.
Correct password. Response is the unlocked link list.
{ "links": [{ "url": "https://example.com", "title": "Example" }] }
GET/api/dashboardapi-key · read

List your QR codes

Headers

NameTypeDescription
AuthorizationrequiredstringBearer qrty_live_<32-hex>

Query parameters

NameTypeDescription
folderIdstringFilter by folder.
tagstringFilter by tag.
searchstringSubstring match against title.
List of QR codes.
{
  "qrCodes": [
    { "id": "...", "title": "...", "links": [ ... ], "createdAt": "...", "expiresAt": "..." }
  ],
  "totalQRCodes": 42,
  "totalScans": 1234
}

Folders, tags & bulk ops

POST/api/foldersapi-key · create

Create folder

Headers

NameTypeDescription
AuthorizationrequiredstringBearer qrty_live_<32-hex>

Body parameters

NameTypeDescription
namerequiredstringFolder name.
Created folder.
{
  "id": "fld_...",
  "userId": "user_...",
  "name": "Marketing",
  "createdAt": "2026-05-02T12:34:56.000Z"
}
GET/api/foldersapi-key · read

List folders

Headers

NameTypeDescription
AuthorizationrequiredstringBearer qrty_live_<32-hex>
All folders with QR counts.
{
  "folders": [
    {
      "id": "fld_...",
      "userId": "user_...",
      "name": "Marketing",
      "createdAt": "2026-05-02T12:34:56.000Z",
      "qrCount": 7
    }
  ]
}
PATCH/api/folders/:idapi-key · update

Rename folder

Headers

NameTypeDescription
AuthorizationrequiredstringBearer qrty_live_<32-hex>
Missing or invalid credentials.
{ "error": "Unauthorized" }
DELETE/api/folders/:idapi-key · delete

Delete folder

Contents become unfiled (not deleted).

Headers

NameTypeDescription
AuthorizationrequiredstringBearer qrty_live_<32-hex>
Missing or invalid credentials.
{ "error": "Unauthorized" }
POST/api/qr/:id/folderapi-key · update

Move QR to folder

Headers

NameTypeDescription
AuthorizationrequiredstringBearer qrty_live_<32-hex>

Body parameters

NameTypeDescription
folderIdstring | nullnull to unfile.
Missing or invalid credentials.
{ "error": "Unauthorized" }
GET/api/tagsapi-key · read

List tags

Headers

NameTypeDescription
AuthorizationrequiredstringBearer qrty_live_<32-hex>
Array of unique tag strings.
{ "tags": ["launch", "campaign-q2", "promo"] }
PATCH/api/qr/:id/tagsapi-key · update

Set tags

Headers

NameTypeDescription
AuthorizationrequiredstringBearer qrty_live_<32-hex>

Body parameters

NameTypeDescription
tagsrequiredstring[]Replaces the full tag set.
Missing or invalid credentials.
{ "error": "Unauthorized" }
POST/api/bulk/deleteapi-key · delete

Bulk delete QR codes

Headers

NameTypeDescription
AuthorizationrequiredstringBearer qrty_live_<32-hex>

Body parameters

NameTypeDescription
idsrequiredstring[]Array of QR ids.
Deleted count.
{ "success": true, "deleted": 3 }
POST/api/bulk/moveapi-key · update

Bulk move QR codes

Headers

NameTypeDescription
AuthorizationrequiredstringBearer qrty_live_<32-hex>

Body parameters

NameTypeDescription
idsrequiredstring[]QR ids.
folderIdrequiredstring | nullDestination folder.
Missing or invalid credentials.
{ "error": "Unauthorized" }

Organizations

Role hierarchy: owner > admin > editor > viewer. Most endpoints accept either the org id or slug as :orgId. Accepts either an API key or a session JWT.

POST/api/orgsapi-key

Create an organization

Caller becomes `owner`.

Headers

NameTypeDescription
AuthorizationrequiredstringBearer qrty_live_<32-hex>

Body parameters

NameTypeDescription
namerequiredstringDisplay name (min 2 chars).
slugrequiredstringURL-safe slug. Must be unique.
Request
curl -X POST https://api.qrty.page/api/orgs \
  -H "Authorization: Bearer qrty_live_..." \
  -H "Content-Type: application/json" \
  -d '{ "name": "Acme Inc", "slug": "acme" }'
Created org.
{
  "id": "org_...",
  "name": "Acme Inc",
  "slug": "acme",
  "createdAt": "2026-05-02T12:34:56.000Z",
  "createdBy": "user_...",
  "updatedAt": "2026-05-02T12:34:56.000Z"
}
GET/api/orgsapi-key

List your orgs

Headers

NameTypeDescription
AuthorizationrequiredstringBearer qrty_live_<32-hex>
Missing or invalid credentials.
{ "error": "Unauthorized" }
GET/api/orgs/:orgIdapi-key

Get org (with members)

Headers

NameTypeDescription
AuthorizationrequiredstringBearer qrty_live_<32-hex>
Missing or invalid credentials.
{ "error": "Unauthorized" }
PATCH/api/orgs/:orgIdapi-key

Update org

Headers

NameTypeDescription
AuthorizationrequiredstringBearer qrty_live_<32-hex>

Body parameters

NameTypeDescription
namestringNew name.
slugstringNew slug.
logoUrlstringLogo URL.
Missing or invalid credentials.
{ "error": "Unauthorized" }
DELETE/api/orgs/:orgIdapi-key

Delete org (owner only)

Headers

NameTypeDescription
AuthorizationrequiredstringBearer qrty_live_<32-hex>
Deleted.
{ "success": true }
POST/api/orgs/:orgId/inviteapi-key

Invite a member

Sends an invite email via Resend.

Headers

NameTypeDescription
AuthorizationrequiredstringBearer qrty_live_<32-hex>

Body parameters

NameTypeDescription
emailrequiredstringInvitee email.
rolerequired'admin' | 'editor' | 'viewer'Role at acceptance.
Invite created and emailed.
{ "id": "inv_...", "email": "a@b.com", "role": "editor" }
POST/api/orgs/:orgId/invites/:inviteId/acceptapi-key

Accept invite

Headers

NameTypeDescription
AuthorizationrequiredstringBearer qrty_live_<32-hex>
Missing or invalid credentials.
{ "error": "Unauthorized" }
PATCH/api/orgs/:orgId/members/:memberIdapi-key

Change a role (owner only)

Headers

NameTypeDescription
AuthorizationrequiredstringBearer qrty_live_<32-hex>
Missing or invalid credentials.
{ "error": "Unauthorized" }
DELETE/api/orgs/:orgId/members/:memberIdapi-key

Remove member

Headers

NameTypeDescription
AuthorizationrequiredstringBearer qrty_live_<32-hex>
Missing or invalid credentials.
{ "error": "Unauthorized" }
GET/api/orgs/:orgId/activityapi-key

Activity log

Headers

NameTypeDescription
AuthorizationrequiredstringBearer qrty_live_<32-hex>

Query parameters

NameTypeDescription
limitnumberDefault 50.
offsetnumberDefault 0.
Missing or invalid credentials.
{ "error": "Unauthorized" }
GET/api/orgs/:orgId/dashboardapi-key

Org-wide dashboard

Headers

NameTypeDescription
AuthorizationrequiredstringBearer qrty_live_<32-hex>
Missing or invalid credentials.
{ "error": "Unauthorized" }

SSO (OIDC)

Per-organization SSO using any OpenID Connect provider (Okta, Azure AD, Auth0, Google Workspace, etc.). Admin-only.

PUT/api/orgs/:orgId/sso/oidcapi-key

Configure OIDC

Create or update the org OIDC config. Response includes the redirect URIs you need to register with your IdP.

Headers

NameTypeDescription
AuthorizationrequiredstringBearer qrty_live_<32-hex>

Body parameters

NameTypeDescription
issuerrequiredstringIdP issuer URL.
clientIdrequiredstringOAuth client id.
clientSecretrequiredstringOAuth client secret.
scopesstringSpace-separated scopes. Default: "openid profile email".
enabledbooleanToggle SSO on/off.
allowJitbooleanJust-in-time provisioning.
defaultRole'admin' | 'editor' | 'viewer'Role assigned to JIT-provisioned users.
Request
curl -X PUT https://api.qrty.page/api/orgs/acme/sso/oidc \
  -H "Authorization: Bearer <session-jwt>" \
  -H "Content-Type: application/json" \
  -d '{
    "issuer": "https://your-tenant.okta.com",
    "clientId": "0oa...",
    "clientSecret": "...",
    "scopes": "openid profile email",
    "enabled": true,
    "allowJit": true,
    "defaultRole": "viewer"
  }'
Updated config + redirect URIs.
{
  "config": { "issuer": "...", "clientId": "...", "enabled": true },
  "redirectUris": [
    "https://api.qrty.page/api/auth/oidc/callback",
    "https://api.qrty.page/api/auth/oidc/initiate",
    "https://api.qrty.page/api/auth/oidc/login?org=acme",
    "https://api.qrty.page/api/auth/oidc/universal-logout/acme"
  ]
}
GET/api/auth/oidc/loginpublic

Start a user-initiated login

Query parameters

NameTypeDescription
orgrequiredstringOrg slug.
return_tostringPath to land on after login.
Rate limit exceeded.
{ "error": "Too Many Requests", "retryAfter": 45 }
GET/api/auth/oidc/initiatepublic

IdP-initiated login

Query parameters

NameTypeDescription
issrequiredstringIdP issuer.
orgstringOrg hint.
target_link_uristringDeep-link target.
Rate limit exceeded.
{ "error": "Too Many Requests", "retryAfter": 45 }
GET/api/auth/oidc/callbackpublic

OIDC callback

Query parameters

NameTypeDescription
coderequiredstringAuthorization code.
staterequiredstringState parameter.
Rate limit exceeded.
{ "error": "Too Many Requests", "retryAfter": 45 }
POST/api/auth/oidc/sessionpublic

Exchange code → session JWT

Body parameters

NameTypeDescription
coderequiredstringShort-lived code from /callback.
Session issued.
{ "token": "<jwt>", "user": { "id": "...", "email": "..." } }
POST/api/auth/oidc/universal-logout/:orgIdpublic

Okta Universal Logout

Revokes all sessions for the org. Called by Okta.

Rate limit exceeded.
{ "error": "Too Many Requests", "retryAfter": 45 }

Redirect URIs to register

  • https://api.qrty.page/api/auth/oidc/callback
  • https://api.qrty.page/api/auth/oidc/initiate
  • https://api.qrty.page/api/auth/oidc/login?org={slug}
  • https://api.qrty.page/api/auth/oidc/universal-logout/{slug}

SCIM 2.0 provisioning

Automate user and group lifecycle from your IdP (Okta, Azure AD, OneLogin). Endpoints are compliant with RFC 7644.

Base URL: https://api.qrty.page/scim/v2/{orgIdOrSlug}. Use the SCIM bearer token in the Authorization header on every request.

PUT/api/orgs/:orgId/sso/scimapi-key

Enable SCIM / rotate token

Response contains the new token only once — store it in your IdP immediately.

Headers

NameTypeDescription
AuthorizationrequiredstringBearer qrty_live_<32-hex>

Body parameters

NameTypeDescription
enabledrequiredbooleanEnable or disable SCIM.
rotateTokenbooleanIssue a new bearer token.
Request
curl -X PUT https://api.qrty.page/api/orgs/acme/sso/scim \
  -H "Authorization: Bearer <session-jwt>" \
  -H "Content-Type: application/json" \
  -d '{ "enabled": true, "rotateToken": true }'
Enabled. Includes baseUrl + new token (shown only on this response).
{
  "scim": {
    "id": "...",
    "orgId": "...",
    "enabled": true,
    "tokenPrefix": "qrty_scim_...",
    "tokenConfigured": true,
    "createdAt": "2026-05-02T09:40:10.152Z",
    "updatedAt": "2026-05-02T09:40:10.152Z"
  },
  "baseUrl": "https://api.qrty.page/scim/v2/acme",
  "token": "qrty_scim_<64-hex-char-token>"
}
GET/scim/v2/:orgId/ServiceProviderConfigscim token

Service provider config

Headers

NameTypeDescription
AuthorizationrequiredstringBearer <scim bearer token>
Missing or invalid credentials.
{ "error": "Unauthorized" }
GET/scim/v2/:orgId/Schemasscim token

Supported schemas

Headers

NameTypeDescription
AuthorizationrequiredstringBearer <scim bearer token>
Missing or invalid credentials.
{ "error": "Unauthorized" }
GET/scim/v2/:orgId/ResourceTypesscim token

Supported resource types

Headers

NameTypeDescription
AuthorizationrequiredstringBearer <scim bearer token>
Missing or invalid credentials.
{ "error": "Unauthorized" }
GET/scim/v2/:orgId/Usersscim token

List users

Headers

NameTypeDescription
AuthorizationrequiredstringBearer <scim bearer token>

Query parameters

NameTypeDescription
filterstringSCIM filter, e.g. userName eq "a@b.com".
startIndexnumber1-based.
countnumberPage size.
Missing or invalid credentials.
{ "error": "Unauthorized" }
POST/scim/v2/:orgId/Usersscim token

Create user

Headers

NameTypeDescription
AuthorizationrequiredstringBearer <scim bearer token>
Content-Typerequiredstringapplication/scim+json

Body parameters

NameTypeDescription
userNamerequiredstringUnique username (usually email).
emailsrequiredarrayAt least one email, with `primary: true`.
nameobject{ givenName, familyName }.
activebooleanDefault true.
externalIdstringIdP-side id.
Request
curl -X POST https://api.qrty.page/scim/v2/acme/Users \
  -H "Authorization: Bearer <scim-token>" \
  -H "Content-Type: application/scim+json" \
  -d '{
    "schemas": ["urn:ietf:params:scim:schemas:core:2.0:User"],
    "userName": "alice@acme.com",
    "emails": [{ "primary": true, "value": "alice@acme.com" }],
    "active": true
  }'
User created.
{ "id": "...", "userName": "alice@acme.com", "active": true }
GET/scim/v2/:orgId/Users/:userIdscim token

Get user

Headers

NameTypeDescription
AuthorizationrequiredstringBearer <scim bearer token>
Missing or invalid credentials.
{ "error": "Unauthorized" }
PUT/scim/v2/:orgId/Users/:userIdscim token

Replace user

Headers

NameTypeDescription
AuthorizationrequiredstringBearer <scim bearer token>
Missing or invalid credentials.
{ "error": "Unauthorized" }
PATCH/scim/v2/:orgId/Users/:userIdscim token

Partial user update

Headers

NameTypeDescription
AuthorizationrequiredstringBearer <scim bearer token>
Missing or invalid credentials.
{ "error": "Unauthorized" }
DELETE/scim/v2/:orgId/Users/:userIdscim token

Deactivate user

Headers

NameTypeDescription
AuthorizationrequiredstringBearer <scim bearer token>
Missing or invalid credentials.
{ "error": "Unauthorized" }
GET/scim/v2/:orgId/Groupsscim token

List groups

Headers

NameTypeDescription
AuthorizationrequiredstringBearer <scim bearer token>
Missing or invalid credentials.
{ "error": "Unauthorized" }
POST/scim/v2/:orgId/Groupsscim token

Create group

Headers

NameTypeDescription
AuthorizationrequiredstringBearer <scim bearer token>

Body parameters

NameTypeDescription
displayNamerequiredstringGroup name.
membersarray[{ value: userId }]
Missing or invalid credentials.
{ "error": "Unauthorized" }
GET/scim/v2/:orgId/Groups/:groupIdscim token

Get group

Headers

NameTypeDescription
AuthorizationrequiredstringBearer <scim bearer token>
Missing or invalid credentials.
{ "error": "Unauthorized" }
PUT/scim/v2/:orgId/Groups/:groupIdscim token

Replace group

Headers

NameTypeDescription
AuthorizationrequiredstringBearer <scim bearer token>
Missing or invalid credentials.
{ "error": "Unauthorized" }
PATCH/scim/v2/:orgId/Groups/:groupIdscim token

Partial group update

Headers

NameTypeDescription
AuthorizationrequiredstringBearer <scim bearer token>
Missing or invalid credentials.
{ "error": "Unauthorized" }
DELETE/scim/v2/:orgId/Groups/:groupIdscim token

Delete group

Headers

NameTypeDescription
AuthorizationrequiredstringBearer <scim bearer token>
Missing or invalid credentials.
{ "error": "Unauthorized" }

Webhooks

Receive real-time event notifications at your URL. Management endpoints require a session JWT — they cannot be managed with API keys.

Events

  • qr.created
  • qr.updated
  • qr.deleted
  • qr.scanned
  • link.clicked

Delivery headers

  • X-QRTY-Event — event type
  • X-QRTY-Signature — hex HMAC-SHA256 of raw body, using your webhook secret
  • Content-Type: application/json
POST/api/webhookssession jwt

Create webhook

Headers

NameTypeDescription
AuthorizationrequiredstringBearer <session jwt>

Body parameters

NameTypeDescription
urlrequiredstringHTTPS endpoint.
eventsrequiredstring[]One or more event names.
Request
curl -X POST https://api.qrty.page/api/webhooks \
  -H "Authorization: Bearer <session-jwt>" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://example.com/hooks/qrty",
    "events": ["qr.scanned", "link.clicked"]
  }'
Subscription created. `secret` is shown once.
{ "id": "wh_123", "url": "...", "secret": "<only-shown-once>", "events": ["qr.scanned"] }
GET/api/webhookssession jwt

List webhooks

Headers

NameTypeDescription
AuthorizationrequiredstringBearer <session jwt>
Missing or invalid credentials.
{ "error": "Unauthorized" }
PATCH/api/webhooks/:idsession jwt

Update webhook

Headers

NameTypeDescription
AuthorizationrequiredstringBearer <session jwt>
Missing or invalid credentials.
{ "error": "Unauthorized" }
DELETE/api/webhooks/:idsession jwt

Delete webhook

Headers

NameTypeDescription
AuthorizationrequiredstringBearer <session jwt>
Missing or invalid credentials.
{ "error": "Unauthorized" }
GET/api/webhooks/:id/deliveriessession jwt

Recent deliveries

Headers

NameTypeDescription
AuthorizationrequiredstringBearer <session jwt>
Missing or invalid credentials.
{ "error": "Unauthorized" }
POST/api/webhooks/:id/testsession jwt

Send a test payload

Headers

NameTypeDescription
AuthorizationrequiredstringBearer <session jwt>
Missing or invalid credentials.
{ "error": "Unauthorized" }

Example payload

json
{
  "event": "qr.scanned",
  "timestamp": "2026-05-02T12:00:00Z",
  "data": {
    "qr_id": "abc12345",
    "country": "US",
    "device": "mobile"
  }
}

Rate limits

Limits are per-IP, per-endpoint, with a 60-second sliding window.

EndpointLimit
POST /api/qr10 / min
GET /api/qr/*60 / min
GET /l/*120 / min
(default)100 / min

Response headers

  • X-RateLimit-Limit — cap for the window
  • X-RateLimit-Remaining — requests left
  • X-RateLimit-Reset — Unix seconds until reset
  • Retry-After — seconds to wait (sent on 429)

Errors

All error responses use a consistent JSON shape: { "error": "..." }. Some endpoints add a message. Status codes follow HTTP conventions.

StatusMeaning
400Validation or malformed request
401Missing or invalid credentials
403Authenticated but not permitted
404Resource not found
409Conflict (e.g. duplicate slug)
429Rate limit exceeded
500Server error

Changelog

2026-05-02

  • Launched public API documentation at /docs.
  • Added SSO (OIDC) and SCIM 2.0 provisioning reference.
  • Added language tabs (cURL, JS, Python) and response tabs (200/4xx/5xx).