Skip to content
QRflick

REST API & webhooks

Create, change and read your dynamic QR codes from your own software, and get a signed webhook when something is scanned or a lead comes in.

Base URL: https://qrflick.com/api/v1

Authentication

Every request carries an API key in the Authorization header. Keys belong to an account (or team workspace) and are created in Settings → Developers by its owner or an admin.

curl https://qrflick.com/api/v1/me \
  -H "Authorization: Bearer api_…"
  • Read-only keys can list and fetch codes, cards, leads, scans and images.
  • Read & write keys can also create, change and delete codes and webhook endpoints.
  • A key stops working if the person who made it leaves the team or stops being an admin, or if the account leaves a plan with API access.

Create a key in Settings → Developers.

Requests, errors and limits

Send and receive JSON. Dates are ISO 8601 in UTC. Ids are the 12-character ids you see in dashboard URLs.

Lists are paginated: use ?page= and ?per_page= (up to 100). Responses carry data, links and meta. Each key has a per-minute request limit; the X-RateLimit-Remaining header shows what’s left.

StatusMeaning
401Missing, wrong or revoked key.
403The key is read-only, the plan has no API access, or the action isn’t allowed.
404No such record in this account.
422Something in the request is invalid or over a plan limit; errors says which field.
429Too many requests. Wait for the seconds in Retry-After.
{
  "message": "The name field is required.",
  "errors": { "name": ["The name field is required."] }
}

Endpoints

RequestWhat it does
GET /me The account, plan, key and limits. Handy to test a connection.
GET /qr-codes Your dynamic codes, newest first. Filters: search, type, archived.
POST /qr-codes Create a code: type, name, payload, and optionally design. (needs read & write)
GET /qr-codes/{id} One code, with its short link, destination and scan count.
PATCH /qr-codes/{id} Change any of name, payload, type, design, or paused (true/false). The printed code stays the same. (needs read & write)
DELETE /qr-codes/{id} Delete a code. Its short link then shows “not found”. (needs read & write)
GET /qr-codes/{id}/image The code as png, svg, pdf or eps: ?format=, and ?size= (pixels) or ?mm= and ?dpi=.
GET /qr-codes/{id}/scans Scans per day and breakdowns (country, city, device, os, browser, language, referrer): ?from= and ?to= (YYYY-MM-DD).
GET /cards Your digital business cards.
GET /cards/{id} One card, with its page and short link.
GET /cards/{id}/leads Contacts shared on a card, newest first. ?since= returns only newer ones.
GET /webhooks Your webhook endpoints.
POST /webhooks Subscribe a URL to events (url, events). The reply includes the signing secret. (needs read & write)
DELETE /webhooks/{id} Unsubscribe an endpoint. (needs read & write)

Example: create and change a code

Create a website code. The reply has the short link to print or share.

curl -X POST https://qrflick.com/api/v1/qr-codes \
  -H "Authorization: Bearer api_…" \
  -H "Content-Type: application/json" \
  -d '{"type": "url", "name": "Summer menu", "payload": {"url": "https://example.com/menu"}}'
{
  "data": {
    "id": "k3v9x2m7qa1b",
    "object": "qr_code",
    "name": "Summer menu",
    "type": "url",
    "payload": { "url": "https://example.com/menu" },
    "short_url": "https://qrflick.com/Ab3xY9k",
    "domain": "qrflick.com",
    "destination": "https://example.com/menu",
    "status": "active",
    "scans": 0,
    "last_scanned_at": null,
    "archived": false,
    "created_at": "2026-01-31T10:00:00+00:00",
    "updated_at": "2026-01-31T10:00:00+00:00"
  }
}

Optional "domain": the host of a platform redirect domain your workspace may use (as offered when creating a code in the app). Leave it out for the standard short domain. The address can't be changed through the API once created.

Later, point the same printed code somewhere else:

curl -X PATCH https://qrflick.com/api/v1/qr-codes/k3v9x2m7qa1b \
  -H "Authorization: Bearer api_…" \
  -H "Content-Type: application/json" \
  -d '{"payload": {"url": "https://example.com/autumn-menu"}}'

