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

Overview

The VCTR REST API: Links, QR Codes and their analytics in one Workspace.

Authenticate with a Workspace API key, which an Admin creates in the app: Authorization: Bearer vctr_sk_…. A key acts on its one Workspace only, with the role chosen when it was created: editor (manage Links and QR Codes, read analytics) or viewer (read only).

Partners authenticate with a Partner key, vctr_pk_…, which VCTR issues. It creates the Partner's Workspaces by the Partner's own reference (PUT /partner/workspaces/{externalRef}) and acts on any of them as an editor, named in the header Vctr-Workspace: ext:<externalRef>. Their Links are created on the Partner's Default Domain. Links and QR Codes carry Partner metadata, opaque key/value pairs that Link lists and stats filter on (metadata[container]=c-1).

Errors answer { "error": { "code", "message" } } with a stable code. Requests are rate-limited per key (lower on the Free Plan) and per IP; a limited request gets 429 rate_limited with Retry-After.

Authentication

Send a Workspace API key as a bearer token. An Admin or the Owner creates keys in the app, under API keys. A key acts on its one Workspace only, with the role chosen when it was created:

  • editor: create, change and delete Links and QR Codes, and read analytics.
  • viewer: read only.

A key works only while the person who created it is still an Admin or the Owner of the Workspace. Partners use a Partner key instead, issued by VCTR, and name the Workspace in the Vctr-Workspace header (see the overview and Partner).

curl https://vctr.io/api/v1/workspace \
  -H "Authorization: Bearer $VCTR_API_KEY"

Errors

Every error answers with the same shape and a stable code to branch on. Codes never change meaning; the message is for people.

{
  "error": {
    "code": "validation_failed",
    "message": "Invalid request body.",
    "issues": [
      {
        "path": "destination",
        "message": "Invalid URL"
      }
    ]
  }
}
StatusCodeMeaning
400validation_failed

The request is malformed: bad JSON, or a missing or mistyped field or parameter. issues lists each one.

401unauthorized

No API key, or one that does not exist or was revoked.

403forbidden

The key's role does not allow this, e.g. a viewer key creating a Link.

404not_found

No such resource in the key's Workspace.

402plan_limit

The Workspace's Plan allows no more of this, e.g. Links. Existing Links keep working; upgrading allows more.

403workspace_disabled

VCTR staff disabled the Workspace for abuse, so its Links and QR Codes can't be changed.

409code_taken

The custom Code is used by another Link.

409code_retired

The custom Code belonged to a deleted Link and is Retired forever.

409domain_taken

The hostname is already added to this Workspace, or another Workspace serves it.

413payload_too_large

The request body is larger than 1 MiB.

415unsupported_media_type

The request body is not application/json.

422invalid_code

The custom Code is malformed, reserved or not allowed.

422invalid_destination

The Destination is not a full http(s) URL, or the Link's Destination can't be set.

422invalid_hostname

Not a hostname a custom Domain can have, e.g. VCTR's own or a Partner's.

422invalid_domain

The domain for a new Link isn't one of the Workspace's active custom Domains.

422confirmation_mismatch

confirm must repeat the hostname of the Domain being removed.

422invalid_design

The QR Code design cannot be rendered, e.g. a logo too large.

422content_too_large

More content than fits in one QR code.

429rate_limited

Too many requests from this key or IP address, or too many new Links from the key's creator. Retry after Retry-After seconds.

500internal_error

Something went wrong on our side. Retry later.

Rate limits

Requests are limited per API key and per client IP address:

  • Per key on the Free Plan: 60 requests a minute.
  • Per key on Pro and Business: 600 requests a minute.
  • Per IP address, whatever the key: 1,200 requests a minute.

Successful responses carry RateLimit-Limit and RateLimit-Remaining. A limited request gets 429 rate_limited with Retry-After, in seconds.

Pagination

