API documentation

Create QR codes, update destinations, and pull analytics programmatically. Included on every paid plan — not an upsell.

Overview

The Relay API is a plain REST API over HTTPS. Every response is JSON. All endpoints live under a single base URL:

https://api.relaycodes.com/functions/v1

Authentication

Every request needs an API key in the Authorization header. Generate a key from Settings → API Keys in your workspace — it's shown once, so store it somewhere safe.

Authorization: Bearer sk_live_...

Keys are scoped to a single workspace and inherit that workspace's plan limits. Revoke a compromised key from the same settings page — revocation is immediate.

Rate limits

Limits are enforced hourly and reset on the hour. Exceeding your plan's request volume returns a 429.

PlanRequests / monthHourly cap
Pro~2,500250
Agency~100,00010,000

Create a QR code

POST/api-qr-create

Creates a new dynamic QR code in your workspace. Fails with a 422 if you're at your plan's QR code limit.

Request body

{
  "destination_url": "https://example.com/spring-menu",
  "title": "Spring Menu — Table Tents",
  "folder": "Spring Campaign",
  "style_config": {
    "fgColor": "#0C0C0E",
    "bgColor": "#FFFFFF",
    "qrStyle": "dots",
    "eyeRadius": 8
  }
}

Response — 201 Created

{
  "data": {
    "id": "b3f1c9...",
    "slug": "spring-menu",
    "destination_url": "https://example.com/spring-menu",
    "title": "Spring Menu — Table Tents",
    "created_at": "2026-07-01T09:14:00Z"
  }
}

curl

curl -X POST https://api.relaycodes.com/functions/v1/api-qr-create \
  -H "Authorization: Bearer sk_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "destination_url": "https://example.com/spring-menu",
    "title": "Spring Menu — Table Tents"
  }'

Update a QR code

PATCH/api-qr-update/:id

Updates the destination, title, or active state of an existing QR code. Set is_active to false to freeze the redirect — the QR image and slug keep working, but scans land on a neutral page instead of your destination.

Request body

{
  "destination_url": "https://example.com/summer-menu",
  "title": "Summer Menu — Table Tents",
  "is_active": true
}

Response — 200 OK

{
  "data": {
    "id": "b3f1c9...",
    "slug": "spring-menu",
    "destination_url": "https://example.com/summer-menu",
    "title": "Summer Menu — Table Tents",
    "is_active": true,
    "updated_at": "2026-07-01T09:20:11Z"
  }
}

curl

curl -X PATCH https://api.relaycodes.com/functions/v1/api-qr-update/b3f1c9... \
  -H "Authorization: Bearer sk_live_..." \
  -H "Content-Type: application/json" \
  -d '{ "destination_url": "https://example.com/summer-menu" }'

Get analytics

GET/api-qr-analytics/:id/analytics?days=30

Returns scan totals and breakdowns for a QR code. The days parameter defaults to 30 and is capped by plan: 30 days on Free, 1 year on Pro, 2 years on Agency. Free-plan workspaces also get a narrower geo breakdown — top 3 countries instead of the top 20 on paid plans.

Response — 200 OK

{
  "data": {
    "total": 1284,
    "daily": [{ "day": "2026-06-30", "scans": 42 }],
    "geo": [{ "country": "France", "scans": 610 }],
    "device": [{ "device": "Mobile", "scans": 1102 }],
    "os": [{ "os": "iOS", "scans": 780 }],
    "browser": [{ "browser": "Safari", "scans": 690 }]
  }
}

curl

curl https://api.relaycodes.com/functions/v1/api-qr-analytics/b3f1c9.../analytics?days=30 \
  -H "Authorization: Bearer sk_live_..."

Errors

Errors return a non-2xx status with a consistent shape:

{ "error": "Destination URL is required" }
StatusMeaning
401Missing or invalid API key
403Key valid but not permitted for this action
404QR code not found in your workspace
422Validation failed, or plan limit reached
429Rate limit exceeded — retry after the hour resets
500Something failed on our end — safe to retry