Developer Documentation
Authentication, resources, webhooks, and signature verification for building against the Tavzio API. Every business can generate its own API keys from its dashboard - no separate developer account needed.
The Tavzio API lets your own software talk directly to a specific Tavzio business account - read its orders, menu, tables, and more, or write to the resources that support it.
Every request is authenticated by an API key your business generates itself, from your own Tavzio dashboard - no separate developer account or approval process.
Every request to /api/v1/... is authenticated by API key, not a login session.
Create a key from your Tavzio dashboard: Settings → API & Integrations → API Keys. The full key is shown exactly once, at creation (or rotation) - copy it immediately. Tavzio never stores or displays the raw key again, only its first characters (e.g. tvz_live_ab12cd34) as a reference.
The key alone identifies your business on every request - you never send a business ID yourself, and any business ID you did send would be ignored in favor of what the key resolves to.
Authorization: Bearer tvz_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxv1 is stable - Tavzio will not make a breaking change to an existing v1 endpoint. A future v2, if introduced, exists alongside v1, not as a replacement for it.
https://<your-tavzio-backend-host>/api/v1Every key is issued with one or more scopes, <resource>:<action> (e.g. orders:read, menu:write), or the wildcard * for everything.
A request against a resource/action your key wasn't granted returns 403 with { "error": { "code": "insufficient_scope" } }.
GET /api/v1 returns every resource currently available, whether it's readable/writable/deletable, and the scopes each requires. This list grows as Tavzio adds features - check it rather than hardcoding a resource list in your integration.
GET /api/v1/:resource — list (paginated: ?limit=50&offset=0, max limit 200). Returns { data: [...], pagination: { limit, offset, total } }.
GET /api/v1/:resource/:id — one record. Returns { data: {...} }.
POST /api/v1/:resource — create (only where the resource is writable - check GET /api/v1). Returns { data: {...} }, 201.
PATCH /api/v1/:resource/:id — update. Returns { data: {...} }.
DELETE /api/v1/:resource/:id — delete (only where deletable). 204.
Order creation is deliberately not available as a generic write - it goes through real pricing/inventory/kitchen-printing/POS-dispatch logic that a generic insert would bypass. Use your existing ordering flow, or ask Tavzio about a dedicated order-creation endpoint for your integration.
This is the one write path external POS/KDS integrations need day-to-day, and it reuses the same status-transition logic the Tavzio dashboard itself uses.
PATCH /api/v1/orders/:id/status
Body: { "status": "preparing" }
// pending | preparing | ready | completed | cancelledStandard HTTP status codes (400, 401, 403, 404, 405, 500). code is a stable machine-readable string; message is for humans and may change wording over time - don't match on it.
{ "error": { "code": "not_found", "message": "Resource not found" } }Each key has its own per-minute budget (120/min by default, adjustable per key by the business). Exceeding it returns 429 with standard RateLimit-* headers.
Configure webhook endpoints from Settings → API & Integrations → Webhooks. Tavzio POSTs a JSON body carrying the event id, type, version, timestamp, business_id, and the event data.
{
"id": "evt_9f1c2a...",
"type": "order.created",
"version": "v1",
"timestamp": "2026-09-09T12:00:00.000Z",
"business_id": "b1a2c3d4-...",
"data": { "order": { "...": "..." } }
}Every delivery carries two headers: Tavzio-Event-Id and Tavzio-Signature (t=<unix timestamp>,v1=<hex hmac-sha256>). Verify it using the raw request body, before JSON parsing - the signature is computed over the raw bytes.
const crypto = require('crypto');
function verify(secret, rawBody, header, toleranceSeconds = 300) {
const [tPart, vPart] = header.split(',');
const timestamp = Number(tPart.split('=')[1]);
const signature = vPart.split('=')[1];
if (Math.abs(Date.now() / 1000 - timestamp) > toleranceSeconds) return false; // stale/replay
const expected = crypto.createHmac('sha256', secret).update(`${timestamp}.${rawBody}`).digest('hex');
return crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected));
}id (evt_...) is globally unique per event. If you receive the same id twice (a genuine retry after your server accepted it but the acknowledgment was lost), treat the second delivery as a no-op.
A non-2xx response (or a timeout) is retried at 1m, 5m, 30m, 2h, then 12h. After 5 failed attempts the delivery is marked exhausted and stops - visible in your dashboard's delivery history for that endpoint.
Use the "Send test" button on any endpoint in the dashboard to send a real, fully-signed test delivery through the exact same path a genuine event uses.
Not every listed event type is emitted yet in every situation - new emission points are added incrementally as Tavzio's own features are wired up to fire them, without ever changing this documented shape. Ask Tavzio if a specific event your integration needs isn't firing yet.
This is the reverse direction - if your platform (a delivery marketplace, a PMS, an accounting system) wants Tavzio to push data to your API using your own authentication, that's the "External Integrations" tab in the Tavzio dashboard, not an API key. Contact Tavzio to register your platform as a supported provider.