API documentation

Everything in the dashboard is available over a REST API and a native MCP server, metered in the same prepaid credits. Base URL: https://www.taproute.io

Authentication

Create API keys in Dashboard → API. Every request needs a bearer header — keys start with tr_ and are shown exactly once at creation.

Authorization: Bearer tr_...
curl https://www.taproute.io/api/v1/links \
  -H "Authorization: Bearer tr_your_key_here"

Endpoints

MethodPathBodyReturns
POST/api/v1/links{url, slug?, rules?}201 · {id, slug, shortUrl}
GET/api/v1/links{links: [...]}
PATCH/api/v1/links/:id{url?, status?, rules?}{id, slug, url, status}
DELETE/api/v1/links/:id{ok: true}
GET/api/v1/wallet{balance, inGrace, …}
GET/api/v1/analytics?days=7|30|90{total, timeseries, byCountry, byDevice, topLinks}

Links

curl -X POST https://www.taproute.io/api/v1/links \
  -H "Authorization: Bearer tr_..." \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://example.com/menu",
    "slug": "summer-menu",
    "rules": { "ios": "https://apps.apple.com/app/id123" }
  }'

# 201 Created
# { "id": "…", "slug": "summer-menu", "shortUrl": "https://tapr.io/summer-menu" }
# Change the destination after printing — the QR keeps working
curl -X PATCH https://www.taproute.io/api/v1/links/LINK_ID \
  -H "Authorization: Bearer tr_..." \
  -H "Content-Type: application/json" \
  -d '{ "url": "https://example.com/winter-menu", "status": "active" }'

Wallet

curl https://www.taproute.io/api/v1/wallet -H "Authorization: Bearer tr_..."

# { "balance": 48210, "inGrace": false, "graceExhausted": false, … }

Analytics

curl "https://www.taproute.io/api/v1/analytics?days=30" -H "Authorization: Bearer tr_..."

# { "total": 1289, "timeseries": [{ "date": "2026-07-01", "count": 42 }, …],
#   "byCountry": […], "byDevice": […], "topLinks": […] }

The rules object

Optional per-link routing, evaluated at the edge on every scan. All fields are optional; anything unmatched falls through to the link's main url.

{
  "ios": "https://apps.apple.com/app/id123456",
  "android": "https://play.google.com/store/apps/details?id=com.example",
  "schedule": [
    { "start": "2026-07-01T08:00:00Z", "end": "2026-07-01T11:00:00Z", "url": "https://example.com/breakfast" },
    { "start": "2026-07-01T11:00:00Z", "url": "https://example.com/lunch" }
  ]
}
  • ios / android — device-specific destinations (App Store / Play Store deep links).
  • schedule — array of time windows with ISO 8601 datetimes. start and end are each optional (open-ended windows). The first matching window wins.

Webhooks

Add endpoints in Dashboard → API to receive a POST for every scan, delivered from the edge in real time. Each request carries two headers:

X-Taproute-EventEvent name, currently scan
X-Taproute-Signaturet=<ms>,v1=<hex hmac-sha256(secret, t + "." + body)>

Payload

{
  "event": "scan",
  "data": {
    "linkId": "9f1c…",
    "slug": "summer-menu",
    "domain": "_",
    "country": "DE",
    "device": "ios",
    "ts": 1751884800000
  }
}

Verifying signatures (Node)

import { createHmac, timingSafeEqual } from 'node:crypto';

// signature header: t=<ms>,v1=<hex>
export function verifyTaproute(secret, signature, rawBody) {
  const parts = Object.fromEntries(signature.split(',').map((p) => p.split('=')));
  const expected = createHmac('sha256', secret)
    .update(`${parts.t}.${rawBody}`)
    .digest('hex');
  return timingSafeEqual(Buffer.from(parts.v1, 'hex'), Buffer.from(expected, 'hex'));
}

MCP server

A native Model Context Protocol server (Streamable HTTP) lets AI agents — Claude, Cursor, and friends — manage your QR codes directly. Authenticate with the same API keys:

{
  "mcpServers": {
    "taproute": {
      "url": "https://www.taproute.io/mcp",
      "headers": { "Authorization": "Bearer tr_..." }
    }
  }
}
create_linkCreate a dynamic QR link, returns the short URL to encode
list_linksList all links with status and destinations
update_linkChange destination, rules, or status (active | paused)
delete_linkDelete a link permanently
get_qrFetch the QR code for a slug as SVG
wallet_balanceCredit balance and grace-period status
analytics_summaryScan analytics for 7 / 30 / 90 days

Errors

Standard HTTP status codes with a JSON body of the shape { "message": "…" }.

400Malformed input — missing url, invalid slug, bad JSON
401Missing or invalid API key
404Link not found (or belongs to another workspace)
409Slug already taken
422Destination rejected by safety screening (malware / phishing lists)
429Link-creation rate limit reached — retry in an hour