List endpoints answer a page of results in data and the nextCursor of the next page, or null on the last one. Pass it back as cursor to read on.

Workspace

The Workspace this key acts on

GET /api/v1/workspace

operationId getWorkspace · needs a viewer key or higher

Parameters

ParameterInTypeDescription
Vctr-Workspaceheaderstring

Partner keys only: the Workspace to act on, ext:<externalRef> or its id. Workspace API keys ignore it.

Response 200

The Workspace and the role the key acts with.

Returns a Workspace.

Errors

Example request

curl 'https://vctr.io/api/v1/workspace' \
  -H "Authorization: Bearer $VCTR_API_KEY"

Example response

{
  "id": "3f6c2a9e-8b1d-4c3e-9a7f-2d5e6b8c1a40",
  "name": "Spring menu",
  "role": "editor"
}

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

ParameterInTypeDescription
idrequiredpathstring (uuid)

The id.

Vctr-Workspaceheaderstring

Partner keys only: the Workspace to act on, ext:<externalRef> or its id. Workspace API keys ignore it.

Response 200

The Destination history.

FieldTypeDescription
datarequiredDestinationChange[]

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

ParameterInTypeDescription
cursorquerystring (uuid)

nextCursor from the previous page.

limitqueryinteger

QR Codes per page, 1 to 100.

1 to 100; default: 50

metadataqueryobject

Only those with all of these Partner metadata pairs: metadata[container]=c-1.

Vctr-Workspaceheaderstring

Partner keys only: the Workspace to act on, ext:<externalRef> or its id. Workspace API keys ignore it.

Response 200

A page of QR Codes.

