VisitorFlow
Build · REST API

API reference

Every VisitorFlow entity is reachable via the REST API at https://api.visitorflow.com/v1. All requests are authenticated, JSON in, JSON out, and rate-limited per tenant. The full machine-readable spec lives at /docs/api/openapi.

Authentication

Generate an API key from Settings → API & webhooks → New key. Keys are scoped: vfp_live_* for production, vfp_test_* for the sandbox. Pass the key as a Bearer token on every request.

curl https://api.visitorflow.com/v1/visits \
  -H "Authorization: Bearer vfp_live_..."

Kiosk endpoints under /v1/kiosk/* use a separate device token issued by the pairing flow — those tokens carry the tenant + location implicitly so the body cannot inject a different tenant.

Rate limits

Default budgets per tenant: 600 read req/min, 120 write req/min. Burst is allowed up to 2× for 10 seconds. Hitting the limit returns 429 Too Many Requests with retry-after in seconds. Enterprise plans get custom budgets.

Versioning

The URL prefix carries the major version (/v1/). Additive changes (new fields, new optional params) ship without a version bump. Breaking changes get a new prefix and a 12-month overlap.

Create a visitor visit

POST /v1/visits
{
  "type": "visitor",
  "host_id": "h_98412",
  "visitor": {
    "name": "Marisol Cruz",
    "email": "marisol@acme.com",
    "company": "Acme Co"
  },
  "location_id": "loc_mty_01"
}

201 Created
{
  "id": "vis_2YZ4...",
  "checked_in_at": "2026-04-30T14:21:09Z",
  "host_notified": true
}

Create a driver visit

POST /v1/visits
{
  "type": "driver",
  "driver": { "name": "J. Pérez", "license_number": "LIC-1234" },
  "shipment": { "bol_ref": "BOL-88412", "type": "delivery", "hazmat": false },
  "vehicle":  { "tractor_plate": "ABC-1234", "trailer_plate": "TR-998" },
  "location_id": "loc_mty_01"
}

Pre-register a visitor

POST /v1/pre-registrations
{
  "host_id": "h_98412",
  "visitor": { "name": "Andrés López", "email": "andres@acme.com" },
  "scheduled_at": "2026-05-02T15:00:00Z",
  "window_minutes": 60
}

VisitorFlow emails the visitor a signed QR; the kiosk decodes it and skips straight to photo + NDA. The QR PNG is also reachable at GET /v1/prereg/qr/{id}.png if you want to embed it in your own emails.

List visits

GET /v1/visits?location_id=loc_mty_01&status=onsite

200 OK
{
  "data": [{ "id": "vis_2YZ4...", ... }],
  "next_cursor": "eyJsYXN0SUQiOi..."
}

Check out a visit

PATCH /v1/visits/vis_2YZ4
{ "status": "checked_out" }

List incidents

GET /v1/incidents?status=open

200 OK
[
  {
    "id": "inc_xy12",
    "severity": "low",
    "type": "tailgating",
    "summary": "Visitor entered without badge scan",
    "status": "open"
  }
]

Errors

Errors follow the JSON:API problem shape. 4xx means you sent something invalid; 5xx means we did. Every response includes an x-request-id header — quote it when contacting support.

401 Unauthorized
{ "error": "Bearer token missing or expired" }

400 Bad Request
{
  "error": "Invalid payload",
  "issues": {
    "fieldErrors": { "visitor.email": ["Invalid email"] }
  }
}

Webhooks

Subscribe to events instead of polling. Every delivery is signed with x-vf-signature: sha256=... and replays with exponential backoff up to a 24h cap. See the webhooks guide.


Looking for SDKs? TypeScript and Python SDKs ship the same week as the public beta. For the canonical schema, fetch the OpenAPI 3.1 document.