Apointa LogoPartner API v1

Partner API

Reservation API for third-party integrations — check availability, book, cancel, change and look up reservations, regardless of the reservation system the restaurant runs.

Base URL

https://apointa.org/api/partner/v1

All requests must use HTTPS.

Authentication

Every endpoint (except /health) requires a bearer token:

Authorization: Bearer apt_live_...

You get exactly one API key per partner account. It covers every restaurant you added in your dashboard. Accessing a restaurant that is not enabled returns 403 FORBIDDEN.

Go to the partner dashboard

Rate limits

60 requests per minute per API key across all endpoints. Exceeding the limit returns 429 Too Many Requests.

Tip: for voice AI, cache availability checks for the same (restaurantId, date, time, partySize) tuple.

Error handling

All errors are returned as JSON:

{
  "success": false,
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Human-readable description"
  }
}
CodeStatusWhen
UNAUTHORIZED401API key missing or invalid
FORBIDDEN403Restaurant not enabled for this API key
VALIDATION_ERROR400Request body fails validation
NOT_FOUND404Restaurant or reservation not found
PROVIDER_ERROR502Upstream reservation system failed
RATE_LIMITED429Rate limit exceeded

Endpoints

GET/health

Liveness probe. No authentication required.

Response

{ "status": "ok", "version": "1.0.0" }
POST/check-availability

Checks whether a given slot is available. No reservation is created.

Request fields

FieldTypeRequiredDescription
restaurantIdstringyesRestaurant ID — you find this in your dashboard
datestringyesYYYY-MM-DD (local date)
timestringyesHH:MM in 24-hour format (local time)
partySizeintegeryesNumber of guests (1-50)

Request

{
  "restaurantId": "35YDWSFbm7I",
  "date": "2026-08-15",
  "time": "19:30",
  "partySize": 4
}

Response

{
  "success": true,
  "data": {
    "available": true,
    "closed": false,
    "alternatives": ["19:00", "19:45", "20:00"]
  }
}
POST/book

Creates a real reservation. Every successful call is a binding booking — do not use it for testing. Only first name and phone number are required.

Request fields

FieldTypeRequiredDescription
restaurantIdstringyesRestaurant ID — you find this in your dashboard
datestringyesYYYY-MM-DD
timestringyesHH:MM
partySizeintegeryesNumber of guests (1-50)
customer.firstNamestringyesGuest's first name
customer.lastNamestringnoGuest's last name. If omitted we book with a neutral placeholder.
customer.phonestring[]yesGuest's phone numbers as an array, primary number first
customer.emailstringnoGuest's email
specialRequestsstringnoFree-text note

Request

{
  "restaurantId": "35YDWSFbm7I",
  "date": "2026-08-15",
  "time": "19:30",
  "partySize": 4,
  "customer": {
    "firstName": "Max",
    "lastName": "Mustermann",
    "phone": ["+491701234567", "+4989123456"],
    "email": "max@example.com"
  },
  "specialRequests": "Table by the window"
}

Response

{
  "success": true,
  "data": {
    "reservationId": "gn-a1b2c3d4",
    "status": "confirmed",
    "date": "2026-08-15",
    "time": "19:30",
    "partySize": 4
  }
}
POST/cancel

Cancels a reservation. Either by reservationId or — if your system does not know it — by phone number, optionally narrowed down with date and time.

Request fields

FieldTypeRequiredDescription
restaurantIdstringyesRestaurant ID — you find this in your dashboard
reservationIdstringnoId from the /book response. Alternatively look up by phone number.
phonestring[]noGuest's phone numbers as an array — alternative numbers are searched too.
datestringnoDate of the reservation, narrows down multiple matches
timestringnoTime of the reservation, narrows down multiple matches

Request

{
  "restaurantId": "35YDWSFbm7I",
  "reservationId": "gn-a1b2c3d4"
}

Response

{
  "success": true,
  "data": {
    "reservationId": "gn-a1b2c3d4",
    "status": "cancelled",
    "date": "2026-08-15",
    "time": "19:30"
  }
}
POST/update

Requests a change. The reservation is located the same way as in /cancel. The restaurant is notified and applies the change.

Request fields

FieldTypeRequiredDescription
restaurantIdstringyesRestaurant ID — you find this in your dashboard
reservationIdstringnoId from the /book response. Alternatively look up by phone number.
phonestring[]noGuest's phone numbers as an array — alternative numbers are searched too.
datestringnoDate of the reservation, narrows down multiple matches
timestringnoTime of the reservation, narrows down multiple matches
updates.datestringnoNew date YYYY-MM-DD
updates.timestringnoNew time HH:MM
updates.partySizeintegernoNew party size
updates.specialRequestsstringnoNew note

Request

{
  "restaurantId": "35YDWSFbm7I",
  "phone": ["+491701234567"],
  "date": "2026-08-15",
  "time": "19:30",
  "updates": {
    "partySize": 6,
    "specialRequests": "Guest with allergies"
  }
}

Response

{
  "success": true,
  "data": {
    "reservationId": "gn-a1b2c3d4",
    "status": "pending",
    "message": "Update request forwarded to restaurant."
  }
}
POST/reservations

Looks up existing reservations by phone number. Only future reservations are returned.

Request fields

FieldTypeRequiredDescription
restaurantIdstringyesRestaurant ID — you find this in your dashboard
phonestring[]yesGuest's phone numbers as an array — alternative numbers are searched too.

Request

{
  "restaurantId": "35YDWSFbm7I",
  "phone": ["+491701234567", "+4989123456"]
}

Response

{
  "success": true,
  "data": {
    "reservations": [
      {
        "reservationId": "gn-a1b2c3d4",
        "date": "2026-08-15",
        "time": "19:30",
        "partySize": 4,
        "specialRequests": "Table by the window"
      }
    ]
  }
}

Important: only reservations from this system

The API only sees reservations that were created through this system. If the guest booked by phone, on the restaurant's website or through any other channel, it is invisible to /reservations, /update and /cancel.

In that case /cancel and /update respond with 404 NOT_FOUND. At the same time we automatically notify the restaurant by email with everything you sent us.

{
  "success": false,
  "error": {
    "code": "NOT_FOUND",
    "message": "No reservation found that was created through this API. The restaurant staff has been notified and will handle the cancellation."
  }
}

Catch this case in your system and tell the caller that a staff member has been notified and will take care of it — that is exactly what happens in the background.

Phone number format

Always send the country code in E.164 format:

  • +491701234567 (Germany)
  • +436641234567 (Austria)
  • +41791234567 (Switzerland)

Phone numbers are always passed as an array. If your system knows a second number of the guest, send it along — we search all of them.

Support

Contact the Apointa team with: request timestamp (UTC), restaurantId, full request body (redact personal data), HTTP status and response body.

hey@apointa.org

Partner API Documentation - Apointa