API Reference

Build powerful integrations with the Referree API. Create short links, generate QR codes, manage custom domains, and track analytics programmatically.

Base URL:https://proxy.referree.com/api/v1

All 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.

TierMonthly Limit
Pro5,000 requests
Business20,000 requests
Scale50,000 requests

When you exceed the limit, the API returns 429 Too Many Requests. Rate limit info is included in response headers:

ParameterTypeDescription
X-RateLimit-LimitnumberYour monthly request limit
X-RateLimit-RemainingnumberRequests remaining this month
X-RateLimit-ResettimestampWhen the limit resets (ISO 8601)
POST/links

Create a new short link.

ParameterTypeDescription
destination_urlrequiredstringThe URL to redirect to
short_codestringCustom back-half (auto-generated if omitted)
custom_domain_idstringUUID of a verified custom domain
is_dynamicbooleanWhether the destination can be changed later (default: true)
og_titlestringOpen Graph title for social previews
og_descriptionstringOpen Graph description
og_image_urlstringOpen Graph image URL
metadataobjectArbitrary key-value metadata (e.g. UTM params, campaign IDs)
generate_qrboolean | objectGenerate 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.

GET/links

List all links for your account with pagination.

ParameterTypeDescription
limitnumberResults per page (default: 50, max: 100)
offsetnumberNumber of results to skip (default: 0)
order_bystringSort field (default: "created_at")
orderstringSort 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
  }
}
GET/links/:id

Retrieve 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..."
PATCH/links/:id

Update a link's destination, metadata, or Open Graph properties.

ParameterTypeDescription
destination_urlstringNew redirect URL
is_dynamicbooleanToggle dynamic/static
og_titlestringOpen Graph title
og_descriptionstringOpen Graph description
og_image_urlstringOpen Graph image URL
metadataobjectReplace 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" }'
DELETE/links/:id

Permanently 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-..." }
GET/links/:id/stats

Get 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"
}
POST/links/bulk

Create 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

GET/domains

List 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.

GET/brand-profiles

List 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"
  }
]
GET/brand-profiles/:id

Retrieve a single brand profile by ID.

curl https://proxy.referree.com/api/v1/brand-profiles/b1c2d3e4-... \
  -H "Authorization: Bearer ref_live_sk_a1b2c3d4..."
PATCH/brand-profiles/:id

Update a brand profile's QR styling and brand colors. Logo URLs can only be set via the dashboard upload.

ParameterTypeDescription
namestringProfile display name
qr_fg_colorstringQR foreground color as hex (e.g. "#1a1a2e")
qr_bg_colorstringQR background color as hex (e.g. "#FFFFFF")
qr_corner_stylestringEye/corner style: "default", "circle", "curved", "curved_br", "drop_br", "drop_eye_br", "eye_bl", "dots"
qr_dot_patternstringModule dot shape: "default", "rounded", "circle", "tilted_rounded", "linear_h", "linear_v", "heart", "triangle"
qr_error_correctionstringError correction level: "L", "M", "Q", "H"
qr_gradient_enabledbooleanEnable gradient fill on QR modules
qr_gradient_startstringGradient start color (hex)
qr_gradient_midstringGradient mid-point color (hex)
qr_gradient_endstringGradient end color (hex)
qr_logo_sizestringLogo size: "S" (small), "M" (medium), "L" (large)
brand_primary_colorstringBrand primary color (hex)
brand_secondary_colorstringBrand 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

POST/qr/:linkId

Generate 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.

ParameterTypeDescription
formatstring"png" or "svg" — returns binary image data (default: "png")
formatsstring[]["svg","png"] — returns JSON with both formats (svg as string, png as base64)
sizenumberImage size in pixels, 100-2000 (default: 300)
fgColorstringForeground color as hex (default: "#000000")
bgColorstringBackground color as hex (default: "#FFFFFF")
errorCorrectionstringError correction level: "L", "M", "Q", or "H" (default: "M")
marginnumberQuiet zone in modules (default: 2)
eyeStylestringCorner/eye style: "default", "circle", "curved", "curved_br", "drop_br", "drop_eye_br", "eye_bl", "dots"
dotShapestringModule dot shape: "default", "rounded", "circle", "tilted_rounded", "linear_h", "linear_v", "heart", "triangle"
brand_profile_idstringUUID 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.png

If 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.

GET/ping

Health 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/ping

Webhooks

Configure webhooks in your dashboard settings to receive real-time notifications when events occur. Webhooks are delivered as POST requests to your endpoint.

Events

EventDescription
link.clickedA short link was clicked
link.createdA new link was created (via API)
link.updatedA link's destination or metadata was updated
link.deletedA 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

ParameterTypeDescription
X-Referree-EventstringThe event type (e.g. "link.clicked")
X-Referree-SignaturestringHMAC-SHA256 signature: sha256={hex}
X-Referree-Webhook-IdstringUnique webhook subscription ID
Content-Typestringapplication/json
User-AgentstringReferree-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

POST/accounts

Create a headless brand account managed by your Scale account. Returns an account_id for use with the X-Brand-Account header.

ParameterTypeDescription
namerequiredstringDisplay name for the brand account
emailstringContact 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

POST/domains

Provision 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.

ParameterTypeDescription
domainrequiredstringRoot domain (e.g. "deeble.co")
subdomainrequiredstringSubdomain 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"
}
GET/domains/:id

Retrieve 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"
}
DELETE/domains/:id

Remove 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:

1
Create a brand accountPOST /accounts with the brand name. Save the returned account_id.
2
Provision the domainPOST /domains with X-Brand-Account header. Note the cname_target in the response.
3
DNS setup — The brand adds a CNAME record: proof → proxy.referree.com. Guide them through your own UI.
4
Poll for verificationGET /domains/:id until dns_status is "verified" and ssl_status is "active".
5
Create branded linksPOST /links with custom_domain_id and your own short_code. Result: https://proof.deeble.co/1

Interested 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.

StatusMeaningExample
400Bad RequestInvalid JSON body, missing required field
401UnauthorizedMissing or invalid API key
403ForbiddenTier doesn't include this feature, brand access denied
404Not FoundLink or resource not found
405Method Not AllowedWrong HTTP method for endpoint
409ConflictDuplicate short code
429Rate LimitedMonthly API quota exceeded
500Server ErrorUnexpected server error
{
  "error": "destination_url is required"
}

Need help integrating?

Reach us at support@referree.com