Bulk QR upload format
The dashboard’s QR codes → Bulk page and the customer API’s
POST /api/qr/codes/bulk endpoint create many dynamic QR codes in one run.
Both support the full customization surface: every row can carry the complete
create-body (styling, presets, brand kits, protection, content payloads).
Dashboard upload (CSV or Excel)
Download a sample CSV template or Excel template from the Bulk page. The templates ship every accepted column plus example rows; the Excel file includes a column-reference sheet. A bare list of URLs (one per line, no header) also works and creates url-type QRs.
Accepted columns:
| Column | What it does |
|---|---|
name | Dashboard label (max 120 chars) |
dataType | url (default), text, email, phone, sms, geo, wifi, vcard |
destination | The content for url, text, phone, geo rows |
preset | A builder look: classic, soft, dots, ocean, sunset, contrast-eyes, menu, bizcard, wifi, event, boutique, cafe |
brandKitId | Apply a saved brand kit by id (bk_...) |
primaryColor / backgroundColor | #rrggbb overrides (a solid override replaces a base gradient) |
frameText | Caption text (max 24 chars) when the effective style has a caption frame |
styleJson | Advanced: a JSON object of raw style options (dotsOptions, frame, shape, width, …) merged last, for 100 percent per-row styling control. Non-style keys are ignored with a warning |
password | Require a password before the redirect opens |
expiresAt | ISO 8601 date/time; must be in the future (and within the JS date range, i.e. before the year 275760) |
maxScans | Stop redirecting after N scans |
wifiSsid, wifiPassword, wifiEncryption | Wi-Fi payload (WPA, WEP, nopass) |
emailTo, emailSubject, emailBody | Email payload |
smsPhone, smsMessage | SMS payload |
vcardFirstName, vcardLastName, vcardPhone, vcardEmail, vcardOrganization, vcardWebsite | Contact card payload |
How styling resolves per row:
- The batch style you pick on the page (default look, a preset, a brand kit, or a fully custom design) applies to every row.
- A row’s
presetorbrandKitId(mutually exclusive) replaces the batch style for that row. primaryColor/backgroundColor/frameTexttweak the result.styleJsonmerges last and wins on any style-option key it sets. It only touches styling: keys likeuid,data, orpasswordin astyleJsoncell are ignored (the preview shows a warning), never merged.
Rows that fail validation are listed with reasons and excluded from the batch; everything else submits. Row cap: 500 per upload from the dashboard.
JSON format (API)
A bulk row is exactly the POST /api/qr/codes create body plus a required
uid (your row id, echoed back in the result). Anything the single-create
endpoint accepts works per row: full styling objects, preset, brandKitId,
secure + password + expiry, maxScans, routingRules, campaign,
deepLinkConfig, and structured data payloads.
POST /api/qr/codes/bulk
Authorization: Bearer exr_live_...
Content-Type: application/json
[
{
"uid": "row-1",
"data": "https://yourbrand.com/spring",
"name": "Spring poster",
"preset": "cafe",
"secure": ["expire"],
"expiry": 1893456000000
},
{
"uid": "row-2",
"data": { "ssid": "AcmeGuest", "password": "welcome123", "encryption": "WPA" },
"dataType": "wifi",
"brandKitId": "bk_abc123"
}
]- Row cap: 1,000 per call (split larger runs across calls).
preset/brandKitIdare expanded server-side to the same pixels as the dashboard. A bad preset id or a kit that is not yours fails the whole call synchronously with a 400 naming the row, before any job is queued.- Logos in bulk: host the logo at an http(s) URL or apply it via a brand
kit. Inline
data:URI logos longer than 200,000 characters (roughly 150KB of image data) are rejected per row to keep job payloads bounded.
Response is a bulkId you poll:
GET /api/qr/codes/bulk/{bulkId}When the status is completed, fetch the result once:
GET /api/qr/codes/bulk/{bulkId}/resultThe result is an array with one entry per row: successes carry your uid,
the internal id, and the redirect URL; failures carry
{ "uid", "status": "failed", "message", "code" }. It is a one-shot
download, removed after the first successful fetch.