Skip to content
Docs · API

A REST API for your venue, with a key that stays on a leash.

Everything the dashboard does, it does over this API. It speaks JSON over HTTPS, it answers at api.guestavo.com, and a caller gets in with a key that belongs to one organization and only opens what you ticked.

Keys are on. This page is the orientation, and settings is where you mint one.

Available now

Mint a key in settings

A key belongs to an organization, so it is made there: Settings, then Organizations, then the one you want, then its API keys tab. You pick the venues it may work at and the actions it may take at each of them, and you copy the value once, because we never show it again. From there it is one header on every request. Revoke it whenever you like and the next call it makes comes back 401.

Choose the organization to make it on

The shape of it

Plain REST, no surprises

If you have used any JSON API in the last decade you already know how this one behaves.

Base address
https://api.guestavo.com, HTTPS only. Every documented route lives under /api.
Format
JSON in, JSON out. Send Content-Type: application/json on anything with a body. Bodies are validated against a schema before a handler sees them.
Methods
GET reads, POST creates, PATCH updates, DELETE removes. A read never changes anything.
Identifiers
UUIDs. Dates are ISO calendar dates (2026-09-14) and times are 24-hour (19:30) in the venue’s own timezone.
Retries
A few creating routes accept an Idempotency-Key header and answer a repeat with the first response instead of writing a second record.

Authentication

One header, one organization

A key is a bearer token. Put it in the Authorization header on every request. There is no other step, no token exchange, and no refresh.

Authorization: Bearer gvsk_your_key_here
Content-Type: application/json
  • 01

    Pinned to one organization

    A key belongs to a single organization and cannot reach another one. Aim it somewhere else and you get a 403 that says the key is not valid for that organization, without naming any id you are not allowed to see.

  • 02

    Narrowed by scope

    Scopes are per module and per direction: booking:read, booking:write, menu:read, staff:read, analytics:read and so on. Ticking a write scope also grants its read half, and the settings screen says so rather than leaving it implied. Money is its own scope: analytics:read is what returns revenue, margin and wage figures, and nothing else implies it.

  • 03

    Narrowed further, if you want

    Beyond scopes, a key can be limited to specific venues and specific actions. A key that may create bookings at the pier and read the menu everywhere else is a normal thing to hand out.

  • 04

    It expires

    Ninety days unless you say otherwise, and a year at the outside. Rotating costs nothing: mint the new one, revoke the old one.

  • 05

    It is a secret

    Keys start with gvsk_ and behave like a password for the venue. Keep it on a server, never in a browser or a mobile app, and never in a repository.

Responses

Every answer wears the same envelope

Success or failure, the top-level shape is the same, so a client can branch on one boolean before it looks at anything else.

Success

{
  "success": true,
  "data": [ ... ],
  "meta": {
    "pagination": {
      "type": "offset",
      "totalCount": 214,
      "filteredCount": 12,
      "count": 12,
      "page": 1,
      "limit": 20,
      "hasMore": false
    }
  }
}

data carries the payload. A list adds meta.pagination with the page, the limit, the filtered count and whether more is waiting. Some routes add a human message, and a create answers 201.

Failure

{
  "success": false,
  "error": {
    "message": "This API key is not permitted to use create_booking",
    "code": "FORBIDDEN",
    "details": { "reason": "tool_not_granted" }
  }
}

error.code is the machine-readable one and the thing to branch on. error.message is English prose for a log, and details carries per-field problems on a validation failure.

The three you will actually meet

  • 401Unauthorized

    The key is missing, unreadable, expired or revoked, or its owner’s account is gone.

    Stop retrying. A key in this state never recovers on its own, so mint a fresh one and replace it.

  • 403Forbidden

    The key is fine, the request is not. Either the scope is missing, or the key is pinned to a different organization, or its grants do not open this action or this venue.

    Read error.details.reason. It tells you which of those it was, and only one of them is fixable by the caller: an unresolved venue means the request never named a propertyId it may act on, so name one and try again.

  • 429Too many requests

    One of the buckets below is empty.

    Back off and retry after the Retry-After header, or the reset in X-RateLimit-Reset if there is no Retry-After. Do not spread the same work across more keys, because the aggregate buckets count them together.

The rest

A 400 is a bad request or a schema failure, 404 is a record that does not exist or that your key may not see, 409 is a conflict such as a booking that no longer fits, 500 is us, and 503 means the database or another dependency is unavailable. Those last two are worth retrying with backoff.

Rate limits

Four buckets, not one

A key-bearing request is counted four times, and the first empty bucket refuses it. One limit per key would be no limit at all, because minting a second key would simply open a second bucket.

  • 600per minute

    Per calling host

    Spent before the route is looked up, so probing costs the prober. Set high because one host may be relaying for a dozen venues.

  • 120per minute

    Per key

    Counted on the token as presented, before it is verified, so an invalid key is counted too.

  • 300per minute

    Per key owner

    Everything one person’s keys generate, however many of them there are.

  • 600per minute

    Per organization

    Everything one venue absorbs, whoever holds the keys.

Requests without a key are bounded separately by the general limit of 100 a minute per address. Counts are shared across every instance, and X-RateLimit-Limit, X-RateLimit-Remaining and X-RateLimit-Reset come back on the response so a client can pace itself instead of discovering the wall.

Worked example

Who is coming in tonight

One real endpoint, start to finish. GET /api/bookings lists an organization’s bookings, filterable by venue, date range, status and guest name. It needs booking:read, which a booking:write key already has.

Request

curl -G https://api.guestavo.com/api/bookings \
  -H "Authorization: Bearer gvsk_your_key_here" \
  --data-urlencode "organizationId=8f14e45f-ceea-467a-9f4c-1b2c3d4e5f60" \
  --data-urlencode "dateFrom=2026-09-14" \
  --data-urlencode "dateTo=2026-09-14" \
  --data-urlencode "status=confirmed" \
  --data-urlencode "limit=20"

Response

{
  "success": true,
  "data": [
    {
      "id": "b7a0c5d2-1f3e-4a58-9c21-6d0e7f8a9b10",
      "propertyId": "3c9d1a77-2b4e-4f60-8d5a-11e2f3a4b5c6",
      "name": "Hribar",
      "date": "2026-09-14",
      "time": "19:30",
      "partySize": 6,
      "status": "confirmed",
      "notes": "Window table if there is one"
    }
  ],
  "meta": { "pagination": { "type": "offset", "count": 1, "page": 1, "limit": 20, "hasMore": false } }
}
  • 01

    organizationId is required and must be the organization the key is pinned to.

  • 02

    propertyId, status, dateFrom, dateTo and search are optional filters, and page with limit walk the results.

  • 03

    A key without analytics:read gets the same bookings with the money fields removed rather than a refusal, because an agent asking for tonight’s diary should get tonight’s diary.

The full contract

This page is not the reference

The API registers well over a thousand routes and this page documents one of them on purpose. What you want next depends on what you are building.

  • 01

    The OpenAPI document

    Generated from the same schemas the server validates against, so it cannot drift from the routes. The interactive browser is deliberately switched off in production, because publishing every route and schema is a map an attacker would enjoy. Running the API locally, it is at /docs.

  • 02

    The agent surface

    If you are wiring an assistant rather than writing a client, the curated set of tools is a much shorter list than this API, and it is described on its own page.

    Read the agent docs
  • 03

    Ask a person

    There is no dedicated developer support queue yet. Tell us what you are building and what the API is doing instead, and a person will read it.

    Get in touch