Your AI Connector Docs

Appointments

The Appointments API lets you book appointments for your contacts on your event types, then fetch, list, update, cancel, or delete them. It also answers the question that comes first in most booking flows — which times are actually free — and covers the calendar side: listing the Google Calendars you have connected and importing events that already live in them. When a Google Calendar connection is active, the matching calendar event is created and kept in sync automatically in the background. Restaurants using Zenchef or Formitable for their own reservations system can also be verified and connected here, so the AI Agent books real tables instead of internal appointments.

All paths on this page are relative to the base URL https://api.youraiconnector.com/v1. Every request needs your API key — see Authentication for the full list of ways to send it. The examples below use the X-API-Key header, with one cURL example showing the ?apiKey= query form too.

Events vs. appointments: An event type is a bookable slot definition (the kind of meeting, its length, its rooms). An appointment is one booked instance of an event type for a specific contact. You book an appointment by referencing the contact and the event type.


The appointment object

Every endpoint that returns an appointment uses the same shape:

Field Description
id Unique ID of the appointment.
contact_id ID of the contact the appointment is booked with.
event_id ID of the event type the appointment was booked on.
status Confirmed or Canceled.
start_time Start of the appointment, ISO 8601 in UTC.
end_time End of the appointment, ISO 8601 in UTC.
created_at When the appointment was created.
last_modified_at When the appointment was last changed.
room_name Room or resource the appointment is booked in, when the event type uses rooms.
description Free-form description of the appointment.
summary Short summary or title.
cancelation_reason Reason supplied when the appointment was canceled, if any.
google_calendar_event_id ID of the linked Google Calendar event. Set once calendar sync completes; null when no calendar is connected or while the sync is still in progress.
calendar_synced true once the appointment is linked to a calendar event.
imported true when the appointment was imported from an external calendar rather than booked directly.
is_recurring true when the appointment is part of a recurring series.
recurrence_frequency How often the appointment repeats, when recurring.
recurring_event_id ID of the recurring series this appointment belongs to.
recurring_interval Interval between repetitions, when recurring.
recurring_sequence Position of this appointment within its recurring series.
end_after_x_occurrences Number of occurrences after which the recurring series ends.
booking_provider Source system the booking came from, when booked through a connected reservation provider.

About calendar sync: Right after you book or change an appointment, google_calendar_event_id may still be null and calendar_synced may be false because the sync runs in the background a moment later. Fetch the appointment again shortly afterward to see the populated calendar fields.


Find available slots

GET /appointments/available-slots

Returns the times that are genuinely free on one event type between two moments. This is normally the first call in a booking flow: show these slots, let the person pick one, then post the chosen time to Book an appointment.

The answer already takes into account the event type’s own opening hours and slot length, its rooms, appointments you have already booked on it, and everything blocked on the connected Google Calendars — so a slot that comes back here is one you can book.

Query parameter Required Description
event_id Yes The event type to check. Must belong to your account.
start_time Yes Start of the window you want slots for, ISO 8601 date-time.
end_time Yes End of the window, ISO 8601 date-time. The whole end day is included.

Results come back grouped by day — and, when the event type uses rooms, one group per room per day:

Field Description
date The day the group covers, written DD/MM/YYYY.
day Weekday name in lower case, for example monday.
room_name The room or resource this group belongs to, when the event type uses rooms.
available_slots The bookable blocks on that day, earliest first.

Each entry in available_slots has:

Field Description
start_time Block start as HH:mm.
end_time Block end as HH:mm.
available true — only free time is returned.
spots_left How many bookings still fit in this block. Only present on event types that take more than one booking per slot.

Times are local to the event type, not UTC. date, start_time, and end_time are wall-clock values in the event type’s own timezone (its override, or your account timezone when it has none). Book an appointment expects an ISO 8601 UTC instant, so convert the slot you picked before posting it.

cURL

curl "https://api.youraiconnector.com/v1/appointments/available-slots?event_id=event_xyz789&start_time=2026-06-15T00:00:00.000Z&end_time=2026-06-19T00:00:00.000Z" \
  -H "X-API-Key: YOUR_API_KEY"

