Quickstart
Enable Business or Business Plus, then create a workspace API key with qr:write. Set LINKWAVE_URL to your Linkwave deployment origin and LINKWAVE_API_KEY to your secret key on your server.
curl -X POST "$LINKWAVE_URL/api/v1/qr-codes" \
-H "Authorization: Bearer $LINKWAVE_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: ticket:ticket_987:v1" \
--data '{
"name": "Ticket ticket_987",
"mode": "static",
"payload": "https://tickets.example.com/validate/unguessable-token",
"design": {
"template": "ticket",
"label": "ADMIT ONE",
"foreground": "#16181d",
"background": "#ffffff",
"logo": {
"preset": "calendar"
}
}
}'A successful creation returns HTTP 201. The response includes data.id, data.image.svg, and data.image.data_uri. SVG is a vector image; the API does not create the complete ticket PDF.
{
"data": {
"id": "8cb37fde-49cb-4f33-9d72-32fe81376d17",
"name": "Ticket ticket_987",
"mode": "static",
"payload": "https://tickets.example.com/validate/unguessable-token",
"encoded_value": "https://tickets.example.com/validate/unguessable-token",
"status": "active",
"created_at": "2026-10-17T12:00:00Z",
"image_endpoint": "https://YOUR_LINKWAVE_HOST/api/v1/qr-codes/8cb37fde-49cb-4f33-9d72-32fe81376d17/image",
"image": {
"format": "svg",
"svg": "<svg ...>...</svg>",
"data_uri": "data:image/svg+xml;charset=utf-8,..."
}
}
}Authentication
Authorization: Bearer lw_live_YOUR_SECRET_KEYWorkspace owners and admins can create and revoke keys in API access. A key is shown once; only its SHA-256 hash is stored. Keys expire after 30, 90 or 365 days. Create a replacement, update your integration, then revoke the old key to rotate without interruption.
qr:write creates QR codes. qr:read lists QR codes, retrieves images and reads the design catalog. All requests are restricted to the key's workspace. Revoked keys, expired keys, deleted workspaces and inactive subscriptions are rejected. API keys must stay on your server; browser CORS access is not enabled.
Ticketing workflow
- Your backend verifies the payment provider's webhook and confirms the order is paid.
- Create one opaque validation token or validation URL per ticket in your ticketing database.
- Call the API once per ticket using an idempotency key such as
ticket:ticket_987:v1. Multiple tickets in an order need distinct keys. - Store the returned QR ID with the ticket and embed the SVG or data URI in your ticket PDF, app or email. Fetch the authenticated image endpoint from your backend when needed; do not include your API key in a URL.
- At entry, your ticketing backend checks that ticket and atomically marks it redeemed.
Generating a QR code does not confirm a payment or prevent a copied ticket from being reused. Your ticketing system remains responsible for ticket ownership, cancellation and one-time redemption.
| Mode | Scan behavior |
|---|---|
| Static (default) | Encodes your payload directly. Works with raw scanner tokens or validation URLs. Printed codes cannot be edited or tracked by Linkwave. Pausing or deleting the Linkwave record does not invalidate a static ticket. |
| Dynamic | Encodes a Linkwave redirect to your HTTP(S) validation URL. Its destination and paused status can be managed in the dashboard. Scans can be tracked. Browser access to the Linkwave redirect is required. |
Designs
Choose a template, foreground color, background color, custom frame label, and logo. Use a dark foreground with a light background; the API requires at least 4.5:1 contrast. The size parameter (128-2048, default 720) controls the inner design size; padding and frames add to the final dimensions.
{
"design": {
"template": "ticket",
"label": "ADMIT ONE",
"foreground": "#16181d",
"background": "#ffffff",
"logo": {
"data_url": "data:image/png;base64,YOUR_BASE64_IMAGE"
}
},
"size": 720
}Use either { "preset": "calendar" } or { "data_url": "data:image/png;base64,..." } for design.logo. Embedded PNG, JPEG and WebP images can be up to 100 KB decoded. Remote image URLs and uploaded SVG images are not accepted. Logos are self-contained in the returned SVG.
Template IDs
Logo preset IDs
Custom labels support up to 28 characters. Choose a label-capable template such as ticket, label-dark, or circle-label to display the label.
Endpoints
| Method | Path | Scope | Response |
|---|---|---|---|
| POST | /api/v1/qr-codes | qr:write | Create QR with styled SVG |
| GET | /api/v1/qr-codes?limit=25&offset=0 | qr:read | Paginated workspace QR codes |
| GET | /api/v1/qr-codes/{id} | qr:read | QR metadata and saved design |
| GET | /api/v1/qr-codes/{id}/image?size=720 | qr:read | Saved design as image/svg+xml |
| GET | /api/v1/templates | qr:read | Template and logo preset IDs |
The list endpoint accepts a limit of 1-100 and an offset of 0-10,000. Responses include pagination.has_more. Use the QR ID stored with each ticket to retrieve it directly. API-created QR codes also appear in your dashboard. Images containing external dashboard logo URLs cannot be rendered through the API; create API designs with a preset or embedded image.
Retries and limits
POST requires Idempotency-Key: 1-128 printable ASCII characters without spaces. Repeating the same key and normalized request returns HTTP 200 and Idempotency-Replayed: true, with the same QR ID. It returns the current saved QR design. Reusing a key with a different request returns 409. A deleted QR returns 410; it is not silently recreated. Keys are scoped to the workspace and retained for its lifetime.
Requests are limited to 120 per minute per workspace across all its keys. Successful authenticated requests include X-RateLimit-Limit, X-RateLimit-Remaining and X-RateLimit-Reset (Unix seconds). A 429 includes Retry-After. Retry 429 and 503 responses with backoff, and retry network failures using the original idempotency key. Retrying does not consume another QR quota slot, but it does count as an API request.
All API-created records, static or dynamic, count toward the workspace QR allowance. Business and Business Plus use a UTC calendar month; deleted QR codes still count for that month. Other API-enabled plans use their total active-record limit. JSON request bodies are limited to 200,000 bytes; payloads are limited to 1024 UTF-8 bytes. Upgrade in Billing when more capacity is needed.
Errors
{
"error": {
"code": "qr_quota_exceeded",
"message": "Workspace QR quota reached. Upgrade your plan",
"request_id": "be6bba15-ae56-44a8-bc0b-2cbe53cc7bd6"
}
}| 400 | Invalid JSON or missing idempotency key |
| 401 | Missing, invalid, expired or revoked key |
| 402 | Workspace QR quota reached |
| 403 | Required scope, API plan or active subscription missing |
| 404 | QR not found in this workspace |
| 409 | Idempotency key reused with different input |
| 410 | Previously created QR was deleted |
| 413 / 415 | Body too large / wrong content type |
| 422 | Payload, image, design or query validation failed |
| 429 | Workspace rate limit reached |
| 503 | Temporary service failure or pending API database migration |
Each response includes X-Request-Id. Keep this ID when contacting support. Never include your API key in logs or support messages.
