API Reference
Build powerful integrations with the Referree API. Create short links, generate QR codes, manage custom domains, and track analytics programmatically.
https://proxy.referree.com/api/v1All requests and responses use application/json unless otherwise noted.
Authentication
Authenticate API requests using a Bearer token. Create API keys in your dashboard settings.
API keys follow the format ref_live_sk_{32 hex chars} for production and ref_test_sk_... for sandbox.
curl https://proxy.referree.com/api/v1/links \
-H "Authorization: Bearer ref_live_sk_a1b2c3d4..."Agency Delegation
Agency accounts can operate on behalf of a managed brand by including the X-Brand-Account header with the brand's account ID. This is required when accessing the brand's custom domains, links, and other assets.
Without this header, all requests operate on the agency's own account. The brand must have an accepted connection with the agency (configured via Settings > Managed Brands).
# List the brand's links (not your own)
curl https://proxy.referree.com/api/v1/links \
-H "Authorization: Bearer ref_live_sk_a1b2c3d4..." \
-H "X-Brand-Account: 6623fae7-fa3f-49ed-b139-14d057c0fe9c"
# List the brand's custom domains
curl https://proxy.referree.com/api/v1/domains \
-H "Authorization: Bearer ref_live_sk_a1b2c3d4..." \
-H "X-Brand-Account: 6623fae7-fa3f-49ed-b139-14d057c0fe9c"Rate Limits
API rate limits are based on your subscription tier and reset on the first of each month.
| Tier | Monthly Limit |
|---|---|
| Pro | 5,000 requests |
| Business | 20,000 requests |
| Scale | 50,000 requests |
When you exceed the limit, the API returns 429 Too Many Requests. Rate limit info is included in response headers:
| Parameter | Type | Description |
|---|---|---|
X-RateLimit-Limit | number | Your monthly request limit |
X-RateLimit-Remaining | number | Requests remaining this month |
X-RateLimit-Reset | timestamp | When the limit resets (ISO 8601) |
Links
/linksCreate a new short link.
| Parameter | Type | Description |
|---|---|---|
destination_urlrequired | string | The URL to redirect to |
short_code | string | Custom back-half (auto-generated if omitted) |
custom_domain_id | string | UUID of a verified custom domain |
is_dynamic | boolean | Whether the destination can be changed later (default: true) |
og_title | string | Open Graph title for social previews |
og_description | string | Open Graph description |
og_image_url | string | Open Graph image URL |
metadata | object | Arbitrary key-value metadata (e.g. UTM params, campaign IDs) |
generate_qr | boolean | object | Generate QR codes inline. Pass true for defaults, or { formats: ["svg","png"], size, fgColor, bgColor } |
curl -X POST https://proxy.referree.com/api/v1/links \
-H "Authorization: Bearer ref_live_sk_a1b2c3d4..." \
-H "Content-Type: application/json" \
-d '{
"destination_url": "https://example.com/landing",
"short_code": "summer24",
"og_title": "Summer Sale",
"metadata": { "campaign": "summer-2024" }
}'Response 201 Created
{
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"short_code": "summer24",
"destination_url": "https://example.com/landing",
"domain": "referr.ee",
"short_url": "https://referr.ee/summer24",
"is_dynamic": true,
"qr_type": "url",
"scan_count": 0,
"og_title": "Summer Sale",
"og_description": null,
"og_image_url": null,
"custom_domain_id": null,
"metadata": { "campaign": "summer-2024" },
"qr_url_svg": "https://referr.ee/summer24.qr.svg",
"qr_url_png": "https://referr.ee/summer24.qr.png",
"created_at": "2026-02-24T12:00:00.000Z",
"updated_at": "2026-02-24T12:00:00.000Z"
}The short_url field contains the full short URL ready to share. The qr_url_svg and qr_url_png fields are public, CDN-cached QR code URLs you can use directly in HTML or email (e.g. <img src="...">). If no custom domain is specified, links use the default domain referr.ee.
Inline QR Generation
Pass generate_qr to receive QR codes in the link creation response — no separate API calls needed.
curl -X POST https://proxy.referree.com/api/v1/links \
-H "Authorization: Bearer ref_live_sk_a1b2c3d4..." \
-H "Content-Type: application/json" \
-d '{
"destination_url": "https://example.com/invite",
"generate_qr": true
}'
# With custom QR options:
# "generate_qr": { "formats": ["svg","png"], "size": 512, "fgColor": "#1a1a2e" }Response 201 Created with generate_qr
{
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"short_code": "summer24",
"destination_url": "https://example.com/invite",
"domain": "referr.ee",
"short_url": "https://referr.ee/summer24",
"is_dynamic": true,
"qr": {
"svg": "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 37 37\">...</svg>",
"png": "iVBORw0KGgoAAAANSUhEUg..."
},
"created_at": "2026-02-25T12:00:00.000Z"
}This collapses 3 API calls (create link + QR SVG + QR PNG) into a single request. Pass true to use your brand profile's colors and styling automatically, or pass an object to override specific options. The png field is base64-encoded binary data.
/linksList all links for your account with pagination.
| Parameter | Type | Description |
|---|---|---|
limit | number | Results per page (default: 50, max: 100) |
offset | number | Number of results to skip (default: 0) |
order_by | string | Sort field (default: "created_at") |
order | string | Sort direction: "asc" or "desc" (default: "desc") |
curl "https://proxy.referree.com/api/v1/links?limit=10&offset=0" \
-H "Authorization: Bearer ref_live_sk_a1b2c3d4..."Response
{
"data": [
{
"id": "a1b2c3d4-...",
"short_code": "summer24",
"destination_url": "https://example.com/landing",
"domain": "go.opulen.com",
"short_url": "https://go.opulen.com/summer24",
"qr_url_svg": "https://go.opulen.com/summer24.qr.svg",
"qr_url_png": "https://go.opulen.com/summer24.qr.png",
"scan_count": 47,
"custom_domain_id": "43738051-...",
"created_at": "2026-02-24T12:00:00.000Z",
...
}
],
"pagination": {
"limit": 10,
"offset": 0,
"total": 142
}
}/links/:idRetrieve a single link by ID.
curl https://proxy.referree.com/api/v1/links/a1b2c3d4-e5f6-7890-abcd-ef1234567890 \
-H "Authorization: Bearer ref_live_sk_a1b2c3d4..."/links/:idUpdate a link's destination, metadata, or Open Graph properties.
| Parameter | Type | Description |
|---|---|---|
destination_url | string | New redirect URL |
is_dynamic | boolean | Toggle dynamic/static |
og_title | string | Open Graph title |
og_description | string | Open Graph description |
og_image_url | string | Open Graph image URL |
metadata | object | Replace metadata object |
curl -X PATCH https://proxy.referree.com/api/v1/links/a1b2c3d4-... \
-H "Authorization: Bearer ref_live_sk_a1b2c3d4..." \
-H "Content-Type: application/json" \
-d '{ "destination_url": "https://example.com/new-page" }'/links/:idPermanently delete a link.
curl -X DELETE https://proxy.referree.com/api/v1/links/a1b2c3d4-... \
-H "Authorization: Bearer ref_live_sk_a1b2c3d4..."Response
{ "deleted": true, "id": "a1b2c3d4-..." }/links/:id/statsGet click analytics for a link, including device, browser, country, and referrer breakdowns.
curl https://proxy.referree.com/api/v1/links/a1b2c3d4-.../stats \
-H "Authorization: Bearer ref_live_sk_a1b2c3d4..."Response
{
"link_id": "a1b2c3d4-...",
"total_clicks": 1234,
"recent_clicks": 100,
"breakdown": {
"devices": { "desktop": 80, "mobile": 18, "tablet": 2 },
"browsers": { "Chrome": 60, "Safari": 30, "Firefox": 10 },
"countries": { "US": 70, "GB": 20, "CA": 10 },
"referrers": { "direct": 50, "twitter.com": 30, "linkedin.com": 20 }
},
"last_click": "2026-02-24T15:30:00.000Z"
}Bulk Links
/links/bulkCreate multiple links in a single request. Requires Business tier or higher. Maximum 100 links per request.
curl -X POST https://proxy.referree.com/api/v1/links/bulk \
-H "Authorization: Bearer ref_live_sk_a1b2c3d4..." \
-H "Content-Type: application/json" \
-d '{
"links": [
{ "destination_url": "https://example.com/page-1" },
{ "destination_url": "https://example.com/page-2", "short_code": "page2" },
{ "destination_url": "https://example.com/page-3" }
]
}'Response 201 Created
{
"created": 3,
"data": [
{ "id": "...", "short_code": "xK9mL2p", "domain": "referr.ee", "short_url": "https://referr.ee/xK9mL2p", ... },
{ "id": "...", "short_code": "page2", "domain": "referr.ee", "short_url": "https://referr.ee/page2", ... },
{ "id": "...", "short_code": "qR4nT8w", "domain": "referr.ee", "short_url": "https://referr.ee/qR4nT8w", ... }
]
}Domains
/domainsList all verified custom domains for your account.
curl https://proxy.referree.com/api/v1/domains \
-H "Authorization: Bearer ref_live_sk_a1b2c3d4..."Response
{
"data": [
{
"id": "43738051-d1ab-4e88-a40c-a10a725e63ea",
"domain": "opulen.com",
"subdomain": "go",
"full_domain": "go.opulen.com",
"dns_status": "verified",
"ssl_status": "active",
"is_primary": true,
"created_at": "2026-01-15T08:00:00.000Z"
}
]
}Brand Profiles
Brand profiles store your QR code styling defaults — colors, corner styles, dot patterns, gradients, and logos. When you generate a QR code (via API or public CDN URLs), styling is resolved automatically: the brand profile assigned to the link's domain is used first, falling back to your account's default profile.
Assign profiles to domains in your dashboard branding page, or use the API endpoints below to manage profile settings.
/brand-profilesList all brand profiles for your account.
curl https://proxy.referree.com/api/v1/brand-profiles \
-H "Authorization: Bearer ref_live_sk_a1b2c3d4..."Response
[
{
"id": "b1c2d3e4-...",
"account_id": "a1b2c3d4-...",
"name": "Default",
"is_default": true,
"qr_fg_color": "#1a1a2e",
"qr_bg_color": "#FFFFFF",
"qr_corner_style": "circle",
"qr_dot_pattern": "rounded",
"qr_error_correction": "H",
"qr_gradient_enabled": true,
"qr_gradient_start": "#1a1a2e",
"qr_gradient_mid": "#4a4a8a",
"qr_gradient_end": "#1a1a2e",
"qr_logo_url": "https://..../logo.png",
"qr_logo_size": "M",
"brand_primary_color": "#1a1a2e",
"brand_secondary_color": "#e6e6fa",
"created_at": "2026-02-20T10:00:00.000Z",
"updated_at": "2026-02-25T14:00:00.000Z"
}
]/brand-profiles/:idRetrieve a single brand profile by ID.
curl https://proxy.referree.com/api/v1/brand-profiles/b1c2d3e4-... \
-H "Authorization: Bearer ref_live_sk_a1b2c3d4..."/brand-profiles/:idUpdate a brand profile's QR styling and brand colors. Logo URLs can only be set via the dashboard upload.
| Parameter | Type | Description |
|---|---|---|
name | string | Profile display name |
qr_fg_color | string | QR foreground color as hex (e.g. "#1a1a2e") |
qr_bg_color | string | QR background color as hex (e.g. "#FFFFFF") |
qr_corner_style | string | Eye/corner style: "default", "circle", "curved", "curved_br", "drop_br", "drop_eye_br", "eye_bl", "dots" |
qr_dot_pattern | string | Module dot shape: "default", "rounded", "circle", "tilted_rounded", "linear_h", "linear_v", "heart", "triangle" |
qr_error_correction | string | Error correction level: "L", "M", "Q", "H" |
qr_gradient_enabled | boolean | Enable gradient fill on QR modules |
qr_gradient_start | string | Gradient start color (hex) |
qr_gradient_mid | string | Gradient mid-point color (hex) |
qr_gradient_end | string | Gradient end color (hex) |
qr_logo_size | string | Logo size: "S" (small), "M" (medium), "L" (large) |
brand_primary_color | string | Brand primary color (hex) |
brand_secondary_color | string | Brand secondary color (hex) |
curl -X PATCH https://proxy.referree.com/api/v1/brand-profiles/b1c2d3e4-... \
-H "Authorization: Bearer ref_live_sk_a1b2c3d4..." \
-H "Content-Type: application/json" \
-d '{
"qr_fg_color": "#1a1a2e",
"qr_corner_style": "circle",
"qr_dot_pattern": "rounded",
"qr_gradient_enabled": true,
"qr_gradient_start": "#1a1a2e",
"qr_gradient_mid": "#4a4a8a",
"qr_gradient_end": "#1a1a2e"
}'Changes to your default brand profile are automatically reflected in all future QR code generations, including the public *.qr.svg and *.qr.png URLs (after CDN cache expires, typically 24 hours).
QR Codes
/qr/:linkIdGenerate a QR code image for a link. Use format for a single binary image response, or formats to get multiple formats as JSON in one call. The response is cached for 1 hour.
| Parameter | Type | Description |
|---|---|---|
format | string | "png" or "svg" — returns binary image data (default: "png") |
formats | string[] | ["svg","png"] — returns JSON with both formats (svg as string, png as base64) |
size | number | Image size in pixels, 100-2000 (default: 300) |
fgColor | string | Foreground color as hex (default: "#000000") |
bgColor | string | Background color as hex (default: "#FFFFFF") |
errorCorrection | string | Error correction level: "L", "M", "Q", or "H" (default: "M") |
margin | number | Quiet zone in modules (default: 2) |
eyeStyle | string | Corner/eye style: "default", "circle", "curved", "curved_br", "drop_br", "drop_eye_br", "eye_bl", "dots" |
dotShape | string | Module dot shape: "default", "rounded", "circle", "tilted_rounded", "linear_h", "linear_v", "heart", "triangle" |
brand_profile_id | string | UUID of a brand profile to use for styling (overrides default profile) |
# Returns binary PNG image
curl -X POST https://proxy.referree.com/api/v1/qr/a1b2c3d4-... \
-H "Authorization: Bearer ref_live_sk_a1b2c3d4..." \
-H "Content-Type: application/json" \
-d '{ "format": "png", "size": 500, "fgColor": "#1a1a2e" }' \
--output qr-code.pngIf your account has a brand profile configured, QR styling (colors, corners, dots, gradient, logo) will use those as fallback when not explicitly provided. Pass brand_profile_id to use a specific profile, or omit it to use your default. For best performance, use generate_qr on POST /links to get link data and QR codes in a single request.
Advanced Styling
Override corner styles and dot patterns per-request. These override brand profile defaults for this request only.
curl -X POST https://proxy.referree.com/api/v1/qr/a1b2c3d4-... \
-H "Authorization: Bearer ref_live_sk_a1b2c3d4..." \
-H "Content-Type: application/json" \
-d '{
"formats": ["svg"],
"size": 500,
"fgColor": "#1a1a2e",
"eyeStyle": "circle",
"dotShape": "rounded"
}'Public QR URLs
Every link has deterministic, CDN-cached QR code URLs. Use them directly as <img src> in HTML, emails, or anywhere you need a QR image without an API call.
# SVG (vector, best for print)
https://go.opulen.com/abc123.qr.svg
# PNG (raster, best for digital)
https://go.opulen.com/abc123.qr.png
# Default domain works too
https://referr.ee/abc123.qr.svg
# Embed in HTML
<img src="https://go.opulen.com/abc123.qr.png" alt="QR Code" />These URLs are public (no auth required) and cached at the CDN edge for 24 hours. QR styling is applied automatically from the brand profile assigned to the domain — if no profile is assigned, the account's default profile is used. No X-Brand-Account header or auth is needed. The qr_url_svg and qr_url_png fields are included in all link API responses.
/pingHealth check and connection warming endpoint. No authentication required. Use this to keep connections warm and reduce cold-start latency.
curl https://proxy.referree.com/api/v1/pingWebhooks
Configure webhooks in your dashboard settings to receive real-time notifications when events occur. Webhooks are delivered as POST requests to your endpoint.
Events
| Event | Description |
|---|---|
| link.clicked | A short link was clicked |
| link.created | A new link was created (via API) |
| link.updated | A link's destination or metadata was updated |
| link.deleted | A link was deleted |
Payload Format
The payload varies by event type. link.clicked includes device and location data. link.created and link.updated include the link details.
{
"event": "link.clicked",
"timestamp": "2026-02-24T15:30:00.000Z",
"data": {
"link_id": "a1b2c3d4-...",
"short_code": "summer24",
"hostname": "go.opulen.com",
"destination_url": "https://example.com/landing",
"device_type": "mobile",
"browser": "Safari",
"os": "iOS",
"country": "US",
"city": "Austin"
}
}Headers
| Parameter | Type | Description |
|---|---|---|
X-Referree-Event | string | The event type (e.g. "link.clicked") |
X-Referree-Signature | string | HMAC-SHA256 signature: sha256={hex} |
X-Referree-Webhook-Id | string | Unique webhook subscription ID |
Content-Type | string | application/json |
User-Agent | string | Referree-Webhook/1.0 |
Verifying Signatures
Each webhook delivery includes an HMAC-SHA256 signature generated with your webhook secret. Always verify this signature to ensure the request is authentic.
const crypto = require('crypto');
function verifyWebhookSignature(rawBody, signature, secret) {
const expected = 'sha256=' + crypto
.createHmac('sha256', secret)
.update(rawBody)
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expected),
);
}
// In your webhook handler:
app.post('/webhooks/referree', (req, res) => {
const signature = req.headers['x-referree-signature'];
const isValid = verifyWebhookSignature(
JSON.stringify(req.body),
signature,
process.env.REFERREE_WEBHOOK_SECRET,
);
if (!isValid) return res.status(401).send('Invalid signature');
const { event, data } = req.body;
console.log(`Received ${event}:`, data);
res.status(200).send('OK');
});Webhooks that fail are retried automatically. After 10 consecutive failures, the webhook is disabled. You can re-enable it from the dashboard.
Scale API
Enterprise Partner Endpoints
Scale-tier partners can programmatically create brand accounts, provision custom domains, and manage branded short links on behalf of their users — all via API. End-users never interact with Referree directly.
The Scale API extends the standard API with endpoints for multi-tenant management. Use POST /accounts to create headless brand accounts, then operate on them using the X-Brand-Account header from the Authentication section.
Accounts
/accountsCreate a headless brand account managed by your Scale account. Returns an account_id for use with the X-Brand-Account header.
| Parameter | Type | Description |
|---|---|---|
namerequired | string | Display name for the brand account |
email | string | Contact email. If omitted, a synthetic email is generated. |
curl -X POST https://proxy.referree.com/api/v1/accounts \
-H "Authorization: Bearer ref_live_sk_a1b2c3d4..." \
-H "Content-Type: application/json" \
-d '{
"name": "Deeble",
"email": "hello@deeble.co"
}'Response 201
{
"account_id": "6623fae7-fa3f-49ed-b139-14d057c0fe9c",
"name": "Deeble",
"email": "hello@deeble.co",
"created_at": "2026-03-20T12:00:00.000Z"
}Domain Provisioning
/domainsProvision a custom domain for a brand account. After creation, the brand must add a CNAME record pointing to proxy.referree.com. Poll GET /domains/:id to check DNS and SSL status.
| Parameter | Type | Description |
|---|---|---|
domainrequired | string | Root domain (e.g. "deeble.co") |
subdomainrequired | string | Subdomain prefix (e.g. "proof") |
curl -X POST https://proxy.referree.com/api/v1/domains \
-H "Authorization: Bearer ref_live_sk_a1b2c3d4..." \
-H "X-Brand-Account: 6623fae7-fa3f-49ed-b139-14d057c0fe9c" \
-H "Content-Type: application/json" \
-d '{
"domain": "deeble.co",
"subdomain": "proof"
}'Response 201
{
"id": "43738051-d1ab-4e88-a40c-a10a725e63ea",
"domain": "deeble.co",
"subdomain": "proof",
"full_domain": "proof.deeble.co",
"dns_status": "pending",
"ssl_status": "pending",
"is_primary": true,
"created_at": "2026-03-20T12:01:00.000Z",
"cname_target": "proxy.referree.com"
}/domains/:idRetrieve a single domain by ID. Use this to poll for DNS verification and SSL provisioning status after creating a domain.
curl https://proxy.referree.com/api/v1/domains/43738051-d1ab-4e88-a40c-a10a725e63ea \
-H "Authorization: Bearer ref_live_sk_a1b2c3d4..." \
-H "X-Brand-Account: 6623fae7-fa3f-49ed-b139-14d057c0fe9c"Response
{
"id": "43738051-d1ab-4e88-a40c-a10a725e63ea",
"domain": "deeble.co",
"subdomain": "proof",
"full_domain": "proof.deeble.co",
"dns_status": "verified",
"ssl_status": "active",
"cf_ssl_status": "active",
"is_primary": true,
"last_checked_at": "2026-03-20T12:15:00.000Z",
"verified_at": "2026-03-20T12:15:00.000Z",
"created_at": "2026-03-20T12:01:00.000Z"
}/domains/:idRemove a custom domain and its Cloudflare hostname. This cannot be undone.
curl -X DELETE https://proxy.referree.com/api/v1/domains/43738051-d1ab-4e88-a40c-a10a725e63ea \
-H "Authorization: Bearer ref_live_sk_a1b2c3d4..." \
-H "X-Brand-Account: 6623fae7-fa3f-49ed-b139-14d057c0fe9c"Response
{
"deleted": true,
"id": "43738051-d1ab-4e88-a40c-a10a725e63ea"
}End-to-End Workflow
Here's the complete flow for a Scale partner onboarding a new brand with a custom domain:
POST /accounts with the brand name. Save the returned account_id.POST /domains with X-Brand-Account header. Note the cname_target in the response.proof → proxy.referree.com. Guide them through your own UI.GET /domains/:id until dns_status is "verified" and ssl_status is "active".POST /links with custom_domain_id and your own short_code. Result: https://proof.deeble.co/1Interested in the Scale API?
Tell us about your use case and we'll get you set up.
Errors
The API uses standard HTTP status codes. Errors return a JSON body with an error field.
| Status | Meaning | Example |
|---|---|---|
| 400 | Bad Request | Invalid JSON body, missing required field |
| 401 | Unauthorized | Missing or invalid API key |
| 403 | Forbidden | Tier doesn't include this feature, brand access denied |
| 404 | Not Found | Link or resource not found |
| 405 | Method Not Allowed | Wrong HTTP method for endpoint |
| 409 | Conflict | Duplicate short code |
| 429 | Rate Limited | Monthly API quota exceeded |
| 500 | Server Error | Unexpected server error |
{
"error": "destination_url is required"
}Need help integrating?
Reach us at support@referree.com