JavaScript

const params = new URLSearchParams({
  event_id: "event_xyz789",
  start_time: "2026-06-15T00:00:00.000Z",
  end_time: "2026-06-19T00:00:00.000Z",
});
const res = await fetch(
  `https://api.youraiconnector.com/v1/appointments/available-slots?${params}`,
  { headers: { "X-API-Key": "YOUR_API_KEY" } }
);
const data = await res.json();
console.log(data.data);

Python

import requests

res = requests.get(
    "https://api.youraiconnector.com/v1/appointments/available-slots",
    headers={"X-API-Key": "YOUR_API_KEY"},
    params={
        "event_id": "event_xyz789",
        "start_time": "2026-06-15T00:00:00.000Z",
        "end_time": "2026-06-19T00:00:00.000Z",
    },
)
print(res.json()["data"])

Response (200 OK):

{
  "success": true,
  "data": [
    {
      "date": "15/06/2026",
      "day": "monday",
      "room_name": "Room A",
      "available_slots": [
        { "start_time": "10:00", "end_time": "10:30", "available": true },
        { "start_time": "10:30", "end_time": "11:00", "available": true }
      ]
    },
    {
      "date": "16/06/2026",
      "day": "tuesday",
      "room_name": "Room A",
      "available_slots": [
        { "start_time": "09:00", "end_time": "09:30", "available": true, "spots_left": 2 }
      ]
    }
  ]
}

A day with nothing free simply does not appear. Missing event_id, start_time, or end_time returns 400; an event type that is not on your account returns 404.


Book an appointment

POST /appointments

Books a new appointment for a contact on one of your event types. The end time is calculated automatically from the event type’s slot duration.

The booking is conflict-checked: if the requested slot overlaps an existing confirmed appointment on the same event type, the request fails with a 409 and nothing is created.

Field Required Description
contact_id Yes ID of the contact to book for. Must belong to your account.
event_id Yes ID of the event type to book on. Must belong to your account.
start_time Yes Desired start as an ISO 8601 date-time.
room_name No Room or resource name, when the event type uses rooms.

cURL (using the ?apiKey= query form)

curl -X POST "https://api.youraiconnector.com/v1/appointments?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "contact_id": "contact_abc123",
    "event_id": "event_xyz789",
    "start_time": "2026-06-15T10:00:00.000Z",
    "room_name": "Room A"
  }'

JavaScript

const res = await fetch("https://api.youraiconnector.com/v1/appointments", {
  method: "POST",
  headers: {
    "X-API-Key": "YOUR_API_KEY",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    contact_id: "contact_abc123",
    event_id: "event_xyz789",
    start_time: "2026-06-15T10:00:00.000Z",
    room_name: "Room A",
  }),
});
const data = await res.json();
console.log(data.appointment_id);

Python

import requests

res = requests.post(
    "https://api.youraiconnector.com/v1/appointments",
    headers={
        "X-API-Key": "YOUR_API_KEY",
        "Content-Type": "application/json",
    },
    json={
        "contact_id": "contact_abc123",
        "event_id": "event_xyz789",
        "start_time": "2026-06-15T10:00:00.000Z",
        "room_name": "Room A",
    },
)
print(res.json()["appointment_id"])

Response (201 Created):

{
  "success": true,
  "appointment_id": "aBcD1234eFgH5678",
  "appointment": {
    "id": "aBcD1234eFgH5678",
    "contact_id": "contact_abc123",
    "event_id": "event_xyz789",
    "status": "Confirmed",
    "start_time": "2026-06-15T10:00:00.000Z",
    "end_time": "2026-06-15T10:30:00.000Z",
    "created_at": "2026-06-10T09:00:00.000Z",
    "last_modified_at": "2026-06-10T09:00:00.000Z",
    "room_name": "Room A",
    "google_calendar_event_id": null,
    "calendar_synced": false
  }
}

Get an appointment

GET /appointments/{appointmentId}