FieldTypeDescription
datarequiredQrCode[]
nextCursorrequiredstring (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

ParameterInTypeDescription
Vctr-Workspaceheaderstring

Partner keys only: the Workspace to act on, ext:<externalRef> or its id. Workspace API keys ignore it.

Request body application/json

FieldTypeDescription
namerequiredstring

1 to 100 characters

destinationrequiredstring

A full http(s) URL.

designobject

The design; every option is optional.

design.dots"square" | "rounded" | "dots" | "classy"
design.eyeFrame"square" | "rounded" | "circle"
design.eyePupil"square" | "rounded" | "dot"
design.foregroundstring (#rrggbb) | object
design.foreground.fromrequiredstring (#rrggbb)
design.foreground.torequiredstring (#rrggbb)
design.foreground.anglerequirednumber
design.backgroundstring (#rrggbb)
design.errorCorrection"L" | "M" | "Q" | "H"
design.logoobject
design.logo.hrefrequiredstring

at most 700000 characters

design.logo.sizerequirednumber
design.frameobject
design.frame.textrequiredstring

at most 60 characters

design.frame.colorrequiredstring (#rrggbb)
design.frame.textColorrequiredstring (#rrggbb)
domainstring

The hostname of the Link: one of the Workspace's active custom Domains. The Default Domain when absent.

at most 300 characters

metadataobject

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

The new QR Code.

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

ParameterInTypeDescription
idrequiredpathstring (uuid)

The id.

Vctr-Workspaceheaderstring

Partner keys only: the Workspace to act on, ext:<externalRef> or its id. Workspace API keys ignore it.

Response 200

The QR Code.

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

ParameterInTypeDescription
idrequiredpathstring (uuid)

The id.

Vctr-Workspaceheaderstring

Partner keys only: the Workspace to act on, ext:<externalRef> or its id. Workspace API keys ignore it.

Request body application/json

FieldTypeDescription
metadatarequiredobject

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

The changed QR Code.

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

ParameterInTypeDescription
idrequiredpathstring (uuid)

The id.

dotsquery"square" | "rounded" | "dots" | "classy"
eyeFramequery"square" | "rounded" | "circle"
eyePupilquery"square" | "rounded" | "dot"
foregroundquerystring (#rrggbb)

Foreground color; on a gradient, where it starts.

foregroundToquerystring (#rrggbb)

Makes the foreground a linear gradient ending in this color (or changes where it ends).

gradientAnglequerynumber

Gradient angle in degrees: 0 runs left to right (the default), 90 top to bottom. Applies to a gradient only.

backgroundquerystring (#rrggbb)
errorCorrectionquery"L" | "M" | "Q" | "H"
frameTextquerystring

Adds a frame with this call to action, or changes its text. Empty removes the frame.

at most 60 characters

frameColorquerystring (#rrggbb)

Frame color, when there is a frame.

frameTextColorquerystring (#rrggbb)

Call-to-action color, when there is a frame.

Vctr-Workspaceheaderstring

Partner keys only: the Workspace to act on, ext:<externalRef> or its id. Workspace API keys ignore it.

Response 200

The SVG file.

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

ParameterInTypeDescription
idrequiredpathstring (uuid)

The id.

dotsquery"square" | "rounded" | "dots" | "classy"
eyeFramequery"square" | "rounded" | "circle"
eyePupilquery"square" | "rounded" | "dot"
foregroundquerystring (#rrggbb)

Foreground color; on a gradient, where it starts.

foregroundToquerystring (#rrggbb)

Makes the foreground a linear gradient ending in this color (or changes where it ends).

gradientAnglequerynumber

Gradient angle in degrees: 0 runs left to right (the default), 90 top to bottom. Applies to a gradient only.

backgroundquerystring (#rrggbb)
errorCorrectionquery"L" | "M" | "Q" | "H"
frameTextquerystring

Adds a frame with this call to action, or changes its text. Empty removes the frame.

at most 60 characters

frameColorquerystring (#rrggbb)

Frame color, when there is a frame.

frameTextColorquerystring (#rrggbb)

Call-to-action color, when there is a frame.

widthqueryinteger

Width in pixels, 16 to 4096; 1024 by default.

Vctr-Workspaceheaderstring

Partner keys only: the Workspace to act on, ext:<externalRef> or its id. Workspace API keys ignore it.

Response 200

The PNG file.

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

ParameterInTypeDescription
idrequiredpathstring (uuid)

The id.

dotsquery"square" | "rounded" | "dots" | "classy"
eyeFramequery"square" | "rounded" | "circle"
eyePupilquery"square" | "rounded" | "dot"
foregroundquerystring (#rrggbb)

Foreground color; on a gradient, where it starts.

foregroundToquerystring (#rrggbb)

Makes the foreground a linear gradient ending in this color (or changes where it ends).

gradientAnglequerynumber

Gradient angle in degrees: 0 runs left to right (the default), 90 top to bottom. Applies to a gradient only.

backgroundquerystring (#rrggbb)
errorCorrectionquery"L" | "M" | "Q" | "H"
frameTextquerystring

Adds a frame with this call to action, or changes its text. Empty removes the frame.

at most 60 characters

frameColorquerystring (#rrggbb)

Frame color, when there is a frame.

frameTextColorquerystring (#rrggbb)

Call-to-action color, when there is a frame.

widthMmquerynumber

Printed width in millimetres, 10 to 170; 50 by default.

Vctr-Workspaceheaderstring

Partner keys only: the Workspace to act on, ext:<externalRef> or its id. Workspace API keys ignore it.

Response 200

The PDF file.

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

ParameterInTypeDescription
fromrequiredquerystring (date-time)

Start of the range (inclusive), ISO 8601.

torequiredquerystring (date-time)

End of the range (exclusive), ISO 8601.

includeBotsqueryboolean

Count Bot hits too; they are left out by default.

default: false

intervalrequiredquery"hour" | "day"

Bucket size: hours, or UTC days.

metadataqueryobject

Only those with all of these Partner metadata pairs: metadata[container]=c-1.

Vctr-Workspaceheaderstring

Partner keys only: the Workspace to act on, ext:<externalRef> or its id. Workspace API keys ignore it.

Response 200

The time series.

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

ParameterInTypeDescription
fromrequiredquerystring (date-time)

Start of the range (inclusive), ISO 8601.

torequiredquerystring (date-time)

End of the range (exclusive), ISO 8601.

includeBotsqueryboolean

Count Bot hits too; they are left out by default.

default: false

dimensionrequiredquery"country" | "region" | "city" | "device_type" | "os" | "browser" | "language" | "referrer_host" | "channel" | "rule" | "variant"

What to group Hits by.

limitqueryinteger

The most common values to return, 1 to 100.

1 to 100; default: 10

metadataqueryobject

Only those with all of these Partner metadata pairs: metadata[container]=c-1.

Vctr-Workspaceheaderstring

Partner keys only: the Workspace to act on, ext:<externalRef> or its id. Workspace API keys ignore it.

Response 200

The breakdown.

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

ParameterInTypeDescription
idrequiredpathstring (uuid)

The id.

fromrequiredquerystring (date-time)

Start of the range (inclusive), ISO 8601.

torequiredquerystring (date-time)

End of the range (exclusive), ISO 8601.

includeBotsqueryboolean

Count Bot hits too; they are left out by default.

default: false

intervalrequiredquery"hour" | "day"

Bucket size: hours, or UTC days.

Vctr-Workspaceheaderstring

Partner keys only: the Workspace to act on, ext:<externalRef> or its id. Workspace API keys ignore it.

Response 200

The time series.

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

ParameterInTypeDescription
idrequiredpathstring (uuid)

The id.

fromrequiredquerystring (date-time)

Start of the range (inclusive), ISO 8601.

torequiredquerystring (date-time)

End of the range (exclusive), ISO 8601.

includeBotsqueryboolean

Count Bot hits too; they are left out by default.

default: false

dimensionrequiredquery"country" | "region" | "city" | "device_type" | "os" | "browser" | "language" | "referrer_host" | "channel" | "rule" | "variant"

What to group Hits by.

limitqueryinteger

The most common values to return, 1 to 100.

1 to 100; default: 10

Vctr-Workspaceheaderstring

Partner keys only: the Workspace to act on, ext:<externalRef> or its id. Workspace API keys ignore it.

Response 200

The breakdown.

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

ParameterInTypeDescription
idrequiredpathstring (uuid)

The id.

fromrequiredquerystring (date-time)

Start of the range (inclusive), ISO 8601.

torequiredquerystring (date-time)

End of the range (exclusive), ISO 8601.

includeBotsqueryboolean

Count Bot hits too; they are left out by default.

default: false

intervalrequiredquery"hour" | "day"

Bucket size: hours, or UTC days.

Vctr-Workspaceheaderstring

Partner keys only: the Workspace to act on, ext:<externalRef> or its id. Workspace API keys ignore it.

Response 200

The time series.

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

ParameterInTypeDescription
idrequiredpathstring (uuid)

The id.

fromrequiredquerystring (date-time)

Start of the range (inclusive), ISO 8601.

torequiredquerystring (date-time)

End of the range (exclusive), ISO 8601.

includeBotsqueryboolean

Count Bot hits too; they are left out by default.

default: false

dimensionrequiredquery"country" | "region" | "city" | "device_type" | "os" | "browser" | "language" | "referrer_host" | "channel" | "rule" | "variant"

What to group Hits by.

limitqueryinteger

The most common values to return, 1 to 100.

1 to 100; default: 10

Vctr-Workspaceheaderstring

Partner keys only: the Workspace to act on, ext:<externalRef> or its id. Workspace API keys ignore it.

Response 200

The breakdown.

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

ParameterInTypeDescription
Vctr-Workspaceheaderstring

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.

FieldTypeDescription
datarequiredDomain[]

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

ParameterInTypeDescription
Vctr-Workspaceheaderstring

Partner keys only: the Workspace to act on, ext:<externalRef> or its id. Workspace API keys ignore it.

Request body application/json

FieldTypeDescription
hostnamerequiredstring

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

ParameterInTypeDescription
idrequiredpathstring (uuid)

The id.

Vctr-Workspaceheaderstring

Partner keys only: the Workspace to act on, ext:<externalRef> or its id. Workspace API keys ignore it.

Response 200

The Domain.

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

ParameterInTypeDescription
idrequiredpathstring (uuid)

The id.

Vctr-Workspaceheaderstring

Partner keys only: the Workspace to act on, ext:<externalRef> or its id. Workspace API keys ignore it.

Request body application/json

FieldTypeDescription
fallbackDestinationrequiredstring | null

A full http(s) URL on another hostname, or null for a 404.

at most 2048 characters

Response 200

The changed Domain.

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

ParameterInTypeDescription
idrequiredpathstring (uuid)

The id.

confirmrequiredquerystring

The hostname of the Domain, to confirm.

at most 300 characters

Vctr-Workspaceheaderstring

Partner keys only: the Workspace to act on, ext:<externalRef> or its id. Workspace API keys ignore it.

Response 204

Removed.

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"

Partner

Create or find a Partner Workspace

PUT /api/v1/partner/workspaces/{externalRef}

operationId upsertPartnerWorkspace · needs a Partner key

Creates the Partner's Workspace with this reference, or answers the one that exists: repeating the call is safe and always answers the same Workspace. A name renames it. Act on it afterwards with the header Vctr-Workspace: ext:<externalRef>.

Parameters

ParameterInTypeDescription
externalRefrequiredpathstring

The Partner's own reference (Crobox: the Crobox Account): 1 to 128 letters, digits and ., _, ~, -.

Request body application/json

FieldTypeDescription
namestring

The Workspace name; the reference when it is created without one.

1 to 100 characters

Response 200

The Partner Workspace.

Returns a PartnerWorkspace.

Errors

Example request

curl -X PUT 'https://vctr.io/api/v1/partner/workspaces/3f6c2a9e-8b1d-4c3e-9a7f-2d5e6b8c1a40' \
  -H "Authorization: Bearer $VCTR_API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{}'

Example response

{
  "id": "3f6c2a9e-8b1d-4c3e-9a7f-2d5e6b8c1a40",
  "externalRef": "acct-1001",
  "name": "Spring menu",
  "plan": "partner",
  "createdAt": "2026-10-01T09:30:00Z"
}

Schemas

Utm

UTM parameters written into the Destination as utm_source, utm_medium, … replacing ones already there; an empty string removes one.

FieldTypeDescription
sourcestring

at most 200 characters

mediumstring

at most 200 characters

campaignstring

at most 200 characters

termstring

at most 200 characters

contentstring

at most 200 characters

Example

{
  "source": "poster",
  "medium": "print",
  "campaign": "spring"
}

RuleInput

FieldTypeDescription
idstring

Stable id, recorded on each Hit; assigned when absent. Keep it when editing so stats stay together.

namestring

What the Rule is for, for people.

at most 80 characters; default: ""

whenRuleConditions
torequiredobject | object

Where matching Hits go: one Destination, or a Split.

to.destinationrequiredstring

A full http(s) URL.

to.splitrequiredobject[]

Weighted Destinations the Rule divides Hits across.

to.split[].idstring

Stable id, recorded on each Hit; assigned when absent. Keep it when editing so stats stay together.

to.split[].destinationrequiredstring

A full http(s) URL.

to.split[].weightrequiredinteger

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.

FieldTypeDescription
countriesstring[]

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"[]
osstring[]

Operating systems as the device detector names them, ignoring case.

languagesstring[]

Primary subtags of the visitor's preferred language (Accept-Language).

timeTimeWindow

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.

FieldTypeDescription
timezonerequiredstring
daysinteger[]

ISO weekdays in timezone, 1 = Monday … 7 = Sunday. Every day when absent.

startrequiredstring

Local start time, inclusive.

endrequiredstring

Local end time, exclusive; earlier than start runs past midnight.

Example

{
  "timezone": "Europe/Amsterdam",
  "days": [
    1
  ],
  "start": "09:00",
  "end": "17:30"
}

Workspace

FieldTypeDescription
idrequiredstring (uuid)
namerequiredstring
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

FieldTypeDescription
errorrequiredobject
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.messagerequiredstring

What went wrong, for people.

error.issuesobject[]

With validation_failed: each invalid field or parameter.

error.issues[].pathrequiredstring
error.issues[].messagerequiredstring

Example

{
  "error": {
    "code": "validation_failed",
    "message": "message",
    "issues": [
      {
        "path": "path",
        "message": "message"
      }
    ]
  }
}

Rule

FieldTypeDescription
idrequiredstring

Stable id, recorded on each Hit; assigned when absent. Keep it when editing so stats stay together.

namerequiredstring

What the Rule is for, for people.

at most 80 characters; default: ""

whenrequiredRuleConditionsOutput
torequiredobject | object

Where matching Hits go: one Destination, or a Split.

to.destinationrequiredstring

A full http(s) URL.

to.splitrequiredobject[]

Weighted Destinations the Rule divides Hits across.

to.split[].idrequiredstring

Stable id, recorded on each Hit; assigned when absent. Keep it when editing so stats stay together.

to.split[].destinationrequiredstring

A full http(s) URL.

to.split[].weightrequiredinteger

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

FieldTypeDescription
destinationrequiredstring (uri)
effectiveAtrequiredstring (date-time)

Example

{
  "destination": "https://example.com/spring-menu",
  "effectiveAt": "2026-10-01T09:30:00Z"
}

QrCode

FieldTypeDescription
idrequiredstring (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.

namerequiredstring
designrequiredQrDesign
contentrequiredstring

Exactly what the code encodes: the uppercase Link URL, or the Offline payload.

linkIdrequiredstring (uuid) | null

The Link this QR Code owns, whose Hits are its Scans.

coderequiredstring | null
shortUrlrequiredstring (uri) | null
destinationrequiredstring (uri) | null
payloadrequiredobject | object | null

What an Offline QR Code holds.

payload.typerequired"wifi"

when type is "wifi"

payload.ssidrequiredstring

when type is "wifi"

payload.passwordrequiredstring

when type is "wifi"

payload.encryptionrequired"WPA" | "WEP" | "nopass"

when type is "wifi"

payload.hiddenrequiredboolean

when type is "wifi"

payload.typerequired"text"

when type is "text"

payload.textrequiredstring

when type is "text"

contactrequiredobject | null

A vCard QR Code's contact details.

contact.firstNamerequiredstring
contact.lastNamerequiredstring
contact.organizationrequiredstring
contact.titlerequiredstring
contact.phonesrequiredobject[]
contact.phones[].typerequired"cell" | "work" | "home"
contact.phones[].numberrequiredstring
contact.emailsrequiredobject[]
contact.emails[].typerequired"work" | "home"
contact.emails[].addressrequiredstring
contact.websiterequiredstring
contact.addressrequiredobject
contact.address.streetrequiredstring
contact.address.cityrequiredstring
contact.address.regionrequiredstring
contact.address.postalCoderequiredstring
contact.address.countryrequiredstring
hasPhotorequiredboolean
createdAtrequiredstring (date-time)
metadatarequiredobject

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

FieldTypeDescription
dotsrequired"square" | "rounded" | "dots" | "classy"
eyeFramerequired"square" | "rounded" | "circle"
eyePupilrequired"square" | "rounded" | "dot"
foregroundrequiredstring (#rrggbb) | object
foreground.fromrequiredstring (#rrggbb)
foreground.torequiredstring (#rrggbb)
foreground.anglerequirednumber

Degrees; 0 runs left to right, 90 top to bottom.

backgroundrequiredstring (#rrggbb)
errorCorrectionrequired"L" | "M" | "Q" | "H"

Ignored with a logo, which always uses H.

logoobject
logo.hrefrequiredstring

A base64 data: URI of a PNG, JPEG or SVG image.

logo.sizerequirednumber

Width as a fraction of the code, 0.1 to 0.4.

frameobject

A border with a call to action below the code.

frame.textrequiredstring
frame.colorrequiredstring (#rrggbb)
frame.textColorrequiredstring (#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

FieldTypeDescription
intervalrequired"hour" | "day"
bucketsrequiredobject[]

Every bucket in range, with zeros where there were no Hits.

buckets[].startrequiredstring (date-time)

Start of the hour or UTC day.

buckets[].hitsrequiredinteger
buckets[].visitorsrequiredinteger

Daily unique visitors: each visitor counts once per Link and UTC day, in the bucket of their first Hit that day.

untrackedrequiredinteger

Untracked hits in range: redirected beyond the monthly Tracked hit quota, counted in hits but without detail.

historyStartrequiredstring (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

FieldTypeDescription
dimensionrequired"country" | "region" | "city" | "device_type" | "os" | "browser" | "language" | "referrer_host" | "channel" | "rule" | "variant"
rowsrequiredobject[]

The most common values, by Hits.

rows[].valuerequiredstring

'' when unknown.

rows[].hitsrequiredinteger
rows[].visitorsrequiredinteger

Daily unique visitors with this value, summed over the days in range.

Example

{
  "dimension": "country",
  "rows": [
    {
      "value": "NL",
      "hits": 1204,
      "visitors": 873
    }
  ]
}

Domain

FieldTypeDescription
idrequiredstring (uuid)
hostnamerequiredstring
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.

dnsRecordsrequiredDnsRecord[]

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.

verificationrequiredobject
verification.verifiedAtrequiredstring (date-time) | null
verification.checkedAtrequiredstring (date-time) | null

The last DNS check; pending Domains are checked every minute, verified ones daily.

verification.problemrequiredstring | null

What the last check found wrong, if anything.

certificaterequiredobject
certificate.statusrequired"none" | "pending" | "issued" | "failed"

The HTTPS certificate: requested once the Domain is verified.

certificate.messagerequiredstring | null
danglingrequiredobject | null

Set while a verified Domain no longer points at VCTR: it is disabled at disablesAt unless it points back.

dangling.sincerequiredstring (date-time)
dangling.disablesAtrequiredstring (date-time)
fallbackDestinationrequiredstring (uri) | null

Where unknown Codes on the hostname go; they answer 404 when null.

createdAtrequiredstring (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.

FieldTypeDescription
typerequired"CNAME" | "A" | "TXT"
namerequiredstring
valuerequiredstring

Example

{
  "type": "CNAME",
  "name": "Spring menu",
  "value": "NL"
}

PartnerWorkspace

FieldTypeDescription
idrequiredstring (uuid)
externalRefrequiredstring

The Partner's own reference for the Workspace.

namerequiredstring
planrequired"partner"

Partner Workspaces have no limits and are not billed by VCTR.

createdAtrequiredstring (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.

FieldTypeDescription
countriesstring[]

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"[]
osstring[]

Operating systems as the device detector names them, ignoring case.

languagesstring[]

Primary subtags of the visitor's preferred language (Accept-Language).

timeTimeWindowOutput

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.

FieldTypeDescription
timezonerequiredstring
daysinteger[]

ISO weekdays in timezone, 1 = Monday … 7 = Sunday. Every day when absent.

startrequiredstring

Local start time, inclusive.

endrequiredstring

Local end time, exclusive; earlier than start runs past midnight.

Example

{
  "timezone": "Europe/Amsterdam",
  "days": [
    1
  ],
  "start": "09:00",
  "end": "17:30"
}