Developers

API Reference

The visitto.me REST API is the tracking backend for your own tools and integrations. Mint a uniquely-tracked short code per recipient, then read who clicked — by name, time, place, and device.

Base URL https://visitto.me/api/v1

Introduction

Everything is scoped to your account and accessed over HTTPS with an API key. One parent link holds a shared destination; each recipient gets its own short code and URL, and every click is attributed to that recipient and rolled up to the link and campaign.

For the full integration walkthrough (idempotency, retries, batching), see the bulk integration guide.

Authentication

Send your API key as a Bearer token on every request:

Authorization: Bearer vm_live_<prefix>_<secret>

Create keys in the dashboard under Settings → API keys. The full token is shown once at creation — only a lookup prefix and a hash of the secret are stored. Keys carry scopes:

  • links:writeCreate and update links
  • recipients:writeMint per-recipient tracked codes
  • analytics:readRead analytics and list recipients
  • campaigns:writeCreate campaigns

Responses & errors

Every response uses the same envelope:

{ "data": ..., "meta": ..., "error": null }

On failure, data is null and error is populated:

{ "data": null, "meta": null,
  "error": { "code": "quota_exceeded", "message": "...", "details": null } }
StatusMeaning
401Missing or invalid API key
403Key is valid but missing the required scope
402Plan quota exceeded — upgrade to continue
404Resource not found (or not yours)
409Conflict — e.g. a custom short code is taken
422Validation error — check details
429Rate limited — back off and retry
500Something went wrong on our side

Rate limits & quotas

API requests are metered per key per day, with the daily ceiling set by your plan (over-limit returns 429). Creating links and recipients also counts against your plan's monthly quotas — exceeding them returns 402 with a message. Batch calls accept up to 5,000 recipients each.

Recipients

POST/links/{id}/recipientsscope: recipients:write

Bulk-mint individually-tracked codes under a link. Idempotent on externalId — re-posting a recipient whoseexternalId already exists returns the existing row instead of minting a duplicate (safe for re-runs). A recipient's optional destinationUrl makes their code redirect somewhere unique.

POST /api/v1/links/{id}/recipients
{
  "recipients": [
    { "name": "Alice", "email": "alice@x.com", "externalId": "crm_1",
      "customFields": { "tier": "gold" } },
    { "name": "Bob", "externalId": "crm_2" }
  ]
}

Returns 201 with { created, skipped, recipients: [{ id, externalId, name, email, shortCode, url, clickCount, customFields }] }.

GET/links/{id}/recipients?limit=&offset=scope: analytics:read

List a link's recipients, ordered by clicks. limit ≤ 500.

Analytics

GET/links/{id}/analyticsscope: analytics:read

Link summary: { id, shortCode, url, destinationUrl, totalClicks, recipientCount, byCountry, topRecipients }.

GET/recipients/{id}/analyticsscope: analytics:read

Per-recipient breakdown: { id, name, email, externalId, shortCode, url, totalClicks, firstClickedAt, lastClickedAt, byCountry, byDevice }.

Campaigns

POST/campaignsscope: campaigns:write

Create a campaign to organise links.

{ "name": "Summer 2026", "description": "Optional", "color": "#2E2AE0" }
GET/campaignsscope: analytics:read

List campaigns with live linkCount + totalClicks.

GET/campaigns/{id}/analyticsscope: analytics:read

Campaign rollup + per-link breakdown.

Webhooks

Register endpoints in Settings → Webhooks. When a recipient clicks, visitto.me sends a signed POST:

POST <your-url>
X-Visitto-Event: recipient.clicked
X-Visitto-Signature: sha256=<hex of HMAC-SHA256(rawBody, secret)>

{ "event": "recipient.clicked",
  "data": { "recipientId", "parentLinkId", "name", "email",
            "externalId", "shortCode", "country", "device", "clickedAt" },
  "timestamp": "..." }

Verify by recomputing HMAC-SHA256(rawBody, secret) and comparing to the header. The externalId lets you route the event straight to your own record.

Quickstart

Create a link with two tracked recipients in one call:

curl -X POST https://visitto.me/api/v1/links \
  -H "Authorization: Bearer vm_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "destinationUrl": "https://acme.com/landing",
    "recipients": [
      { "name": "Alice", "externalId": "crm_1" },
      { "name": "Bob",   "externalId": "crm_2" }
    ]
  }'

Each returned items[].url is a unique short URL to encode as a QR or send. Read who engaged with GET /recipients/{id}/analytics.