Returns a single appointment by its ID, including its calendar sync state.

cURL

curl "https://api.youraiconnector.com/v1/appointments/aBcD1234eFgH5678" \
  -H "X-API-Key: YOUR_API_KEY"

JavaScript

const res = await fetch(
  "https://api.youraiconnector.com/v1/appointments/aBcD1234eFgH5678",
  { headers: { "X-API-Key": "YOUR_API_KEY" } }
);
const data = await res.json();
console.log(data.appointment);

Python

import requests

res = requests.get(
    "https://api.youraiconnector.com/v1/appointments/aBcD1234eFgH5678",
    headers={"X-API-Key": "YOUR_API_KEY"},
)
print(res.json()["appointment"])

Response (200 OK):

{
  "success": true,
  "appointment": {
    "id": "aBcD1234eFgH5678",
    "contact_id": "contact_abc123",
    "event_id": "event_xyz789",
    "status": "Confirmed",
    "start_time": "2026-06-15T10:00:00.000Z",
    "end_time": "2026-06-15T10:30:00.000Z",
    "room_name": "Room A",
    "google_calendar_event_id": "abc123googleevent",
    "calendar_synced": true
  }
}

List appointments

GET /appointments

Lists appointments for your account, newest first, with cursor-based pagination.

Query parameter Required Description
contact_id No Only return appointments for this contact. Contact-filtered listings include confirmed appointments only.
date No Only return appointments on this calendar day (YYYY-MM-DD). Requires contact_id.
status No Filter by Confirmed or Canceled. Only available without contact_id.
limit No Page size, an integer between 1 and 100. Default 50.
cursor No The next_cursor value from a previous response.

A few rules to keep in mind:

  • Without filters, you get every appointment on the account, page by page.
  • By contact — set contact_id to see one contact’s confirmed appointments. You can narrow this to a single day by also passing date.
  • By status — set status (without contact_id) to list only Confirmed or only Canceled appointments across the account.
  • The date filter without contact_id, or status=Canceled together with contact_id, returns a 400.

cURL

curl "https://api.youraiconnector.com/v1/appointments?contact_id=contact_abc123&date=2026-06-15" \
  -H "X-API-Key: YOUR_API_KEY"

JavaScript

const params = new URLSearchParams({
  contact_id: "contact_abc123",
  date: "2026-06-15",
});
const res = await fetch(
  `https://api.youraiconnector.com/v1/appointments?${params}`,
  { headers: { "X-API-Key": "YOUR_API_KEY" } }
);
const data = await res.json();
console.log(data.appointments, data.next_cursor);

Python

import requests

res = requests.get(
    "https://api.youraiconnector.com/v1/appointments",
    headers={"X-API-Key": "YOUR_API_KEY"},
    params={"contact_id": "contact_abc123", "date": "2026-06-15"},
)
data = res.json()
print(data["appointments"], data["next_cursor"])

Response (200 OK):

{
  "success": true,
  "appointments": [
    {
      "id": "aBcD1234eFgH5678",
      "contact_id": "contact_abc123",
      "event_id": "event_xyz789",
      "status": "Confirmed",
      "start_time": "2026-06-15T10:00:00.000Z",
      "end_time": "2026-06-15T10:30:00.000Z",
      "calendar_synced": true
    }
  ],
  "next_cursor": null
}

To page through results, pass the next_cursor from one response as the cursor of the next request. Keep going until next_cursor is null. See Errors & Pagination for the shared pagination pattern.


Update an appointment

PUT /appointments/{appointmentId}

Reschedule an appointment or change its details. Send only the fields you want to change — at least one is required. The combined start and end must stay in chronological order (end_time must be after start_time). Changes are synced to the linked calendar event automatically.

Field Description
start_time New start, ISO 8601 date-time.
end_time New end, ISO 8601 date-time. Must be after the start time.
room_name New room or resource name.
description New description, or null to clear it.
summary New summary, or null to clear it.

cURL

