Skip to content

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.

ClientYour agentTuppence MCP“Tuesday morning?”find_availabilityslotsoffers 10:00create_bookingconfirmed · source: agent“You're booked”
ToolScopeDoes
list_servicesservices:readBookable services: duration, price, options, add-ons, staff.
create_serviceservices:writeAdds a service.
find_availabilityavailability:readStarts between two days for one or more services, with a named person or anyone.
create_bookingbookings:writeBooks an offered start for a customer or client details. Needs idempotency_key.
get_booking, list_bookingsbookings:readOne booking, or a list by day, status, person, service or client.
reschedule_booking, cancel_bookingbookings:writeMoves or cancels; by: "client" applies the policy.
approve_booking, decline_bookingbookings:writeAnswers a request to book.
add_to_waitlistwaitlist:writeWhen 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).

Give the agent a restricted key (Developers → API keys → Restricted); it sees only tools its scopes allow.

ScopesLets it
services:read, availability:read, bookings:writeFind, book, move and cancel
waitlist:writePut 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.

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" } }
SituationAgent behaviour
idempotency_keyRequired. 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_unavailableAlready taken. Offer the nearest free starts in alternatives, or search again.
pending_approvalYour policy requires approval; tell the client it’s a request.
channelThe client’s source (instagram, whatsapp, phone), for reports.
DepositsNot 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.

The JSON-RPC calls an MCP client makes, on an account with a staffed service (Set up bookings):

Terminal window
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 whatsapp

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.

Docs assistant

Open as a page
Loading the assistant…