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/v1Authentication
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.
| Plan | Requests / month | Hourly cap |
|---|---|---|
| Pro | ~2,500 | 250 |
| Agency | ~100,000 | 10,000 |
Create a QR code
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
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
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" }| Status | Meaning |
|---|---|
| 401 | Missing or invalid API key |
| 403 | Key valid but not permitted for this action |
| 404 | QR code not found in your workspace |
| 422 | Validation failed, or plan limit reached |
| 429 | Rate limit exceeded — retry after the hour resets |
| 500 | Something failed on our end — safe to retry |