curl -X PUT "https://api.youraiconnector.com/v1/appointments/aBcD1234eFgH5678" \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "start_time": "2026-06-16T10:00:00.000Z",
    "end_time": "2026-06-16T10:30:00.000Z"
  }'

JavaScript

const res = await fetch(
  "https://api.youraiconnector.com/v1/appointments/aBcD1234eFgH5678",
  {
    method: "PUT",
    headers: {
      "X-API-Key": "YOUR_API_KEY",
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      start_time: "2026-06-16T10:00:00.000Z",
      end_time: "2026-06-16T10:30:00.000Z",
    }),
  }
);
const data = await res.json();
console.log(data.appointment);

Python

import requests

res = requests.put(
    "https://api.youraiconnector.com/v1/appointments/aBcD1234eFgH5678",
    headers={
        "X-API-Key": "YOUR_API_KEY",
        "Content-Type": "application/json",
    },
    json={
        "start_time": "2026-06-16T10:00:00.000Z",
        "end_time": "2026-06-16T10:30:00.000Z",
    },
)
print(res.json()["appointment"])

Response (200 OK):

{
  "success": true,
  "appointment_id": "aBcD1234eFgH5678",
  "appointment": {
    "id": "aBcD1234eFgH5678",
    "contact_id": "contact_abc123",
    "event_id": "event_xyz789",
    "status": "Confirmed",
    "start_time": "2026-06-16T10:00:00.000Z",
    "end_time": "2026-06-16T10:30:00.000Z",
    "calendar_synced": true
  }
}

Cancel an appointment

POST /appointments/{appointmentId}/cancel

Cancels a confirmed appointment, optionally recording a reason. The appointment stays in your account with status Canceled, and the linked calendar event is removed automatically in the background. Cancelling an already-canceled appointment returns a 400.

Field Required Description
cancellation_reason No Reason for the cancellation, stored on the appointment.

cURL

curl -X POST "https://api.youraiconnector.com/v1/appointments/aBcD1234eFgH5678/cancel" \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "cancellation_reason": "Client asked to reschedule next month"
  }'

JavaScript

const res = await fetch(
  "https://api.youraiconnector.com/v1/appointments/aBcD1234eFgH5678/cancel",
  {
    method: "POST",
    headers: {
      "X-API-Key": "YOUR_API_KEY",
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      cancellation_reason: "Client asked to reschedule next month",
    }),
  }
);
const data = await res.json();
console.log(data.success);

Python

import requests

res = requests.post(
    "https://api.youraiconnector.com/v1/appointments/aBcD1234eFgH5678/cancel",
    headers={
        "X-API-Key": "YOUR_API_KEY",
        "Content-Type": "application/json",
    },
    json={"cancellation_reason": "Client asked to reschedule next month"},
)
print(res.json()["success"])

Response (200 OK):

{
  "success": true,
  "appointment_id": "aBcD1234eFgH5678"
}

Delete an appointment

DELETE /appointments/{appointmentId}

Permanently deletes an appointment and its references. If you only want to call the booking off while keeping the record, use cancel instead.

cURL

curl -X DELETE "https://api.youraiconnector.com/v1/appointments/aBcD1234eFgH5678" \
  -H "X-API-Key: YOUR_API_KEY"

JavaScript

const res = await fetch(
  "https://api.youraiconnector.com/v1/appointments/aBcD1234eFgH5678",
  { method: "DELETE", headers: { "X-API-Key": "YOUR_API_KEY" } }
);
const data = await res.json();
console.log(data.success);

Python

import requests

res = requests.delete(
    "https://api.youraiconnector.com/v1/appointments/aBcD1234eFgH5678",
    headers={"X-API-Key": "YOUR_API_KEY"},
)
print(res.json()["success"])

Response (200 OK):

{
  "success": true
}

List your connected Google Calendars

GET /appointments/google-calendars

Returns the Google Calendars available on this account, straight from Google — useful for showing the account holder a picker of which calendar to import from below, or just to confirm the connection is live.

This only works once the account has connected Google Calendar (Settings → Integrations) with at least read access. If it hasn’t, or the granted access no longer includes the calendar-read scope, you get a 400 telling you to (re)connect it.

