API v1
An API for codes that stay printed.
{ "id": "qr_8841ce2mxk", "type": "url", "dynamic": true, "status": "active", "shortUrl": "https://dotwick.co/Xq7mK2p", "createdAt": "2026-09-10T09:41:08.000Z"}A REST API over the same objects the app uses. Create codes, change destinations, pull scans and export print-ready files — no SDK required: curl, fetch or requests will do.
included on Pro and up · 60 req/min on Pro, 600 on Business
Create keys in the app under Settings → API, with only the scopes they need.
curl -X POST https://api.dotwick.com/v1/codes \ -H "Authorization: Bearer sk_live_…" \ -H "Content-Type: application/json" \ -H "Idempotency-Key: van-014-2026-09-10" \ -d '{ "name": "Van fleet – unit 14", "content": { "type": "url", "url": "https://example.com/fleet/van-014" }, "design": { "pattern": "rounded", "eyes": { "frame": "circle", "ball": "circle" }, "colors": { "fg": "#18181B", "bg": "#FFFFFF" } }, "folderId": "fld_8k2m4q7x" }'{ "id": "qr_8841ce2mxk", "name": "Van fleet – unit 14", "type": "url", "dynamic": true, "status": "active", "slug": "Xq7mK2p", "shortUrl": "https://dotwick.co/Xq7mK2p", "content": { "type": "url", "url": "https://example.com/fleet/van-014" }, "folderId": "fld_8k2m4q7x", "createdAt": "2026-09-10T09:41:08.000Z"}api.dotwick.com/v1
REST over JSON
Predictable resources, cursor pagination, idempotency keys on every POST. Errors come back as { error: { code, message, requestId } }.
11 event types
Webhooks
Scans, code changes, finished bulk runs and exports, domain status and form answers. Signed per Standard Webhooks and retried for 24 hours.
POST /v1/codes/bulk
Bulk and exports
Create up to 500 codes in one request, then download them as SVG, PNG, PDF or EPS in a single ZIP rendered in the background.
api.dotwick.com/openapi.json
OpenAPI
Every public route is described in OpenAPI 3.1, generated from the same contracts the API validates with, each operation tagged with the scope it needs.
- POST/v1/codes
- GET/v1/codes
- GET/v1/codes/{id}
- PATCH/v1/codes/{id}
- DELETE/v1/codes/{id}
- POST/v1/codes/bulk
- GET/v1/codes/{id}/scans
- POST/v1/webhooks
Endpoints
Every route an API key can call. Anything else — billing, members, key management — stays in the app.
| Method | Path | Scope | Purpose |
|---|---|---|---|
| Codes | |||
| GET | /v1/codes | codes:read | List codes, filter by folder, type or status |
| POST | /v1/codes | codes:write | Create a code |
| GET | /v1/codes/{id} | codes:read | Retrieve a code with its design and rules |
| PATCH | /v1/codes/{id} | codes:write | Change destination, name, design, rules |
| DELETE | /v1/codes/{id} | codes:write | Delete a code (its short link answers 410) |
| POST | /v1/codes/move | codes:write | Move codes to a folder |
| POST | /v1/codes/move-domain | codes:write | Move codes to another domain |
| POST | /v1/codes/tags | codes:write | Add or remove tags on many codes |
| POST | /v1/codes/{id}/duplicate | codes:write | Duplicate a code |
| PATCH | /v1/codes/bulk | codes:write | Pause, resume, archive, tag, move or delete many codes |
| GET | /v1/utm-presets | codes:read | List saved UTM presets |
| GET | /v1/codes/{id}/versions | codes:read | Destination history of a code |
| GET | /v1/codes/{id}/activity | codes:read | Activity feed of a code |
| GET | /v1/codes/{id}/export | codes:read | Download as SVG, PNG, JPG, PDF or EPS |
| GET | /v1/codes/{id}/hosted-stats | codes:read | Counters of a hosted code |
| GET | /v1/codes/{id}/submissions | submissions:read | Responses to a form code |
| GET | /v1/codes/{id}/submissions/export | submissions:read | Form responses as CSV |
| GET | /v1/gs1/products | codes:read | List GS1 products (the gs1 codes) |
| GET | /v1/codes/{id}/gs1/qualifiers | codes:read | Batch, CPV and serial levels of a GS1 code |
| GET | /v1/codes/{id}/gs1/print-runs | codes:read | Print runs of a GS1 code |
| GET | /v1/codes/{id}/gs1/print-runs/{runId} | codes:read | Poll a print run and get its file links |
| Bulk runs | |||
| POST | /v1/codes/bulk | codes:write | Create up to 500 codes as one run |
| GET | /v1/bulk-imports | codes:read | List bulk runs |
| GET | /v1/bulk-imports/{id} | codes:read | Poll a bulk run |
| GET | /v1/bulk-imports/{id}/rows | codes:read | Rows of a bulk run |
| GET | /v1/bulk-imports/{id}/results.csv | codes:read | Results of a run as CSV |
| POST | /v1/bulk-imports/{id}/cancel | codes:write | Cancel a queued or running run |
| POST | /v1/bulk-imports/{id}/retry | codes:write | Retry the failed rows of a run |
| Analytics | |||
| GET | /v1/analytics/summary | analytics:read | Scan totals for a range |
| GET | /v1/analytics/timeseries | analytics:read | Scans over time |
| GET | /v1/analytics/breakdown | analytics:read | Top countries, devices, … |
| GET | /v1/analytics/scans | analytics:read | Per-scan records |
| GET | /v1/analytics/sparklines | analytics:read | Daily counts for codes |
| GET | /v1/analytics/compare | analytics:read | Compare up to 4 codes |
| GET | /v1/analytics/export | analytics:read | Scans as CSV (small ranges) |
| POST | /v1/analytics/exports | analytics:read | Scans as CSV, prepared in the background |
| GET | /v1/codes/{id}/analytics/summary | analytics:read | One code's totals |
| GET | /v1/codes/{id}/analytics/timeseries | analytics:read | One code over time |
| GET | /v1/codes/{id}/analytics/breakdown | analytics:read | One code's top lists |
| GET | /v1/codes/{id}/scans | analytics:read | One code's per-scan records |
| GET | /v1/codes/{id}/gs1/batches | analytics:read | Scans per batch of a GS1 code |
| GET | /v1/codes/{id}/downloads | analytics:read | Downloads by file of a Documents code |
| GET | /v1/analytics/locations | analytics:read | GPS map of consenting scans |
| GET | /v1/codes/{id}/analytics/locations | analytics:read | One code's GPS map |
| GET | /v1/alerts | analytics:read | List scan alert rules |
| Exports | |||
| POST | /v1/exports | codes:read | Export many codes as one ZIP |
| GET | /v1/exports | codes:read | List exports |
| GET | /v1/exports/{id} | codes:read | Poll an export and get the ZIP link |
| Folders | |||
| GET | /v1/folders | folders:read | List folders |
| POST | /v1/folders | folders:write | Create a folder |
| GET | /v1/folders/{id} | folders:read | Retrieve a folder |
| PATCH | /v1/folders/{id} | folders:write | Rename or move a folder |
| DELETE | /v1/folders/{id} | folders:write | Delete a folder (codes move up) |
| Templates | |||
| GET | /v1/templates | templates:read | List design templates |
| POST | /v1/templates | templates:write | Create a template |
| GET | /v1/templates/{id} | templates:read | Retrieve a template |
| PATCH | /v1/templates/{id} | templates:write | Change a template |
| DELETE | /v1/templates/{id} | templates:write | Delete a template |
| Uploads | |||
| POST | /v1/assets | codes:write | Upload a logo or a file |
| GET | /v1/assets/{id} | codes:read | Retrieve an upload with a signed URL |
| Domains | |||
| GET | /v1/domains | domains:read | List custom domains |
| GET | /v1/domains/{id} | domains:read | Retrieve a domain and its status |
| GET | /v1/subdomain | domains:read | The workspace's subdomain and its links |
| GET | /v1/subdomain/availability | domains:read | Check if a subdomain label is free |
| Workspace | |||
| GET | /v1/workspaces/current | any key | The key's workspace and plan |
| GET | /v1/usage | any key | Plan limits and usage this month |
| Webhooks | |||
| GET | /v1/webhooks | webhooks:read | List webhook endpoints |
| POST | /v1/webhooks | webhooks:write | Register an endpoint and event types |
| GET | /v1/webhooks/{id} | webhooks:read | Retrieve an endpoint |
| PATCH | /v1/webhooks/{id} | webhooks:write | Change or re-enable an endpoint |
| DELETE | /v1/webhooks/{id} | webhooks:write | Delete an endpoint |
| POST | /v1/webhooks/{id}/test | webhooks:write | Send a webhook.test event |
| GET | /v1/webhooks/{id}/deliveries | webhooks:read | Deliveries of an endpoint |
| GET | /v1/webhooks/{id}/deliveries/{deliveryId} | webhooks:read | A delivery with its attempts |
| POST | /v1/webhooks/{id}/deliveries/{deliveryId}/redeliver | webhooks:write | Send a delivery again |
| POST | /v1/webhooks/{id}/recover | webhooks:write | Resend failed deliveries |
| Tags | |||
| GET | /v1/tags | tags:read | List tags |
| POST | /v1/tags | tags:write | Create a tag |
| GET | /v1/tags/{id} | tags:read | Retrieve a tag |
| PATCH | /v1/tags/{id} | tags:write | Rename or recolour a tag |
| DELETE | /v1/tags/{id} | tags:write | Delete a tag (removed from its codes) |
| Reports | |||
| POST | /v1/reports | analytics:read | Render a PDF or CSV analytics report |
| GET | /v1/reports | analytics:read | List rendered reports |
| GET | /v1/reports/{id} | analytics:read | Poll a report and get its link |
| Zapier and Make hooks | |||
| GET | /v1/hooks | webhooks:read | List integration hooks |
| POST | /v1/hooks | webhooks:write | Subscribe a hook to one event |
| DELETE | /v1/hooks/{id} | webhooks:write | Unsubscribe a hook |
| GET | /v1/hooks/samples/{event} | webhooks:read | Recent events of a type, for a trigger's sample |
| Business cards | |||
| GET | /v1/cards | cards:read | List team business cards |
| POST | /v1/cards | cards:write | Create a business card |
| GET | /v1/cards/{id} | cards:read | Retrieve a business card |
| PATCH | /v1/cards/{id} | cards:write | Change a business card |
| POST | /v1/cards/{id}/deactivate | cards:write | Deactivate a business card |
| GET | /v1/cards/{id}/analytics | cards:read | Views, saves and taps of a card |
| Conversions | |||
| POST | /v1/conversions | conversions:write | Record a conversion |
| GET | /v1/analytics/funnel | analytics:read | Scan to revenue funnel |
| NFC tags | |||
| GET | /v1/nfc/tags | codes:read | List NFC tags |
| POST | /v1/nfc/tags | codes:write | Register an NFC tag on a code |
Request and response schemas for every route are in the API reference and the OpenAPI document. API reference →
Rate limits
- Pro
- 60 req / min · burst 20 / 10 s
- Business
- 600 req / min · burst 200 / 10 s
- Enterprise
- custom
- Export and bulk jobs · Pro / Business
- 2 concurrent / 5 concurrent
- Limits are per workspace, shared by all its API keys
- Bursts: at most 2× the per-minute rate in any 10 seconds
- 429 responses carry Retry-After in seconds
- Every response carries X-RateLimit-Limit, -Remaining and -Reset (seconds until the window resets)
- Every POST takes an Idempotency-Key header, honoured for 24 hours
Authentication
Send your secret key as a bearer token. Keys start with sk_live_, are shown once when created, carry scopes, and act in their workspace with their creator's role, at most Editor.
Authentication and scopes →