Bookings for AI agents
An AI agent on your Instagram DMs, WhatsApp or phone line can book through the MCP server. Its tools wrap the API, so the same responses, errors and rules apply: no double-booking, and client moves and cancellations follow your policy.
The tools
Section titled “The tools”| Tool | Scope | Does |
|---|---|---|
list_services | services:read | Bookable services: duration, price, options, add-ons, staff. |
create_service | services:write | Adds a service. |
find_availability | availability:read | Starts between two days for one or more services, with a named person or anyone. |
create_booking | bookings:write | Books an offered start for a customer or client details. Needs idempotency_key. |
get_booking, list_bookings | bookings:read | One booking, or a list by day, status, person, service or client. |
reschedule_booking, cancel_booking | bookings:write | Moves or cancels; by: "client" applies the policy. |
approve_booking, decline_booking | bookings:write | Answers a request to book. |
add_to_waitlist | waitlist:write | When nothing’s free, records the request; the client is offered a held slot when one opens. |
Without location, the account’s only location is used; with several, the tool lists them.
These tools act for you, with your key, so bookings confirm like desk bookings. An agent acting
for a client has no key: it books on your page via agent.json and the client confirms by their
own link (a client’s own agent).
A key for a receptionist
Section titled “A key for a receptionist”Give the agent a restricted key (Developers → API keys → Restricted); it sees only tools its scopes allow.
| Scopes | Lets it |
|---|---|
services:read, availability:read, bookings:write | Find, book, move and cancel |
waitlist:write | Put a client on the waitlist |
These scopes expose no tool that moves money or changes services, team or policy. Add an IP allowlist if it runs from a fixed address.
What a booking looks like
Section titled “What a booking looks like”Find a time, then book exactly what was offered:
{ "name": "find_availability", "arguments": { "service": "svc_…", "from": "2026-10-12", "to": "2026-10-18" } }{ "name": "create_booking", "arguments": { "items": [{ "service": "svc_…" }], "starts_at": "2026-10-13T10:00:00+01:00", "client": { "name": "Sam Patel", "phone": "07700 900123" }, "channel": "instagram", "idempotency_key": "dm-4471-booking" } }{ "name": "create_booking", "arguments": { "items": [{ "service": "svc_…" }], "starts_at": "2026-10-13T10:00:00-04:00", "client": { "name": "Sam Patel", "phone": "(201) 555-0123" }, "channel": "instagram", "idempotency_key": "dm-4471-booking" } }| Situation | Agent behaviour |
|---|---|
idempotency_key | Required. Reuse it on retries after a timeout so the booking is made once. Not auto-generated: MCP request ids are small per-session numbers unrelated calls share. |
slot_unavailable | Already taken. Offer the nearest free starts in alternatives, or search again. |
pending_approval | Your policy requires approval; tell the client it’s a request. |
channel | The client’s source (instagram, whatsapp, phone), for reports. |
| Deposits | Not taken by create_booking; the booking confirms with deposit_amount due (collect it). |
by: "client" | A client’s move or cancellation. Inside your window, moves return policy_window_closed; cancellations keep what the policy keeps. The default is the business, which can always move and owes nothing on cancel. |
Try it
Section titled “Try it”The JSON-RPC calls an MCP client makes, on an account with a staffed service (Set up bookings):
tool() { # call one tool and print its answer curl -sS https://api.tuppence.ai/mcp \ -H "Authorization: Bearer $TUPPENCE_SECRET_KEY" \ -H "Content-Type: application/json" -H "Accept: application/json, text/event-stream" \ -d "{\"jsonrpc\": \"2.0\", \"id\": 1, \"method\": \"tools/call\", \"params\": {\"name\": \"$1\", \"arguments\": $2}}" | jq -r '.result.content[0].text'}today=$(date +%F)week=$(date -v+6d +%F 2>/dev/null || date -d "+6 days" +%F)
service=$(tool list_services '{"status": "active", "limit": 1}' | jq -r '.data[0].id')slot=$(tool find_availability \ "{\"service\": \"$service\", \"from\": \"$today\", \"to\": \"$week\"}" | jq -r '.slots[0].starts_at')tool create_booking "{\"items\": [{\"service\": \"$service\"}], \"starts_at\": \"$slot\", \"client\": {\"name\": \"Sam Patel\", \"email\": \"sam@example.test\"}, \"channel\": \"whatsapp\", \"idempotency_key\": \"$(uuidgen)\"}" | jq -r '"\(.status) \(.source) \(.channel)"' # confirmed agent whatsappOn the record
Section titled “On the record”Agent bookings have source: "agent", and request logs attribute each tool call to the agent and
its key. Filter the bookings list by source to see what your receptionist booked.
- MCP server: connecting a client.
- The Bookings API: the same calls from your code.