cURL

curl "https://api.youraiconnector.com/v1/appointments/google-calendars" \
  -H "X-API-Key: YOUR_API_KEY"

JavaScript

const res = await fetch("https://api.youraiconnector.com/v1/appointments/google-calendars", {
  headers: { "X-API-Key": "YOUR_API_KEY" },
});
const data = await res.json();
console.log(data.data);

Python

import requests

res = requests.get(
    "https://api.youraiconnector.com/v1/appointments/google-calendars",
    headers={"X-API-Key": "YOUR_API_KEY"},
)
print(res.json()["data"])

Response (200 OK):

{
  "success": true,
  "data": [
    {
      "id": "primary",
      "summary": "jane@example.com",
      "timeZone": "America/New_York",
      "accessRole": "owner",
      "primary": true
    },
    {
      "id": "abcdefg1234567890@group.calendar.google.com",
      "summary": "Bookings",
      "timeZone": "America/New_York",
      "accessRole": "writer"
    }
  ]
}

Each entry is Google’s own CalendarListEntry shape, so field names follow Google’s camelCase, not this API’s usual snake_case — that’s Google’s data passed through as-is, not ours. A missing or revoked connection returns 400 with an error explaining Google Calendar needs to be (re)connected.


Import events from a Google Calendar

POST /appointments/import-calendar-events

Pulls the events already sitting in a campaign’s or AI Agent’s connected Google Calendar(s) and turns them into appointments — useful the first time you connect a calendar that already has bookings on it. This can take a while (each event goes through extraction to figure out who it’s for), so it never runs inline: the request enqueues a background job and hands you back a job_id to poll.

Field Required Description
campaign_id One of these two The campaign whose connected calendar(s) to import from.
agent_id One of these two The AI Agent whose connected calendar(s) to import from.
identifier Yes "EMAIL" or "PHONE_NUMBER" — which piece of contact info to extract from each calendar event to match or create the contact it belongs to.

Send exactly one of campaign_id / agent_id, never both and never neither — either combination returns a 400. Whichever one you send must belong to your account, or you get a 404.

cURL

curl -X POST "https://api.youraiconnector.com/v1/appointments/import-calendar-events?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "agent_id": "agent_abc123",
    "identifier": "EMAIL"
  }'

JavaScript

const res = await fetch("https://api.youraiconnector.com/v1/appointments/import-calendar-events", {
  method: "POST",
  headers: {
    "X-API-Key": "YOUR_API_KEY",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    agent_id: "agent_abc123",
    identifier: "EMAIL",
  }),
});
const data = await res.json();
console.log(data.job_id);

Python

import requests

res = requests.post(
    "https://api.youraiconnector.com/v1/appointments/import-calendar-events",
    headers={
        "X-API-Key": "YOUR_API_KEY",
        "Content-Type": "application/json",
    },
    json={"agent_id": "agent_abc123", "identifier": "EMAIL"},
)
print(res.json()["job_id"])

Response (202 Accepted):

{
  "success": true,
  "job_id": "jK9mQ2xR7pL4wN1t",
  "status": "queued",
  "campaign_id": null,
  "agent_id": "agent_abc123"
}

campaign_id and agent_id echo back whichever one you sent; the other is always null.

Poll the import job

GET /appointments/import-calendar-events/{jobId}

curl "https://api.youraiconnector.com/v1/appointments/import-calendar-events/jK9mQ2xR7pL4wN1t" \
  -H "X-API-Key: YOUR_API_KEY"

Response (200 OK):

{
  "success": true,
  "job_id": "jK9mQ2xR7pL4wN1t",
  "status": "completed",
  "message": "Imported 12 events as appointments.",
  "error": null
}
status Meaning
queued Not picked up yet. Keep polling.
processing The import is running. Keep polling.
completed Done — message has a short human-readable summary.
failed Something went wrong — error has the reason.

GET on a jobId that doesn’t exist (or belongs to a different account) returns 404.


Restaurant booking integrations (Zenchef / Formitable)