Download it as a print-ready SVG, 40 mm wide:

curl "https://qrflick.com/api/v1/qr-codes/k3v9x2m7qa1b/image?format=svg&mm=40" \
  -H "Authorization: Bearer api_…" -o menu.svg

QR code types and payload fields

The payload holds the fields for the code’s type. Business card and profile codes are made with their card or profile, not here.

typePayload fields
url url*
whatsapp phone*, message
phone phone*
email email*, subject, body
sms phone*, message
maps query*
google_review place_id*
link_bio Pro title*, description, links*
menu Pro title*, description, menu_url*
app_store Pro ios_url*, android_url*, fallback_url*
event Pro title*, description, starts_at*, location, url
coupon Pro title*, description, code*, expires_at, url
social platform*, handle*
file title*, description, file_url

* required. Pro types need a plan with smart redirects.

Webhooks

Add an endpoint in Settings → Developers (or with POST /webhooks) and pick its events. Each event is POSTed as JSON:

EventSent when
qr_code.createdA QR code was created.
qr_code.updatedA QR code got a new destination, status or setting.
qr_code.deletedA QR code was deleted.
scan.createdSomeone scanned a code (bots and link previews are left out).
lead.createdSomeone shared their contact on a card or wrote through a profile.
POST /your/endpoint
Content-Type: application/json
webhook-id: evt_01jbx6q9d2m8w4k7r3t5y0a1bc
webhook-timestamp: 1767225600
webhook-signature: v1,K5oZfzN95Z9UVu1EsfQmfVNQhnkZ2pj9o9NDN/H/pI4=

{
  "id": "evt_01jbx6q9d2m8w4k7r3t5y0a1bc",
  "type": "scan.created",
  "created_at": "2026-01-01T00:00:00+00:00",
  "data": {
    "qr_code": { "id": "k3v9x2m7qa1b", "name": "Summer menu", "type": "url" },
    "short_url": "https://qrflick.com/Ab3xY9k",
    "scanned_at": "2026-01-01T00:00:00+00:00",
    "country": "ES", "city": "Madrid", "device": "mobile", "os": "ios",
    "browser": "safari", "language": "es", "referrer": null, "variant": null
  }
}
  • Reply with any 2xx status within 10 seconds; do slow work after replying.
  • Anything else is retried after 1 min, 5 min, 30 min, 2 h, 6 h.
  • After 20 events in a row fail every try, the endpoint is paused and the owner gets an email.
  • Retries and “Send again” keep the same webhook-id: use it to skip duplicates.
  • Scans never include IP addresses. Lead events include the contact details the visitor chose to share.

Checking signatures

Deliveries are signed the Standard Webhooks way. Sign “webhook-id.webhook-timestamp.raw body” with HMAC-SHA256, using the base64-decoded part of your secret after whsec_, and compare with the webhook-signature header. Reject old timestamps to stop replays.

// PHP
$secret  = 'whsec_…';
$payload = $id . '.' . $timestamp . '.' . $rawBody;
$key     = base64_decode(substr($secret, 6));
$expected = 'v1,' . base64_encode(hash_hmac('sha256', $payload, $key, true));
$valid = abs(time() - (int) $timestamp) < 300
    && hash_equals($expected, $signatureHeader);
// Node.js
import crypto from 'node:crypto';
const key = Buffer.from(secret.slice(6), 'base64');
const expected = 'v1,' + crypto.createHmac('sha256', key)
  .update(`${id}.${timestamp}.${rawBody}`).digest('base64');
const valid = Math.abs(Date.now() / 1000 - Number(timestamp)) < 300
  && crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(signatureHeader));

Standard Webhooks libraries (standardwebhooks.com) for most languages check this for you.

Your next connection is waiting.

Make your next scan
the start of something.

Your next customer could already be looking. Make it easy to find you.

Create your first QR free No credit card. No expiry. Just possibilities.