Developers
API reference
The VCTR REST API manages Links and QR Codes in one Workspace and reads their analytics. It is JSON over HTTPS, on every plan. This page is generated from the OpenAPI 3.1 document, so you can also generate a typed client from it.
Base URL https://vctr.io/api/v1
Links
List Links
GET /api/v1/links
operationId listLinks · needs a viewer key or higher
The Workspace's Links, newest first, a page at a time: pass nextCursor as cursor for the next page. Deleted Links are left out.
Parameters
| Parameter | In | Type | Description |
|---|
search | query | string | Only Links whose Code or Destination contains this, ignoring case. at most 200 characters |
|---|
kind | query | "plain" | "qr" | Only plain Links, or only the Links of QR Codes. |
|---|
metadata | query | object | Only those with all of these Partner metadata pairs: metadata[container]=c-1. |
|---|
cursor | query | string (uuid) | nextCursor from the previous page.
|
|---|
limit | query | integer | Links per page, 1 to 100. 1 to 100; default: 50 |
|---|
Vctr-Workspace | header | string | Partner keys only: the Workspace to act on, ext:<externalRef> or its id. Workspace API keys ignore it. |
|---|
Response 200
| Field | Type | Description |
|---|
datarequired | Link[] | |
|---|
nextCursorrequired | string (uuid) | null | The cursor of the next page; null on the last page. |
|---|
Errors
Example request
curl 'https://vctr.io/api/v1/links' \
-H "Authorization: Bearer $VCTR_API_KEY"
Example response
{
"data": [
{
"id": "3f6c2a9e-8b1d-4c3e-9a7f-2d5e6b8c1a40",
"code": "K7P2XQM",
"domain": "vctr.io",
"shortUrl": "https://vctr.io/K7P2XQM",
"destination": "https://example.com/spring-menu",
"kind": "plain",
"qrCodeId": "3f6c2a9e-8b1d-4c3e-9a7f-2d5e6b8c1a40",
"safety": "ok",
"createdAt": "2026-10-01T09:30:00Z",
"rules": [
{
"id": "id",
"name": "Dutch phones",
"when": {},
"to": {}
}
],
"passwordProtected": false,
"expiresAt": "2026-10-01T09:30:00Z",
"metadata": {}
}
],
"nextCursor": "3f6c2a9e-8b1d-4c3e-9a7f-2d5e6b8c1a40"
}
Create a Link
POST /api/v1/links
operationId createLink · needs a editor key or higher
Creates a Link to the Destination with a generated 7-character Code, or with the custom code given. A Code is never reused: a Retired code answers code_retired. Rules, password and expiresAt need a paid Plan (plan_limit otherwise). After a downgrade they keep working and can be removed, not changed.
Parameters
| Parameter | In | Type | Description |
|---|
Vctr-Workspace | header | string | Partner keys only: the Workspace to act on, ext:<externalRef> or its id. Workspace API keys ignore it. |
|---|
Request body application/json
| Field | Type | Description |
|---|
destinationrequired | string | A full http(s) URL. |
|---|
utm | Utm | |
|---|
rules | RuleInput[] | The Rules in order, at most 20; the first that matches a Hit decides where it goes. |
|---|
password | string | null | Asked for before the redirect; null removes it. Only a hash is kept. Paid Plans. 4 to 200 characters |
|---|
expiresAt | string (date-time) | null | When the Link stops redirecting (404 expired page); null removes it. Paid Plans. |
|---|
code | string | A custom Code: 3 to 64 letters, digits, hyphens or underscores, case-insensitive. Generated when absent. at least 1 character |
|---|
domain | string | The hostname of the Link: one of the Workspace's active custom Domains. The Default Domain when absent. at most 300 characters |
|---|
metadata | object | Partner metadata: up to 20 opaque key/value pairs to filter Links and stats on, e.g. { "container": "c-1" }. Keys are 1 to 64 letters, digits, _, . or -; values at most 256 characters. |
|---|
Response 201
Returns a Link.
Errors
Example request
curl -X POST 'https://vctr.io/api/v1/links' \
-H "Authorization: Bearer $VCTR_API_KEY" \
-H 'Content-Type: application/json' \
-d '{
"destination": "https://example.com/spring-menu",
"code": "spring-menu",
"domain": "go.example.com",
"metadata": {
"container": "c-1"
}
}'
Example response
{
"id": "3f6c2a9e-8b1d-4c3e-9a7f-2d5e6b8c1a40",
"code": "K7P2XQM",
"domain": "vctr.io",
"shortUrl": "https://vctr.io/K7P2XQM",
"destination": "https://example.com/spring-menu",
"kind": "plain",
"qrCodeId": "3f6c2a9e-8b1d-4c3e-9a7f-2d5e6b8c1a40",
"safety": "ok",
"createdAt": "2026-10-01T09:30:00Z",
"rules": [
{
"id": "id",
"name": "Dutch phones",
"when": {
"countries": [
"NL",
"BE"
],
"deviceTypes": [
"desktop"
],
"os": [
"iOS",
"Android"
],
"languages": [
"nl"
],
"time": {}
},
"to": {
"destination": "https://example.com/nl"
}
}
],
"passwordProtected": false,
"expiresAt": "2026-10-01T09:30:00Z",
"metadata": {}
}
Get a Link
GET /api/v1/links/{id}
operationId getLink · needs a viewer key or higher
Parameters
| Parameter | In | Type | Description |
|---|
idrequired | path | string (uuid) | The id. |
|---|
Vctr-Workspace | header | string | Partner keys only: the Workspace to act on, ext:<externalRef> or its id. Workspace API keys ignore it. |
|---|
Response 200
Returns a Link.
Errors
Example request
curl 'https://vctr.io/api/v1/links/3f6c2a9e-8b1d-4c3e-9a7f-2d5e6b8c1a40' \
-H "Authorization: Bearer $VCTR_API_KEY"
Example response
{
"id": "3f6c2a9e-8b1d-4c3e-9a7f-2d5e6b8c1a40",
"code": "K7P2XQM",
"domain": "vctr.io",
"shortUrl": "https://vctr.io/K7P2XQM",
"destination": "https://example.com/spring-menu",
"kind": "plain",
"qrCodeId": "3f6c2a9e-8b1d-4c3e-9a7f-2d5e6b8c1a40",
"safety": "ok",
"createdAt": "2026-10-01T09:30:00Z",
"rules": [
{
"id": "id",
"name": "Dutch phones",
"when": {
"countries": [
"NL",
"BE"
],
"deviceTypes": [
"desktop"
],
"os": [
"iOS",
"Android"
],
"languages": [
"nl"
],
"time": {}
},
"to": {
"destination": "https://example.com/nl"
}
}
],
"passwordProtected": false,
"expiresAt": "2026-10-01T09:30:00Z",
"metadata": {}
}
Change a Link
PATCH /api/v1/links/{id}
operationId updateLink · needs a editor key or higher
Changes what is sent and leaves the rest. A new Destination (or new utm parameters on it) takes effect at once and is added to the Destination history. metadata replaces all of its Partner metadata ({} clears it). Rules, password and expiresAt need a paid Plan (plan_limit otherwise). After a downgrade they keep working and can be removed, not changed.
Parameters
| Parameter | In | Type | Description |
|---|
idrequired | path | string (uuid) | The id. |
|---|
Vctr-Workspace | header | string | Partner keys only: the Workspace to act on, ext:<externalRef> or its id. Workspace API keys ignore it. |
|---|
Request body application/json
| Field | Type | Description |
|---|
destination | string | A full http(s) URL. |
|---|
utm | Utm | |
|---|
metadata | object | Partner metadata: up to 20 opaque key/value pairs to filter Links and stats on, e.g. { "container": "c-1" }. Keys are 1 to 64 letters, digits, _, . or -; values at most 256 characters. |
|---|
rules | RuleInput[] | The Rules in order, at most 20; the first that matches a Hit decides where it goes. |
|---|
password | string | null | Asked for before the redirect; null removes it. Only a hash is kept. Paid Plans. 4 to 200 characters |
|---|
expiresAt | string (date-time) | null | When the Link stops redirecting (404 expired page); null removes it. Paid Plans. |
|---|
Response 200
Returns a Link.
Errors
Example request
curl -X PATCH 'https://vctr.io/api/v1/links/3f6c2a9e-8b1d-4c3e-9a7f-2d5e6b8c1a40' \
-H "Authorization: Bearer $VCTR_API_KEY" \
-H 'Content-Type: application/json' \
-d '{
"destination": "https://example.com/spring-menu",
"metadata": {
"container": "c-1"
}
}'
Example response
{
"id": "3f6c2a9e-8b1d-4c3e-9a7f-2d5e6b8c1a40",
"code": "K7P2XQM",
"domain": "vctr.io",
"shortUrl": "https://vctr.io/K7P2XQM",
"destination": "https://example.com/spring-menu",
"kind": "plain",
"qrCodeId": "3f6c2a9e-8b1d-4c3e-9a7f-2d5e6b8c1a40",
"safety": "ok",
"createdAt": "2026-10-01T09:30:00Z",
"rules": [
{
"id": "id",
"name": "Dutch phones",
"when": {
"countries": [
"NL",
"BE"
],
"deviceTypes": [
"desktop"
],
"os": [
"iOS",
"Android"
],
"languages": [
"nl"
],
"time": {}
},
"to": {
"destination": "https://example.com/nl"
}
}
],
"passwordProtected": false,
"expiresAt": "2026-10-01T09:30:00Z",
"metadata": {}
}
Delete a Link
DELETE /api/v1/links/{id}
operationId deleteLink · needs a editor key or higher
Deletes the Link for good. Its Code is Retired: it answers 410 Gone from then on and can never be used again.
Parameters
| Parameter | In | Type | Description |
|---|
idrequired | path | string (uuid) | The id. |
|---|
Vctr-Workspace | header | string | Partner keys only: the Workspace to act on, ext:<externalRef> or its id. Workspace API keys ignore it. |
|---|
Response 204
Errors
Example request
curl -X DELETE 'https://vctr.io/api/v1/links/3f6c2a9e-8b1d-4c3e-9a7f-2d5e6b8c1a40' \
-H "Authorization: Bearer $VCTR_API_KEY"
A Link's Destination history
GET /api/v1/links/{id}/history
operationId getLinkHistory · needs a viewer key or higher
Every Destination the Link has had and when each took effect, newest first.
Parameters
| Parameter | In | Type | Description |
|---|
idrequired | path | string (uuid) | The id. |
|---|
Vctr-Workspace | header | string | Partner keys only: the Workspace to act on, ext:<externalRef> or its id. Workspace API keys ignore it. |
|---|
Response 200
Errors
Example request
curl 'https://vctr.io/api/v1/links/3f6c2a9e-8b1d-4c3e-9a7f-2d5e6b8c1a40/history' \
-H "Authorization: Bearer $VCTR_API_KEY"
Example response
{
"data": [
{
"destination": "https://example.com/spring-menu",
"effectiveAt": "2026-10-01T09:30:00Z"
}
]
}
QR Codes
List QR Codes
GET /api/v1/qr-codes
operationId listQrCodes · needs a viewer key or higher
The Workspace's QR Codes, Offline ones included, newest first, a page at a time: pass nextCursor as cursor for the next page.
Parameters
| Parameter | In | Type | Description |
|---|
cursor | query | string (uuid) | nextCursor from the previous page.
|
|---|
limit | query | integer | QR Codes per page, 1 to 100. 1 to 100; default: 50 |
|---|
metadata | query | object | Only those with all of these Partner metadata pairs: metadata[container]=c-1. |
|---|
Vctr-Workspace | header | string | Partner keys only: the Workspace to act on, ext:<externalRef> or its id. Workspace API keys ignore it. |
|---|
Response 200
| Field | Type | Description |
|---|
datarequired | QrCode[] | |
|---|
nextCursorrequired | string (uuid) | null | The cursor of the next page; null on the last page. |
|---|
Errors
Example request
curl 'https://vctr.io/api/v1/qr-codes' \
-H "Authorization: Bearer $VCTR_API_KEY"
Example response
{
"data": [
{
"id": "3f6c2a9e-8b1d-4c3e-9a7f-2d5e6b8c1a40",
"kind": "link",
"name": "Spring menu",
"design": {
"dots": "square",
"eyeFrame": "square",
"eyePupil": "square",
"foreground": "#1a237e",
"background": "#1a237e",
"errorCorrection": "L",
"logo": {
"href": "data:image/png;base64,iVBORw0KGgo…",
"size": 0.22
},
"frame": {
"text": "Scan me",
"color": "#1a237e",
"textColor": "#1a237e"
}
},
"content": "HTTPS://VCTR.IO/K7P2XQM",
"linkId": "3f6c2a9e-8b1d-4c3e-9a7f-2d5e6b8c1a40",
"code": "code",
"shortUrl": "https://vctr.io/K7P2XQM",
"destination": "https://example.com/spring-menu",
"payload": {
"type": "wifi",
"ssid": "Cafe Guest",
"password": "espresso42",
"encryption": "WPA",
"hidden": false
},
"contact": {
"firstName": "Ada",
"lastName": "de Vries",
"organization": "Example B.V.",
"title": "Owner",
"phones": [
{
"type": "cell",
"number": "+31 20 123 4567"
}
],
"emails": [
{
"type": "work",
"address": "ada@example.com"
}
],
"website": "https://example.com",
"address": {
"street": "street",
"city": "city",
"region": "region",
"postalCode": "postalCode",
"country": "country"
}
},
"hasPhoto": false,
"createdAt": "2026-10-01T09:30:00Z",
"metadata": {}
}
],
"nextCursor": "3f6c2a9e-8b1d-4c3e-9a7f-2d5e6b8c1a40"
}
Create a QR Code
POST /api/v1/qr-codes
operationId createQrCode · needs a editor key or higher
Creates a QR Code and the Link it owns, which sends Scans to the Destination. Missing design options take the defaults.
Parameters
| Parameter | In | Type | Description |
|---|
Vctr-Workspace | header | string | Partner keys only: the Workspace to act on, ext:<externalRef> or its id. Workspace API keys ignore it. |
|---|
Request body application/json
| Field | Type | Description |
|---|
namerequired | string | 1 to 100 characters |
|---|
destinationrequired | string | A full http(s) URL. |
|---|
design | object | The design; every option is optional. |
|---|
design.dots | "square" | "rounded" | "dots" | "classy" | |
|---|
design.eyeFrame | "square" | "rounded" | "circle" | |
|---|
design.eyePupil | "square" | "rounded" | "dot" | |
|---|
design.foreground | string (#rrggbb) | object | |
|---|
design.foreground.fromrequired | string (#rrggbb) | |
|---|
design.foreground.torequired | string (#rrggbb) | |
|---|
design.foreground.anglerequired | number | |
|---|
design.background | string (#rrggbb) | |
|---|
design.errorCorrection | "L" | "M" | "Q" | "H" | |
|---|
design.logo | object | |
|---|
design.logo.hrefrequired | string | at most 700000 characters |
|---|
design.logo.sizerequired | number | |
|---|
design.frame | object | |
|---|
design.frame.textrequired | string | at most 60 characters |
|---|
design.frame.colorrequired | string (#rrggbb) | |
|---|
design.frame.textColorrequired | string (#rrggbb) | |
|---|
domain | string | The hostname of the Link: one of the Workspace's active custom Domains. The Default Domain when absent. at most 300 characters |
|---|
metadata | object | Partner metadata: up to 20 opaque key/value pairs to filter Links and stats on, e.g. { "container": "c-1" }. Keys are 1 to 64 letters, digits, _, . or -; values at most 256 characters. |
|---|
Response 201
Returns a QrCode.
Errors
Example request
curl -X POST 'https://vctr.io/api/v1/qr-codes' \
-H "Authorization: Bearer $VCTR_API_KEY" \
-H 'Content-Type: application/json' \
-d '{
"name": "Spring menu",
"destination": "https://example.com/spring-menu",
"domain": "go.example.com",
"metadata": {
"container": "c-1"
}
}'
Example response
{
"id": "3f6c2a9e-8b1d-4c3e-9a7f-2d5e6b8c1a40",
"kind": "link",
"name": "Spring menu",
"design": {
"dots": "square",
"eyeFrame": "square",
"eyePupil": "square",
"foreground": "#1a237e",
"background": "#1a237e",
"errorCorrection": "L",
"logo": {
"href": "data:image/png;base64,iVBORw0KGgo…",
"size": 0.22
},
"frame": {
"text": "Scan me",
"color": "#1a237e",
"textColor": "#1a237e"
}
},
"content": "HTTPS://VCTR.IO/K7P2XQM",
"linkId": "3f6c2a9e-8b1d-4c3e-9a7f-2d5e6b8c1a40",
"code": "code",
"shortUrl": "https://vctr.io/K7P2XQM",
"destination": "https://example.com/spring-menu",
"payload": {
"type": "wifi",
"ssid": "Cafe Guest",
"password": "espresso42",
"encryption": "WPA",
"hidden": false
},
"contact": {
"firstName": "Ada",
"lastName": "de Vries",
"organization": "Example B.V.",
"title": "Owner",
"phones": [
{
"type": "cell",
"number": "+31 20 123 4567"
}
],
"emails": [
{
"type": "work",
"address": "ada@example.com"
}
],
"website": "https://example.com",
"address": {
"street": "street",
"city": "city",
"region": "region",
"postalCode": "postalCode",
"country": "country"
}
},
"hasPhoto": false,
"createdAt": "2026-10-01T09:30:00Z",
"metadata": {}
}
Get a QR Code
GET /api/v1/qr-codes/{id}
operationId getQrCode · needs a viewer key or higher
Parameters
| Parameter | In | Type | Description |
|---|
idrequired | path | string (uuid) | The id. |
|---|
Vctr-Workspace | header | string | Partner keys only: the Workspace to act on, ext:<externalRef> or its id. Workspace API keys ignore it. |
|---|
Response 200
Returns a QrCode.
Errors
Example request
curl 'https://vctr.io/api/v1/qr-codes/3f6c2a9e-8b1d-4c3e-9a7f-2d5e6b8c1a40' \
-H "Authorization: Bearer $VCTR_API_KEY"
Example response
{
"id": "3f6c2a9e-8b1d-4c3e-9a7f-2d5e6b8c1a40",
"kind": "link",
"name": "Spring menu",
"design": {
"dots": "square",
"eyeFrame": "square",
"eyePupil": "square",
"foreground": "#1a237e",
"background": "#1a237e",
"errorCorrection": "L",
"logo": {
"href": "data:image/png;base64,iVBORw0KGgo…",
"size": 0.22
},
"frame": {
"text": "Scan me",
"color": "#1a237e",
"textColor": "#1a237e"
}
},
"content": "HTTPS://VCTR.IO/K7P2XQM",
"linkId": "3f6c2a9e-8b1d-4c3e-9a7f-2d5e6b8c1a40",
"code": "code",
"shortUrl": "https://vctr.io/K7P2XQM",
"destination": "https://example.com/spring-menu",
"payload": {
"type": "wifi",
"ssid": "Cafe Guest",
"password": "espresso42",
"encryption": "WPA",
"hidden": false
},
"contact": {
"firstName": "Ada",
"lastName": "de Vries",
"organization": "Example B.V.",
"title": "Owner",
"phones": [
{
"type": "cell",
"number": "+31 20 123 4567"
}
],
"emails": [
{
"type": "work",
"address": "ada@example.com"
}
],
"website": "https://example.com",
"address": {
"street": "street",
"city": "city",
"region": "region",
"postalCode": "postalCode",
"country": "country"
}
},
"hasPhoto": false,
"createdAt": "2026-10-01T09:30:00Z",
"metadata": {}
}
Change a QR Code's Partner metadata
PATCH /api/v1/qr-codes/{id}
operationId updateQrCode · needs a editor key or higher
Replaces all of the QR Code's Partner metadata ({} clears it); they are its Link's. Change where it points with PATCH /links/{linkId}.
Parameters
| Parameter | In | Type | Description |
|---|
idrequired | path | string (uuid) | The id. |
|---|
Vctr-Workspace | header | string | Partner keys only: the Workspace to act on, ext:<externalRef> or its id. Workspace API keys ignore it. |
|---|
Request body application/json
| Field | Type | Description |
|---|
metadatarequired | object | Partner metadata: up to 20 opaque key/value pairs to filter Links and stats on, e.g. { "container": "c-1" }. Keys are 1 to 64 letters, digits, _, . or -; values at most 256 characters. |
|---|
Response 200
Returns a QrCode.
Errors
Example request
curl -X PATCH 'https://vctr.io/api/v1/qr-codes/3f6c2a9e-8b1d-4c3e-9a7f-2d5e6b8c1a40' \
-H "Authorization: Bearer $VCTR_API_KEY" \
-H 'Content-Type: application/json' \
-d '{
"metadata": {
"container": "c-1"
}
}'
Example response
{
"id": "3f6c2a9e-8b1d-4c3e-9a7f-2d5e6b8c1a40",
"kind": "link",
"name": "Spring menu",
"design": {
"dots": "square",
"eyeFrame": "square",
"eyePupil": "square",
"foreground": "#1a237e",
"background": "#1a237e",
"errorCorrection": "L",
"logo": {
"href": "data:image/png;base64,iVBORw0KGgo…",
"size": 0.22
},
"frame": {
"text": "Scan me",
"color": "#1a237e",
"textColor": "#1a237e"
}
},
"content": "HTTPS://VCTR.IO/K7P2XQM",
"linkId": "3f6c2a9e-8b1d-4c3e-9a7f-2d5e6b8c1a40",
"code": "code",
"shortUrl": "https://vctr.io/K7P2XQM",
"destination": "https://example.com/spring-menu",
"payload": {
"type": "wifi",
"ssid": "Cafe Guest",
"password": "espresso42",
"encryption": "WPA",
"hidden": false
},
"contact": {
"firstName": "Ada",
"lastName": "de Vries",
"organization": "Example B.V.",
"title": "Owner",
"phones": [
{
"type": "cell",
"number": "+31 20 123 4567"
}
],
"emails": [
{
"type": "work",
"address": "ada@example.com"
}
],
"website": "https://example.com",
"address": {
"street": "street",
"city": "city",
"region": "region",
"postalCode": "postalCode",
"country": "country"
}
},
"hasPhoto": false,
"createdAt": "2026-10-01T09:30:00Z",
"metadata": {}
}
Export a QR Code as SVG
GET /api/v1/qr-codes/{id}/export.svg
operationId exportQrCodeSVG · needs a viewer key or higher
A vector file, identical to the preview in the app. Style options override the stored design for this file only.
Parameters
| Parameter | In | Type | Description |
|---|
idrequired | path | string (uuid) | The id. |
|---|
dots | query | "square" | "rounded" | "dots" | "classy" | |
|---|
eyeFrame | query | "square" | "rounded" | "circle" | |
|---|
eyePupil | query | "square" | "rounded" | "dot" | |
|---|
foreground | query | string (#rrggbb) | Foreground color; on a gradient, where it starts. |
|---|
foregroundTo | query | string (#rrggbb) | Makes the foreground a linear gradient ending in this color (or changes where it ends). |
|---|
gradientAngle | query | number | Gradient angle in degrees: 0 runs left to right (the default), 90 top to bottom. Applies to a gradient only. |
|---|
background | query | string (#rrggbb) | |
|---|
errorCorrection | query | "L" | "M" | "Q" | "H" | |
|---|
frameText | query | string | Adds a frame with this call to action, or changes its text. Empty removes the frame. at most 60 characters |
|---|
frameColor | query | string (#rrggbb) | Frame color, when there is a frame. |
|---|
frameTextColor | query | string (#rrggbb) | Call-to-action color, when there is a frame. |
|---|
Vctr-Workspace | header | string | Partner keys only: the Workspace to act on, ext:<externalRef> or its id. Workspace API keys ignore it. |
|---|
Response 200
Content type image/svg+xml.
Errors
Example request
curl 'https://vctr.io/api/v1/qr-codes/3f6c2a9e-8b1d-4c3e-9a7f-2d5e6b8c1a40/export.svg' \
-H "Authorization: Bearer $VCTR_API_KEY" \
--output qr-code.svg
Export a QR Code as PNG
GET /api/v1/qr-codes/{id}/export.png
operationId exportQrCodePNG · needs a viewer key or higher
A raster image. Style options override the stored design for this file only.
Parameters
| Parameter | In | Type | Description |
|---|
idrequired | path | string (uuid) | The id. |
|---|
dots | query | "square" | "rounded" | "dots" | "classy" | |
|---|
eyeFrame | query | "square" | "rounded" | "circle" | |
|---|
eyePupil | query | "square" | "rounded" | "dot" | |
|---|
foreground | query | string (#rrggbb) | Foreground color; on a gradient, where it starts. |
|---|
foregroundTo | query | string (#rrggbb) | Makes the foreground a linear gradient ending in this color (or changes where it ends). |
|---|
gradientAngle | query | number | Gradient angle in degrees: 0 runs left to right (the default), 90 top to bottom. Applies to a gradient only. |
|---|
background | query | string (#rrggbb) | |
|---|
errorCorrection | query | "L" | "M" | "Q" | "H" | |
|---|
frameText | query | string | Adds a frame with this call to action, or changes its text. Empty removes the frame. at most 60 characters |
|---|
frameColor | query | string (#rrggbb) | Frame color, when there is a frame. |
|---|
frameTextColor | query | string (#rrggbb) | Call-to-action color, when there is a frame. |
|---|
width | query | integer | Width in pixels, 16 to 4096; 1024 by default. |
|---|
Vctr-Workspace | header | string | Partner keys only: the Workspace to act on, ext:<externalRef> or its id. Workspace API keys ignore it. |
|---|
Response 200
Content type image/png.
Errors
Example request
curl 'https://vctr.io/api/v1/qr-codes/3f6c2a9e-8b1d-4c3e-9a7f-2d5e6b8c1a40/export.png' \
-H "Authorization: Bearer $VCTR_API_KEY" \
--output qr-code.png
Export a QR Code as PDF
GET /api/v1/qr-codes/{id}/export.pdf
operationId exportQrCodePDF · needs a viewer key or higher
A one-page PDF for print, the code rasterized at 600 dpi. Style options override the stored design for this file only.
Parameters
| Parameter | In | Type | Description |
|---|
idrequired | path | string (uuid) | The id. |
|---|
dots | query | "square" | "rounded" | "dots" | "classy" | |
|---|
eyeFrame | query | "square" | "rounded" | "circle" | |
|---|
eyePupil | query | "square" | "rounded" | "dot" | |
|---|
foreground | query | string (#rrggbb) | Foreground color; on a gradient, where it starts. |
|---|
foregroundTo | query | string (#rrggbb) | Makes the foreground a linear gradient ending in this color (or changes where it ends). |
|---|
gradientAngle | query | number | Gradient angle in degrees: 0 runs left to right (the default), 90 top to bottom. Applies to a gradient only. |
|---|
background | query | string (#rrggbb) | |
|---|
errorCorrection | query | "L" | "M" | "Q" | "H" | |
|---|
frameText | query | string | Adds a frame with this call to action, or changes its text. Empty removes the frame. at most 60 characters |
|---|
frameColor | query | string (#rrggbb) | Frame color, when there is a frame. |
|---|
frameTextColor | query | string (#rrggbb) | Call-to-action color, when there is a frame. |
|---|
widthMm | query | number | Printed width in millimetres, 10 to 170; 50 by default. |
|---|
Vctr-Workspace | header | string | Partner keys only: the Workspace to act on, ext:<externalRef> or its id. Workspace API keys ignore it. |
|---|
Response 200
Content type application/pdf.
Errors
Example request
curl 'https://vctr.io/api/v1/qr-codes/3f6c2a9e-8b1d-4c3e-9a7f-2d5e6b8c1a40/export.pdf' \
-H "Authorization: Bearer $VCTR_API_KEY" \
--output qr-code.pdf
Stats
Hits over time for the Workspace
GET /api/v1/stats/timeseries
operationId getWorkspaceTimeseries · needs a viewer key or higher
Hits and Daily unique visitors per hour or UTC day.
Parameters
| Parameter | In | Type | Description |
|---|
fromrequired | query | string (date-time) | Start of the range (inclusive), ISO 8601. |
|---|
torequired | query | string (date-time) | End of the range (exclusive), ISO 8601. |
|---|
includeBots | query | boolean | Count Bot hits too; they are left out by default. default: false |
|---|
intervalrequired | query | "hour" | "day" | Bucket size: hours, or UTC days. |
|---|
metadata | query | object | Only those with all of these Partner metadata pairs: metadata[container]=c-1. |
|---|
Vctr-Workspace | header | string | Partner keys only: the Workspace to act on, ext:<externalRef> or its id. Workspace API keys ignore it. |
|---|
Response 200
Returns a Timeseries.
Errors
Example request
curl 'https://vctr.io/api/v1/stats/timeseries?from=2026-10-01T00:00:00Z&to=2026-11-01T00:00:00Z&interval=hour' \
-H "Authorization: Bearer $VCTR_API_KEY"
Example response
{
"interval": "hour",
"buckets": [
{
"start": "2026-10-01T09:30:00Z",
"hits": 1204,
"visitors": 873
}
],
"untracked": 0,
"historyStart": "2026-10-01T09:30:00Z"
}
Hits by country, device, referrer… for the Workspace
GET /api/v1/stats/breakdown
operationId getWorkspaceBreakdown · needs a viewer key or higher
The most common values of one dimension, by Hits.
Parameters
| Parameter | In | Type | Description |
|---|
fromrequired | query | string (date-time) | Start of the range (inclusive), ISO 8601. |
|---|
torequired | query | string (date-time) | End of the range (exclusive), ISO 8601. |
|---|
includeBots | query | boolean | Count Bot hits too; they are left out by default. default: false |
|---|
dimensionrequired | query | "country" | "region" | "city" | "device_type" | "os" | "browser" | "language" | "referrer_host" | "channel" | "rule" | "variant" | What to group Hits by. |
|---|
limit | query | integer | The most common values to return, 1 to 100. 1 to 100; default: 10 |
|---|
metadata | query | object | Only those with all of these Partner metadata pairs: metadata[container]=c-1. |
|---|
Vctr-Workspace | header | string | Partner keys only: the Workspace to act on, ext:<externalRef> or its id. Workspace API keys ignore it. |
|---|
Response 200
Returns a Breakdown.
Errors
Example request
curl 'https://vctr.io/api/v1/stats/breakdown?from=2026-10-01T00:00:00Z&to=2026-11-01T00:00:00Z&dimension=country' \
-H "Authorization: Bearer $VCTR_API_KEY"
Example response
{
"dimension": "country",
"rows": [
{
"value": "NL",
"hits": 1204,
"visitors": 873
}
]
}
Hits over time for a Link
GET /api/v1/links/{id}/stats/timeseries
operationId getLinkTimeseries · needs a viewer key or higher
Hits and Daily unique visitors per hour or UTC day.
Parameters
| Parameter | In | Type | Description |
|---|
idrequired | path | string (uuid) | The id. |
|---|
fromrequired | query | string (date-time) | Start of the range (inclusive), ISO 8601. |
|---|
torequired | query | string (date-time) | End of the range (exclusive), ISO 8601. |
|---|
includeBots | query | boolean | Count Bot hits too; they are left out by default. default: false |
|---|
intervalrequired | query | "hour" | "day" | Bucket size: hours, or UTC days. |
|---|
Vctr-Workspace | header | string | Partner keys only: the Workspace to act on, ext:<externalRef> or its id. Workspace API keys ignore it. |
|---|
Response 200
Returns a Timeseries.
Errors
Example request
curl 'https://vctr.io/api/v1/links/3f6c2a9e-8b1d-4c3e-9a7f-2d5e6b8c1a40/stats/timeseries?from=2026-10-01T00:00:00Z&to=2026-11-01T00:00:00Z&interval=hour' \
-H "Authorization: Bearer $VCTR_API_KEY"
Example response
{
"interval": "hour",
"buckets": [
{
"start": "2026-10-01T09:30:00Z",
"hits": 1204,
"visitors": 873
}
],
"untracked": 0,
"historyStart": "2026-10-01T09:30:00Z"
}
Hits by country, device, referrer… for a Link
GET /api/v1/links/{id}/stats/breakdown
operationId getLinkBreakdown · needs a viewer key or higher
The most common values of one dimension, by Hits.
Parameters
| Parameter | In | Type | Description |
|---|
idrequired | path | string (uuid) | The id. |
|---|
fromrequired | query | string (date-time) | Start of the range (inclusive), ISO 8601. |
|---|
torequired | query | string (date-time) | End of the range (exclusive), ISO 8601. |
|---|
includeBots | query | boolean | Count Bot hits too; they are left out by default. default: false |
|---|
dimensionrequired | query | "country" | "region" | "city" | "device_type" | "os" | "browser" | "language" | "referrer_host" | "channel" | "rule" | "variant" | What to group Hits by. |
|---|
limit | query | integer | The most common values to return, 1 to 100. 1 to 100; default: 10 |
|---|
Vctr-Workspace | header | string | Partner keys only: the Workspace to act on, ext:<externalRef> or its id. Workspace API keys ignore it. |
|---|
Response 200
Returns a Breakdown.
Errors
Example request
curl 'https://vctr.io/api/v1/links/3f6c2a9e-8b1d-4c3e-9a7f-2d5e6b8c1a40/stats/breakdown?from=2026-10-01T00:00:00Z&to=2026-11-01T00:00:00Z&dimension=country' \
-H "Authorization: Bearer $VCTR_API_KEY"
Example response
{
"dimension": "country",
"rows": [
{
"value": "NL",
"hits": 1204,
"visitors": 873
}
]
}
Hits over time for a QR Code (its Scans)
GET /api/v1/qr-codes/{id}/stats/timeseries
operationId getQrCodeTimeseries · needs a viewer key or higher
Hits and Daily unique visitors per hour or UTC day.
Parameters
| Parameter | In | Type | Description |
|---|
idrequired | path | string (uuid) | The id. |
|---|
fromrequired | query | string (date-time) | Start of the range (inclusive), ISO 8601. |
|---|
torequired | query | string (date-time) | End of the range (exclusive), ISO 8601. |
|---|
includeBots | query | boolean | Count Bot hits too; they are left out by default. default: false |
|---|
intervalrequired | query | "hour" | "day" | Bucket size: hours, or UTC days. |
|---|
Vctr-Workspace | header | string | Partner keys only: the Workspace to act on, ext:<externalRef> or its id. Workspace API keys ignore it. |
|---|
Response 200
Returns a Timeseries.
Errors
Example request
curl 'https://vctr.io/api/v1/qr-codes/3f6c2a9e-8b1d-4c3e-9a7f-2d5e6b8c1a40/stats/timeseries?from=2026-10-01T00:00:00Z&to=2026-11-01T00:00:00Z&interval=hour' \
-H "Authorization: Bearer $VCTR_API_KEY"
Example response
{
"interval": "hour",
"buckets": [
{
"start": "2026-10-01T09:30:00Z",
"hits": 1204,
"visitors": 873
}
],
"untracked": 0,
"historyStart": "2026-10-01T09:30:00Z"
}
Hits by country, device, referrer… for a QR Code (its Scans)
GET /api/v1/qr-codes/{id}/stats/breakdown
operationId getQrCodeBreakdown · needs a viewer key or higher
The most common values of one dimension, by Hits.
Parameters
| Parameter | In | Type | Description |
|---|
idrequired | path | string (uuid) | The id. |
|---|
fromrequired | query | string (date-time) | Start of the range (inclusive), ISO 8601. |
|---|
torequired | query | string (date-time) | End of the range (exclusive), ISO 8601. |
|---|
includeBots | query | boolean | Count Bot hits too; they are left out by default. default: false |
|---|
dimensionrequired | query | "country" | "region" | "city" | "device_type" | "os" | "browser" | "language" | "referrer_host" | "channel" | "rule" | "variant" | What to group Hits by. |
|---|
limit | query | integer | The most common values to return, 1 to 100. 1 to 100; default: 10 |
|---|
Vctr-Workspace | header | string | Partner keys only: the Workspace to act on, ext:<externalRef> or its id. Workspace API keys ignore it. |
|---|
Response 200
Returns a Breakdown.
Errors
Example request
curl 'https://vctr.io/api/v1/qr-codes/3f6c2a9e-8b1d-4c3e-9a7f-2d5e6b8c1a40/stats/breakdown?from=2026-10-01T00:00:00Z&to=2026-11-01T00:00:00Z&dimension=country' \
-H "Authorization: Bearer $VCTR_API_KEY"
Example response
{
"dimension": "country",
"rows": [
{
"value": "NL",
"hits": 1204,
"visitors": 873
}
]
}
Domains
List custom Domains
GET /api/v1/domains
operationId listDomains · needs a editor key or higher
Parameters
| Parameter | In | Type | Description |
|---|
Vctr-Workspace | header | string | Partner keys only: the Workspace to act on, ext:<externalRef> or its id. Workspace API keys ignore it. |
|---|
Response 200
The Workspace's custom Domains, oldest first.
| Field | Type | Description |
|---|
datarequired | Domain[] | |
|---|
Errors
Example request
curl 'https://vctr.io/api/v1/domains' \
-H "Authorization: Bearer $VCTR_API_KEY"
Example response
{
"data": [
{
"id": "3f6c2a9e-8b1d-4c3e-9a7f-2d5e6b8c1a40",
"hostname": "go.example.com",
"status": "pending",
"dnsRecords": [
{
"type": "CNAME",
"name": "Spring menu",
"value": "NL"
}
],
"verification": {
"verifiedAt": "2026-10-01T09:30:00Z",
"checkedAt": "2026-10-01T09:30:00Z",
"problem": "problem"
},
"certificate": {
"status": "none",
"message": "message"
},
"dangling": {
"since": "2026-10-01T09:30:00Z",
"disablesAt": "2026-10-01T09:30:00Z"
},
"fallbackDestination": "https://example.com/spring-menu",
"createdAt": "2026-10-01T09:30:00Z"
}
]
}
Add a custom Domain
POST /api/v1/domains
operationId addDomain · needs a editor key or higher
Adds a hostname such as go.example.com. Create the dnsRecords it answers with at the DNS provider: VCTR checks them every minute, then requests a certificate and starts serving Links on it (status becomes active). Needs an editor key: keys act for the Admin who created them.
Parameters
| Parameter | In | Type | Description |
|---|
Vctr-Workspace | header | string | Partner keys only: the Workspace to act on, ext:<externalRef> or its id. Workspace API keys ignore it. |
|---|
Request body application/json
| Field | Type | Description |
|---|
hostnamerequired | string | at most 300 characters |
|---|
Response 201
The new Domain, pending until its DNS records are found.
Returns a Domain.
Errors
Example request
curl -X POST 'https://vctr.io/api/v1/domains' \
-H "Authorization: Bearer $VCTR_API_KEY" \
-H 'Content-Type: application/json' \
-d '{
"hostname": "go.example.com"
}'
Example response
{
"id": "3f6c2a9e-8b1d-4c3e-9a7f-2d5e6b8c1a40",
"hostname": "go.example.com",
"status": "pending",
"dnsRecords": [
{
"type": "CNAME",
"name": "Spring menu",
"value": "NL"
}
],
"verification": {
"verifiedAt": "2026-10-01T09:30:00Z",
"checkedAt": "2026-10-01T09:30:00Z",
"problem": "problem"
},
"certificate": {
"status": "none",
"message": "message"
},
"dangling": {
"since": "2026-10-01T09:30:00Z",
"disablesAt": "2026-10-01T09:30:00Z"
},
"fallbackDestination": "https://example.com/spring-menu",
"createdAt": "2026-10-01T09:30:00Z"
}
Get a custom Domain
GET /api/v1/domains/{id}
operationId getDomain · needs a editor key or higher
The Domain with its verification and certificate status.
Parameters
| Parameter | In | Type | Description |
|---|
idrequired | path | string (uuid) | The id. |
|---|
Vctr-Workspace | header | string | Partner keys only: the Workspace to act on, ext:<externalRef> or its id. Workspace API keys ignore it. |
|---|
Response 200
Returns a Domain.
Errors
Example request
curl 'https://vctr.io/api/v1/domains/3f6c2a9e-8b1d-4c3e-9a7f-2d5e6b8c1a40' \
-H "Authorization: Bearer $VCTR_API_KEY"
Example response
{
"id": "3f6c2a9e-8b1d-4c3e-9a7f-2d5e6b8c1a40",
"hostname": "go.example.com",
"status": "pending",
"dnsRecords": [
{
"type": "CNAME",
"name": "Spring menu",
"value": "NL"
}
],
"verification": {
"verifiedAt": "2026-10-01T09:30:00Z",
"checkedAt": "2026-10-01T09:30:00Z",
"problem": "problem"
},
"certificate": {
"status": "none",
"message": "message"
},
"dangling": {
"since": "2026-10-01T09:30:00Z",
"disablesAt": "2026-10-01T09:30:00Z"
},
"fallbackDestination": "https://example.com/spring-menu",
"createdAt": "2026-10-01T09:30:00Z"
}
Set a custom Domain's fallback Destination
PATCH /api/v1/domains/{id}
operationId updateDomain · needs a editor key or higher
Parameters
| Parameter | In | Type | Description |
|---|
idrequired | path | string (uuid) | The id. |
|---|
Vctr-Workspace | header | string | Partner keys only: the Workspace to act on, ext:<externalRef> or its id. Workspace API keys ignore it. |
|---|
Request body application/json
| Field | Type | Description |
|---|
fallbackDestinationrequired | string | null | A full http(s) URL on another hostname, or null for a 404. at most 2048 characters |
|---|
Response 200
Returns a Domain.
Errors
Example request
curl -X PATCH 'https://vctr.io/api/v1/domains/3f6c2a9e-8b1d-4c3e-9a7f-2d5e6b8c1a40' \
-H "Authorization: Bearer $VCTR_API_KEY" \
-H 'Content-Type: application/json' \
-d '{
"fallbackDestination": "fallbackDestination"
}'
Example response
{
"id": "3f6c2a9e-8b1d-4c3e-9a7f-2d5e6b8c1a40",
"hostname": "go.example.com",
"status": "pending",
"dnsRecords": [
{
"type": "CNAME",
"name": "Spring menu",
"value": "NL"
}
],
"verification": {
"verifiedAt": "2026-10-01T09:30:00Z",
"checkedAt": "2026-10-01T09:30:00Z",
"problem": "problem"
},
"certificate": {
"status": "none",
"message": "message"
},
"dangling": {
"since": "2026-10-01T09:30:00Z",
"disablesAt": "2026-10-01T09:30:00Z"
},
"fallbackDestination": "https://example.com/spring-menu",
"createdAt": "2026-10-01T09:30:00Z"
}
Remove a custom Domain
DELETE /api/v1/domains/{id}
operationId removeDomain · needs a editor key or higher
Removes the Domain and deletes every Link on it. Their Codes are Retired on the hostname for good: they answer 410 Gone and can never be used again, even if the hostname is added again by any Workspace. confirm must repeat the hostname.
Parameters
| Parameter | In | Type | Description |
|---|
idrequired | path | string (uuid) | The id. |
|---|
confirmrequired | query | string | The hostname of the Domain, to confirm. at most 300 characters |
|---|
Vctr-Workspace | header | string | Partner keys only: the Workspace to act on, ext:<externalRef> or its id. Workspace API keys ignore it. |
|---|
Response 204
Errors
Example request
curl -X DELETE 'https://vctr.io/api/v1/domains/3f6c2a9e-8b1d-4c3e-9a7f-2d5e6b8c1a40?confirm=go.example.com' \
-H "Authorization: Bearer $VCTR_API_KEY"
Schemas
Utm
UTM parameters written into the Destination as utm_source, utm_medium, … replacing ones already there; an empty string removes one.
| Field | Type | Description |
|---|
source | string | at most 200 characters |
|---|
medium | string | at most 200 characters |
|---|
campaign | string | at most 200 characters |
|---|
term | string | at most 200 characters |
|---|
content | string | at most 200 characters |
|---|
Example
{
"source": "poster",
"medium": "print",
"campaign": "spring"
}
| Field | Type | Description |
|---|
id | string | Stable id, recorded on each Hit; assigned when absent. Keep it when editing so stats stay together. |
|---|
name | string | What the Rule is for, for people. at most 80 characters; default: "" |
|---|
when | RuleConditions | |
|---|
torequired | object | object | Where matching Hits go: one Destination, or a Split. |
|---|
to.destinationrequired | string | A full http(s) URL. |
|---|
to.splitrequired | object[] | Weighted Destinations the Rule divides Hits across. |
|---|
to.split[].id | string | Stable id, recorded on each Hit; assigned when absent. Keep it when editing so stats stay together. |
|---|
to.split[].destinationrequired | string | A full http(s) URL. |
|---|
to.split[].weightrequired | integer | Relative share of Hits. 1 to 100 |
|---|
Example
{
"id": "id",
"name": "Dutch phones",
"when": {
"countries": [
"NL",
"BE"
],
"deviceTypes": [
"desktop"
],
"os": [
"iOS",
"Android"
],
"languages": [
"nl"
],
"time": {
"timezone": "Europe/Amsterdam",
"days": [
1
],
"start": "09:00",
"end": "17:30"
}
},
"to": {
"destination": "https://example.com/nl"
}
}
RuleConditions
All conditions given must hold; within one, any value matches. None at all matches every Hit.
| Field | Type | Description |
|---|
countries | string[] | ISO 3166-1 alpha-2 country codes. |
|---|
deviceTypes | "desktop" | "smartphone" | "tablet" | "phablet" | "feature_phone" | "console" | "tv" | "car_browser" | "smart_display" | "camera" | "portable_media_player" | "smart_speaker" | "wearable" | "peripheral"[] | |
|---|
os | string[] | Operating systems as the device detector names them, ignoring case. |
|---|
languages | string[] | Primary subtags of the visitor's preferred language (Accept-Language). |
|---|
time | TimeWindow | |
|---|
Example
{
"countries": [
"NL",
"BE"
],
"deviceTypes": [
"desktop"
],
"os": [
"iOS",
"Android"
],
"languages": [
"nl"
],
"time": {
"timezone": "Europe/Amsterdam",
"days": [
1
],
"start": "09:00",
"end": "17:30"
}
}
TimeWindow
Matches Hits made between start and end, local time in timezone.
| Field | Type | Description |
|---|
timezonerequired | string | |
|---|
days | integer[] | ISO weekdays in timezone, 1 = Monday … 7 = Sunday. Every day when absent. |
|---|
startrequired | string | Local start time, inclusive. |
|---|
endrequired | string | Local end time, exclusive; earlier than start runs past midnight. |
|---|
Example
{
"timezone": "Europe/Amsterdam",
"days": [
1
],
"start": "09:00",
"end": "17:30"
}
Workspace
| Field | Type | Description |
|---|
idrequired | string (uuid) | |
|---|
namerequired | string | |
|---|
rolerequired | "editor" | "viewer" | What the key may do: editor manages Links and QR Codes, viewer only reads. |
|---|
Example
{
"id": "3f6c2a9e-8b1d-4c3e-9a7f-2d5e6b8c1a40",
"name": "Spring menu",
"role": "editor"
}
Error
| Field | Type | Description |
|---|
errorrequired | object | |
|---|
error.coderequired | "validation_failed" | "unauthorized" | "forbidden" | "not_found" | "plan_limit" | "workspace_disabled" | "code_taken" | "code_retired" | "domain_taken" | "payload_too_large" | "unsupported_media_type" | "invalid_code" | "invalid_destination" | "invalid_hostname" | "invalid_domain" | "confirmation_mismatch" | "invalid_design" | "content_too_large" | "rate_limited" | "internal_error" | Stable, machine-readable error code. |
|---|
error.messagerequired | string | What went wrong, for people. |
|---|
error.issues | object[] | With validation_failed: each invalid field or parameter. |
|---|
error.issues[].pathrequired | string | |
|---|
error.issues[].messagerequired | string | |
|---|
Example
{
"error": {
"code": "validation_failed",
"message": "message",
"issues": [
{
"path": "path",
"message": "message"
}
]
}
}
Link
| Field | Type | Description |
|---|
idrequired | string (uuid) | |
|---|
coderequired | string | The Code, uppercase; Codes are case-insensitive and never change. |
|---|
domainrequired | string | The hostname of the Link's Domain. |
|---|
shortUrlrequired | string (uri) | |
|---|
destinationrequired | string (uri) | Where the Link sends visitors now. |
|---|
kindrequired | "plain" | "qr" | qr when the Link belongs to a QR Code (its Hits are Scans).
|
|---|
qrCodeIdrequired | string (uuid) | null | The QR Code that owns this Link, if any. |
|---|
safetyrequired | "ok" | "flagged" | "taken_down" | flagged while its Destination matched a threat list: visitors see a warning instead of the redirect until VCTR staff review it.
|
|---|
createdAtrequired | string (date-time) | |
|---|
rulesrequired | Rule[] | Rules in order: the first that matches a Hit sends it elsewhere than destination. Paid Plans. |
|---|
passwordProtectedrequired | boolean | Whether visitors must enter a password first. Paid Plans. |
|---|
expiresAtrequired | string (date-time) | null | When the Link stops redirecting; null for never. Paid Plans. |
|---|
metadatarequired | object | Partner metadata: the key/value pairs set on the Link, never interpreted by VCTR. |
|---|
Example
{
"id": "3f6c2a9e-8b1d-4c3e-9a7f-2d5e6b8c1a40",
"code": "K7P2XQM",
"domain": "vctr.io",
"shortUrl": "https://vctr.io/K7P2XQM",
"destination": "https://example.com/spring-menu",
"kind": "plain",
"qrCodeId": "3f6c2a9e-8b1d-4c3e-9a7f-2d5e6b8c1a40",
"safety": "ok",
"createdAt": "2026-10-01T09:30:00Z",
"rules": [
{
"id": "id",
"name": "Dutch phones",
"when": {
"countries": [
"NL",
"BE"
],
"deviceTypes": [
"desktop"
],
"os": [
"iOS",
"Android"
],
"languages": [
"nl"
],
"time": {
"timezone": "Europe/Amsterdam",
"days": [
null
],
"start": "09:00",
"end": "17:30"
}
},
"to": {
"destination": "https://example.com/nl"
}
}
],
"passwordProtected": false,
"expiresAt": "2026-10-01T09:30:00Z",
"metadata": {}
}
Rule
| Field | Type | Description |
|---|
idrequired | string | Stable id, recorded on each Hit; assigned when absent. Keep it when editing so stats stay together. |
|---|
namerequired | string | What the Rule is for, for people. at most 80 characters; default: "" |
|---|
whenrequired | RuleConditionsOutput | |
|---|
torequired | object | object | Where matching Hits go: one Destination, or a Split. |
|---|
to.destinationrequired | string | A full http(s) URL. |
|---|
to.splitrequired | object[] | Weighted Destinations the Rule divides Hits across. |
|---|
to.split[].idrequired | string | Stable id, recorded on each Hit; assigned when absent. Keep it when editing so stats stay together. |
|---|
to.split[].destinationrequired | string | A full http(s) URL. |
|---|
to.split[].weightrequired | integer | Relative share of Hits. 1 to 100 |
|---|
Example
{
"id": "id",
"name": "Dutch phones",
"when": {
"countries": [
"NL",
"BE"
],
"deviceTypes": [
"desktop"
],
"os": [
"iOS",
"Android"
],
"languages": [
"nl"
],
"time": {
"timezone": "Europe/Amsterdam",
"days": [
1
],
"start": "09:00",
"end": "17:30"
}
},
"to": {
"destination": "https://example.com/nl"
}
}
DestinationChange
| Field | Type | Description |
|---|
destinationrequired | string (uri) | |
|---|
effectiveAtrequired | string (date-time) | |
|---|
Example
{
"destination": "https://example.com/spring-menu",
"effectiveAt": "2026-10-01T09:30:00Z"
}
QrCode
| Field | Type | Description |
|---|
idrequired | string (uuid) | |
|---|
kindrequired | "link" | "vcard" | "wifi" | "text" | link and vcard QR Codes own a Link; wifi and text are Offline QR Codes, with no Link and no Hits.
|
|---|
namerequired | string | |
|---|
designrequired | QrDesign | |
|---|
contentrequired | string | Exactly what the code encodes: the uppercase Link URL, or the Offline payload. |
|---|
linkIdrequired | string (uuid) | null | The Link this QR Code owns, whose Hits are its Scans. |
|---|
coderequired | string | null | |
|---|
shortUrlrequired | string (uri) | null | |
|---|
destinationrequired | string (uri) | null | |
|---|
payloadrequired | object | object | null | What an Offline QR Code holds. |
|---|
payload.typerequired | "wifi" | when type is "wifi" |
|---|
payload.ssidrequired | string | when type is "wifi" |
|---|
payload.passwordrequired | string | when type is "wifi" |
|---|
payload.encryptionrequired | "WPA" | "WEP" | "nopass" | when type is "wifi" |
|---|
payload.hiddenrequired | boolean | when type is "wifi" |
|---|
payload.typerequired | "text" | when type is "text" |
|---|
payload.textrequired | string | when type is "text" |
|---|
contactrequired | object | null | A vCard QR Code's contact details. |
|---|
contact.firstNamerequired | string | |
|---|
contact.lastNamerequired | string | |
|---|
contact.organizationrequired | string | |
|---|
contact.titlerequired | string | |
|---|
contact.phonesrequired | object[] | |
|---|
contact.phones[].typerequired | "cell" | "work" | "home" | |
|---|
contact.phones[].numberrequired | string | |
|---|
contact.emailsrequired | object[] | |
|---|
contact.emails[].typerequired | "work" | "home" | |
|---|
contact.emails[].addressrequired | string | |
|---|
contact.websiterequired | string | |
|---|
contact.addressrequired | object | |
|---|
contact.address.streetrequired | string | |
|---|
contact.address.cityrequired | string | |
|---|
contact.address.regionrequired | string | |
|---|
contact.address.postalCoderequired | string | |
|---|
contact.address.countryrequired | string | |
|---|
hasPhotorequired | boolean | |
|---|
createdAtrequired | string (date-time) | |
|---|
metadatarequired | object | Partner metadata: its Link's key/value pairs; empty for Offline QR Codes. |
|---|
Example
{
"id": "3f6c2a9e-8b1d-4c3e-9a7f-2d5e6b8c1a40",
"kind": "link",
"name": "Spring menu",
"design": {
"dots": "square",
"eyeFrame": "square",
"eyePupil": "square",
"foreground": "#1a237e",
"background": "#1a237e",
"errorCorrection": "L",
"logo": {
"href": "data:image/png;base64,iVBORw0KGgo…",
"size": 0.22
},
"frame": {
"text": "Scan me",
"color": "#1a237e",
"textColor": "#1a237e"
}
},
"content": "HTTPS://VCTR.IO/K7P2XQM",
"linkId": "3f6c2a9e-8b1d-4c3e-9a7f-2d5e6b8c1a40",
"code": "code",
"shortUrl": "https://vctr.io/K7P2XQM",
"destination": "https://example.com/spring-menu",
"payload": {
"type": "wifi",
"ssid": "Cafe Guest",
"password": "espresso42",
"encryption": "WPA",
"hidden": false
},
"contact": {
"firstName": "Ada",
"lastName": "de Vries",
"organization": "Example B.V.",
"title": "Owner",
"phones": [
{
"type": "cell",
"number": "+31 20 123 4567"
}
],
"emails": [
{
"type": "work",
"address": "ada@example.com"
}
],
"website": "https://example.com",
"address": {
"street": "street",
"city": "city",
"region": "region",
"postalCode": "postalCode",
"country": "country"
}
},
"hasPhoto": false,
"createdAt": "2026-10-01T09:30:00Z",
"metadata": {}
}
QrDesign
| Field | Type | Description |
|---|
dotsrequired | "square" | "rounded" | "dots" | "classy" | |
|---|
eyeFramerequired | "square" | "rounded" | "circle" | |
|---|
eyePupilrequired | "square" | "rounded" | "dot" | |
|---|
foregroundrequired | string (#rrggbb) | object | |
|---|
foreground.fromrequired | string (#rrggbb) | |
|---|
foreground.torequired | string (#rrggbb) | |
|---|
foreground.anglerequired | number | Degrees; 0 runs left to right, 90 top to bottom. |
|---|
backgroundrequired | string (#rrggbb) | |
|---|
errorCorrectionrequired | "L" | "M" | "Q" | "H" | Ignored with a logo, which always uses H. |
|---|
logo | object | |
|---|
logo.hrefrequired | string | A base64 data: URI of a PNG, JPEG or SVG image. |
|---|
logo.sizerequired | number | Width as a fraction of the code, 0.1 to 0.4. |
|---|
frame | object | A border with a call to action below the code. |
|---|
frame.textrequired | string | |
|---|
frame.colorrequired | string (#rrggbb) | |
|---|
frame.textColorrequired | string (#rrggbb) | |
|---|
Example
{
"dots": "square",
"eyeFrame": "square",
"eyePupil": "square",
"foreground": "#1a237e",
"background": "#1a237e",
"errorCorrection": "L",
"logo": {
"href": "data:image/png;base64,iVBORw0KGgo…",
"size": 0.22
},
"frame": {
"text": "Scan me",
"color": "#1a237e",
"textColor": "#1a237e"
}
}
Timeseries
| Field | Type | Description |
|---|
intervalrequired | "hour" | "day" | |
|---|
bucketsrequired | object[] | Every bucket in range, with zeros where there were no Hits. |
|---|
buckets[].startrequired | string (date-time) | Start of the hour or UTC day. |
|---|
buckets[].hitsrequired | integer | |
|---|
buckets[].visitorsrequired | integer | Daily unique visitors: each visitor counts once per Link and UTC day, in the bucket of their first Hit that day. |
|---|
untrackedrequired | integer | Untracked hits in range: redirected beyond the monthly Tracked hit quota, counted in hits but without detail. |
|---|
historyStartrequired | string (date-time) | The earliest time the Workspace's Plan shows stats for; from is moved up to it. |
|---|
Example
{
"interval": "hour",
"buckets": [
{
"start": "2026-10-01T09:30:00Z",
"hits": 1204,
"visitors": 873
}
],
"untracked": 0,
"historyStart": "2026-10-01T09:30:00Z"
}
Breakdown
| Field | Type | Description |
|---|
dimensionrequired | "country" | "region" | "city" | "device_type" | "os" | "browser" | "language" | "referrer_host" | "channel" | "rule" | "variant" | |
|---|
rowsrequired | object[] | The most common values, by Hits. |
|---|
rows[].valuerequired | string | '' when unknown. |
|---|
rows[].hitsrequired | integer | |
|---|
rows[].visitorsrequired | integer | Daily unique visitors with this value, summed over the days in range. |
|---|
Example
{
"dimension": "country",
"rows": [
{
"value": "NL",
"hits": 1204,
"visitors": 873
}
]
}
Domain
| Field | Type | Description |
|---|
idrequired | string (uuid) | |
|---|
hostnamerequired | string | |
|---|
statusrequired | "pending" | "active" | "disabled" | pending: waiting for its DNS records; active: verified and serving Links; disabled: its DNS stopped pointing at VCTR for 7 days, so it serves nothing until it points back.
|
|---|
dnsRecordsrequired | DnsRecord[] | The CNAME (or, for a root domain, A records) that point the hostname at VCTR, and the TXT record that proves the Workspace controls it. |
|---|
verificationrequired | object | |
|---|
verification.verifiedAtrequired | string (date-time) | null | |
|---|
verification.checkedAtrequired | string (date-time) | null | The last DNS check; pending Domains are checked every minute, verified ones daily. |
|---|
verification.problemrequired | string | null | What the last check found wrong, if anything. |
|---|
certificaterequired | object | |
|---|
certificate.statusrequired | "none" | "pending" | "issued" | "failed" | The HTTPS certificate: requested once the Domain is verified. |
|---|
certificate.messagerequired | string | null | |
|---|
danglingrequired | object | null | Set while a verified Domain no longer points at VCTR: it is disabled at disablesAt unless it points back. |
|---|
dangling.sincerequired | string (date-time) | |
|---|
dangling.disablesAtrequired | string (date-time) | |
|---|
fallbackDestinationrequired | string (uri) | null | Where unknown Codes on the hostname go; they answer 404 when null. |
|---|
createdAtrequired | string (date-time) | |
|---|
Example
{
"id": "3f6c2a9e-8b1d-4c3e-9a7f-2d5e6b8c1a40",
"hostname": "go.example.com",
"status": "pending",
"dnsRecords": [
{
"type": "CNAME",
"name": "Spring menu",
"value": "NL"
}
],
"verification": {
"verifiedAt": "2026-10-01T09:30:00Z",
"checkedAt": "2026-10-01T09:30:00Z",
"problem": "problem"
},
"certificate": {
"status": "none",
"message": "message"
},
"dangling": {
"since": "2026-10-01T09:30:00Z",
"disablesAt": "2026-10-01T09:30:00Z"
},
"fallbackDestination": "https://example.com/spring-menu",
"createdAt": "2026-10-01T09:30:00Z"
}
DnsRecord
A record to create at the DNS provider.
| Field | Type | Description |
|---|
typerequired | "CNAME" | "A" | "TXT" | |
|---|
namerequired | string | |
|---|
valuerequired | string | |
|---|
Example
{
"type": "CNAME",
"name": "Spring menu",
"value": "NL"
}
PartnerWorkspace
| Field | Type | Description |
|---|
idrequired | string (uuid) | |
|---|
externalRefrequired | string | The Partner's own reference for the Workspace. |
|---|
namerequired | string | |
|---|
planrequired | "partner" | Partner Workspaces have no limits and are not billed by VCTR. |
|---|
createdAtrequired | string (date-time) | |
|---|
Example
{
"id": "3f6c2a9e-8b1d-4c3e-9a7f-2d5e6b8c1a40",
"externalRef": "acct-1001",
"name": "Spring menu",
"plan": "partner",
"createdAt": "2026-10-01T09:30:00Z"
}
RuleConditionsOutput
All conditions given must hold; within one, any value matches. None at all matches every Hit.
| Field | Type | Description |
|---|
countries | string[] | ISO 3166-1 alpha-2 country codes. |
|---|
deviceTypes | "desktop" | "smartphone" | "tablet" | "phablet" | "feature_phone" | "console" | "tv" | "car_browser" | "smart_display" | "camera" | "portable_media_player" | "smart_speaker" | "wearable" | "peripheral"[] | |
|---|
os | string[] | Operating systems as the device detector names them, ignoring case. |
|---|
languages | string[] | Primary subtags of the visitor's preferred language (Accept-Language). |
|---|
time | TimeWindowOutput | |
|---|
Example
{
"countries": [
"NL",
"BE"
],
"deviceTypes": [
"desktop"
],
"os": [
"iOS",
"Android"
],
"languages": [
"nl"
],
"time": {
"timezone": "Europe/Amsterdam",
"days": [
1
],
"start": "09:00",
"end": "17:30"
}
}
TimeWindowOutput
Matches Hits made between start and end, local time in timezone.
| Field | Type | Description |
|---|
timezonerequired | string | |
|---|
days | integer[] | ISO weekdays in timezone, 1 = Monday … 7 = Sunday. Every day when absent. |
|---|
startrequired | string | Local start time, inclusive. |
|---|
endrequired | string | Local end time, exclusive; earlier than start runs past midnight. |
|---|
Example
{
"timezone": "Europe/Amsterdam",
"days": [
1
],
"start": "09:00",
"end": "17:30"
}