Zenchef and Formitable are restaurant reservation systems your AI Agent can book real tables through. Each has a public, unauthenticated booking widget (https://api.youraiconnector.com/v1/zenchef-widget/... and https://api.youraiconnector.com/v1/formitable-widget/...) that renders inside chat for the diner — those widget routes are plain HTML pages meant to be opened in a browser, not JSON API endpoints, so they aren’t documented here. What follows are the account-management endpoints: verifying a restaurant ID belongs to the account holder, then adding, updating, or removing it.

Zenchef

Connecting a Zenchef restaurant is a two-step verification, so the account holder proves they actually run the restaurant before it gets wired into the bot: first check the ID exists (without revealing the name), then have them type the restaurant’s name themselves and verify it matches.

Step 1 — Check a restaurant ID exists

POST /appointments/zenchef-restaurants/check

Field Required Description
restaurant_id Yes The Zenchef restaurant ID to check.
curl -X POST "https://api.youraiconnector.com/v1/appointments/zenchef-restaurants/check?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "restaurant_id": "12345" }'

Response (200 OK):

{
  "success": true,
  "data": { "exists": true, "requiresNameVerification": true }
}

exists: false means no Zenchef restaurant has that ID — nothing else to do. Rate-limited to 10 checks per 5 minutes per account; going over returns 429.

Step 2 — Verify the restaurant’s name

POST /appointments/zenchef-restaurants/verify-name

Field Required Description
restaurant_id Yes The Zenchef restaurant ID from step 1.
user_input_name Yes The name the account holder typed in — compared against the restaurant’s real name on Zenchef (case/whitespace-insensitive).
curl -X POST "https://api.youraiconnector.com/v1/appointments/zenchef-restaurants/verify-name?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "restaurant_id": "12345", "user_input_name": "The Blue Door Bistro" }'

Response (200 OK):

{
  "success": true,
  "data": {
    "verified": true,
    "restaurantDetails": {
      "id": "12345",
      "name": "The Blue Door Bistro",
      "address": "1 Rue de Rivoli, Paris",
      "status": "active"
    }
  }
}

verified: false means the name didn’t match — restaurantDetails is omitted, ask the account holder to try again. Rate-limited to 3 attempts per 5 minutes (tighter than the existence check, since this is the actual proof step). A restaurant_id that no longer resolves on Zenchef returns 404.

Step 3 — Save the restaurant

POST /appointments/zenchef-restaurants

Field Required Description
restaurant_id Yes 1–64 chars, letters/numbers/underscore/hyphen.
restaurant_name Yes The verified restaurant name from step 2.
curl -X POST "https://api.youraiconnector.com/v1/appointments/zenchef-restaurants?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "restaurant_id": "12345", "restaurant_name": "The Blue Door Bistro" }'

Response (201 Created):

{ "success": true, "data": { "restaurantId": "12345" } }

Update a saved Zenchef restaurant

PUT /appointments/zenchef-restaurants/{restaurantId}

Field Required Description
restaurant_name No New display name.
is_active No Set false to stop the bot from booking against this restaurant without removing it.
curl -X PUT "https://api.youraiconnector.com/v1/appointments/zenchef-restaurants/12345" \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "is_active": false }'

Response (200 OK): same shape as the save response above.

Remove a Zenchef restaurant

DELETE /appointments/zenchef-restaurants/{restaurantId}

curl -X DELETE "https://api.youraiconnector.com/v1/appointments/zenchef-restaurants/12345" \
  -H "X-API-Key: YOUR_API_KEY"

Response (200 OK): { "success": true, "data": { "restaurantId": "12345" } }

A restaurantId not currently on the account returns 404 on update or delete.

Formitable

Formitable doesn’t need the two-step name proof Zenchef does — its restaurant IDs are already scoped per business, so one verification call is enough. It also has a details lookup used to cache the restaurant’s website URL during setup.

Verify a restaurant ID

POST /appointments/formitable-restaurants/verify

Field Required Description
restaurant_id Yes The Formitable restaurant ID.
language No Language tag for the probe request. Defaults to "nl".
curl -X POST "https://api.youraiconnector.com/v1/appointments/formitable-restaurants/verify?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "restaurant_id": "the-blue-door", "language": "en" }'

Response (200 OK):

{
  "success": true,
  "data": {
    "verified": true,
    "restaurantDetails": {
      "restaurantId": "the-blue-door",
      "productCount": 4,
      "sampleProductTitle": "Dinner for two",
      "language": "en"
    }
  }
}

A restaurant_id Formitable doesn’t recognize returns 404. Rate-limited to 10 attempts per 5 minutes per account.

Get restaurant details

GET /appointments/formitable-restaurants/{restaurantId}/details?language=en

Fetches the restaurant’s public profile from Formitable, including its website — used to cache the website URL while setting the restaurant up. language is an optional query parameter, defaulting to "en".

curl "https://api.youraiconnector.com/v1/appointments/formitable-restaurants/the-blue-door/details?language=en" \
  -H "X-API-Key: YOUR_API_KEY"

Response (200 OK):

{
  "success": true,
  "data": {
    "uid": "the-blue-door",
    "name": "The Blue Door Bistro",
    "website": "https://thebluedoorbistro.com",
    "email": "info@thebluedoorbistro.com",
    "telephone": "+31201234567",
    "streetAddress": "Prinsengracht 1",
    "zipcode": "1015 AB",
    "city": "Amsterdam",
    "country": "Netherlands",
    "countryCode": "NL",
    "currency": "EUR"
  }
}

Save the restaurant

POST /appointments/formitable-restaurants

Field Required Description
restaurant_id Yes 1–64 chars, letters/numbers/underscore/hyphen.
restaurant_name Yes Display name.
language Yes ISO language tag, e.g. "en" or "en-GB".
website_url No The restaurant’s website, from the details lookup above. Must be http(s)://.
curl -X POST "https://api.youraiconnector.com/v1/appointments/formitable-restaurants?apiKey=YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "restaurant_id": "the-blue-door",
    "restaurant_name": "The Blue Door Bistro",
    "language": "en",
    "website_url": "https://thebluedoorbistro.com"
  }'

Response (201 Created): { "success": true, "data": { "restaurantId": "the-blue-door" } }

Update a saved Formitable restaurant

PUT /appointments/formitable-restaurants/{restaurantId}

Field Required Description
restaurant_name No New display name.
language No New ISO language tag.
is_active No Set false to stop the bot from booking against this restaurant without removing it.
website_url No New website URL.
curl -X PUT "https://api.youraiconnector.com/v1/appointments/formitable-restaurants/the-blue-door" \
  -H "X-API-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "is_active": false }'

Response (200 OK): same shape as the save response above.

Remove a Formitable restaurant

DELETE /appointments/formitable-restaurants/{restaurantId}

curl -X DELETE "https://api.youraiconnector.com/v1/appointments/formitable-restaurants/the-blue-door" \
  -H "X-API-Key: YOUR_API_KEY"

Response (200 OK): { "success": true, "data": { "restaurantId": "the-blue-door" } }

A restaurantId not currently on the account returns 404 on update or delete.

Error shape on all Zenchef/Formitable endpoints: unlike the rest of this page, errors here carry their status twice — once as the HTTP status and once as error_code in the body — for example { "success": false, "error": "Restaurant not found", "error_code": 404 }. Handle it the same way as any other error: check success, read error for the message.


Appointments API errors

Appointment endpoints return the standard error envelope:

{
  "success": false,
  "error": "Appointment not found"
}
Status When it happens on an appointment endpoint
400 A required field is missing or invalid — for example a bad start_time, an end_time not after start_time, an invalid filter combination, no fields to update, or an already-canceled appointment.
404 The appointment, contact, or event type was not found.
409 The requested time slot is already taken (booking conflict).

The shared codes every endpoint can return — 401, 403 (your plan does not include API access), 429 (rate limit) and 500 — are listed with retry guidance in Errors & Pagination.


Next steps