#An AI agent that books appointments in your app
An AI agent for bookings lets the signed-in users of your app check availability, book, move and cancel appointments by asking in the app's chat. It answers questions about your services from what you wrote, and hands anything else to a person. Your coding agent builds it from your app's project, with tools that call your app's own bookings API, and Vatio runs it inside your app. Vatio has no calendar of its own: your app stays the place where a booking exists.
The same agent fits any app with bookings: clinics, salons, vets, restaurant tables, hotel stays, property viewings, service visits and consultations.
#What the agent does
- Shows availability. "Anything with Dr. Ruiz on Thursday?" It asks what the user needs, gets free slots from your API and offers a few.
- Books. For the signed-in user, in the slot they picked, and it only says the appointment is booked when your API booked it.
- Reads the user's own appointments. "When is my next session?", from your API, for that user and nobody else.
- Moves and cancels, under the rules your backend applies: how much notice, and whether a fee applies.
- Answers questions about services, prices, how to prepare and where you are, from knowledge you write.
- Hands everything else to a person: exceptions to your policy, complaints, refunds and anyone who asks for a person. It calls a tool that alerts your team with a link to the conversation, and whoever replies from the inbox takes it over (Hand off to a person).
A typical exchange in your app's chat widget, with a signed-in user:
User: can I move my thursday appointment to next week?
Agent: Sure. You have Thursday at 10:00 with Dr. Ruiz. Next week she has Monday at 9:30, Tuesday at 16:00 or Wednesday at 11:00. Which one works?
User: tuesday
Agent: Done: you're booked for Tuesday at 16:00 with Dr. Ruiz, and Thursday is cancelled.
Behind those replies were calls to my_appointments, find_slots and reschedule_appointment. Each one shows in the inbox next to the reply it produced, with what your API answered.
#Start in your app's project
From a chat, the fastest start: add https://vatio.ai/mcp as a connector in Claude, ChatGPT or Cursor, sign in with your Vatio account and ask for this agent. It is created in a preview you can try; nothing reaches your users until you publish (Vatio in Claude and ChatGPT).
To build it into your app's code, run this in the app's project:
npx @vatio-ai/skills/vatio-initRun the first line in a terminal in your app's project and the second in Claude Code. In Cursor or Codex, ask it to use vatio-init. You can say what you want after the command:
/vatio-init an agent that lets signed-in users book, move and cancel appointments through our bookings APIvatio-init reads your app: the API an agent could call, how your users sign in, and the docs or policies it could use as knowledge. It tells you what it found and what it recommends before writing anything. The first time it pushes the agent, your browser opens to sign in with email, Google or GitHub; signing in creates your account.
#What vatio-init will ask you
Your code says which booking endpoints exist. It doesn't say how you want them used, so that is what vatio-init asks:
- What may the agent book, move or cancel? Which services, with whom, and how far ahead.
- What are your rules for moving and cancelling? How much notice, whether a fee applies, and who can make an exception. Your backend enforces them; the agent explains them.
- What must it never say or promise? A slot your API didn't return, a waived fee, a discount, a medical opinion.
- When does a person take over? Complaints, refunds, exceptions to the policy, anyone who asks for a person.
Your opening hours go in the agent's instructions, next to what the business does, and the agent reads them from there. Who is told when a conversation needs a person is up to your backend: the agent calls a tool with a link to the conversation, and your backend alerts whoever should answer, by WhatsApp, Slack or email.
The answers become the agent's instructions, its tools, including when it asks for a person, and its knowledge.
#How it books through your app
Every tool is one HTTP request to your app's backend. There is no calendar inside Vatio and no calendar integration to connect: the agent asks your API for free slots, and your API creates, moves and cancels the booking. If your app keeps its schedule in a calendar service, your backend talks to that service and the agent talks to your backend. Connect your backend covers the endpoints.
To act for a user, the agent has to know who they are:
- Your backend signs (on Supabase, an Edge Function that reads the user's Supabase session does it) a short-lived token saying who is signed in and passes it to the widget. Your coding agent can add that to your backend (Authentication): the private key stays in your backend, and Vatio holds only the public key, so it can check a token but never create one.
A tool marked private forwards that token to your API on every call. Your endpoint verifies it and takes the user from it. A private tool cannot take a user id as a parameter: the deploy fails if one tries, because the model fills parameters and could be talked into filling that one with someone else's id. So "cancel the booking for maria@example.com" reaches nothing that isn't the signed-in user's own. Someone who isn't signed in only gets your public tools, and a user who signs in halfway through keeps the same conversation. See Private tools.
#What your coding agent writes
You don't need to edit these files by hand, but they live in your project, so you can read and review them like the rest of your code. An excerpt of vatio.yml:
workspace: northside-physioauth: public_key: identity.pubagent: name: Northside Physio instructions: | You help signed-in patients of Northside Physio see, book, move and cancel their appointments, and answer questions about treatments, prices and the clinic.
Booking: ask what they need and a preferred day. Call find_slots and offer at most three options. Read the booking back before calling book_appointment. Only say it is booked when book_appointment returned it.
Moving and cancelling: find the appointment with my_appointments, then call reschedule_appointment or cancel_appointment. If a tool says it is too late, explain the policy and offer to pass the request to the team.
Rules you never break: - Never offer a slot that find_slots did not return. - Never waive a cancellation fee or promise an exception. - Never give medical advice. Call ask_for_a_person for questions about pain or symptoms. tools: [find_slots, my_appointments, book_appointment, reschedule_appointment, cancel_appointment, ask_for_a_person, knowledge_lookup] knowledge: [clinic]tools/book_appointment.yml:
description: Book a free slot for the signed-in patient.when_to_use: The patient chose a slot from find_slots and confirmed it when you read it back.parameters: type: object properties: slot_id: { type: string, description: "The id find_slots returned" } notes: { type: string, description: "Anything the physio should know beforehand" } required: [slot_id]access: privaterequest: method: POST base_url: "$env.APP_API" path: /me/appointments headers: Authorization: "Bearer $auth.token" body: slot_id: notes:respond: data: appointment: "$.appointment"There is no patient id in it: your endpoint books for the user the token names. When something fails, it answers with a real status and a message, such as a 409 with { "message": "That slot was just taken.", "error_key": "slot_taken" }, and the agent says so and offers another slot instead of confirming a booking that doesn't exist. cancel_appointment works the same way: your backend checks the notice period and, when it is too late, refuses with a message that says why. On Supabase, the same file calls an Edge Function with the user's token, and the function books for the user the token names.
find_slots can stay public, so someone who hasn't signed in yet can still see what's free. Services, prices, how to prepare and the cancellation policy in words go into knowledge entries, which your team can edit in the inbox when a price changes.
tools/ask_for_a_person.yml is how the agent asks for a person. Your endpoint alerts the front desk with the link, and their reply from the inbox takes the conversation over:
description: Tell the front desk that this conversation needs a person.when_to_use: > The patient asks for an exception to the cancellation policy, complains about a session or a charge, describes pain or symptoms and asks what to do, or asks to talk to a person.parameters: type: object properties: reason: { type: string, description: "Why it needs a person, in one line" } required: [reason]access: publicrequest: method: POST base_url: "$env.APP_API" path: /vatio/ask-for-a-person requires: [chat.url] body: reason: chat_url: "$chat.url" name: "$contact.name"respond: message: > The front desk was told. Tell the patient plainly that you are checking this with a colleague, who will answer here.The agent keeps answering until someone replies, and says it is checking with a colleague rather than inventing an errand.
#Try it before your users do
When vatio-init has pushed the agent, it gives you a link to the inbox, with Try the agent open, where you try the preview on WhatsApp, Instagram or a web chat. The web chat is a visitor who isn't signed in, so it only reaches public tools. To try what a user can do, ask your coding agent to put the preview's widget in a development version of your app, and sign in as a test user. Preview can call a staging backend, or a separate Supabase project, while live calls production, so test bookings stay out of your real schedule.
Then try:
- Book a slot, and check that it shows up in your app's own schedule.
- Cancel too late for your policy. The agent should explain the policy, cancel nothing and offer to pass the request on.
- Ask for a person. "I want to talk to someone" should call
ask_for_a_person, and the alert should reach your team with a link that opens the conversation. - Ask for someone else's booking, by name or email. It should find nothing.
Open the conversation in the inbox under Tests in Conversations: each reply shows the tool calls behind it, with their arguments and what your API answered. When the agent does what you want, tell your coding agent to publish. Nothing reaches your users until you do.
#After launch
Your team reads real conversations in the inbox. On a wrong reply, Report this reply asks what should have happened, for example: "She asked for Tuesday afternoon and it was full. Should have offered Wednesday afternoon instead of saying there was nothing this week." From then on, that note is a test.
Once a day, run /vatio-improve in your app's project. Your coding agent reads every open report, fixes the cause where it lives (the instructions, a knowledge entry, a tool or your API) and pushes the fix to preview. Vatio replays every reported case against preview, and you review which ones pass and approve the update. A case that passes on live and fails on preview blocks publication unless you explicitly override it. A daily email brings the command when new reports arrive.
The tests cover what was reported, not every conversation the agent could have. See Improve your AI agent.
#WhatsApp and Instagram
The same agent answers on your WhatsApp number and Instagram account, with the same tools and knowledge, on every plan (Pricing). A chat there has no page to carry a token, so the agent's private tools need sign_in_url, your app's login page. When a private tool needs a user, Vatio sends a Sign in button to that page; once they sign in, the conversation picks up and the agent runs the tool it was waiting on. See On WhatsApp and Instagram.
#Frequently asked questions
#Can an AI agent book appointments in my app?
Yes. With Vatio, your coding agent reads your app and gives the agent tools that call your app's own bookings API: one to find free slots, one to book, one to move and one to cancel. The agent runs in your app's chat widget, books for the signed-in user and only confirms what your API confirmed. Your app stays the place where a booking exists.
#Does Vatio have its own calendar or a calendar integration?
No. Vatio has no calendar of its own and no built-in calendar integration. Each tool is one HTTP request to your app's backend, so availability and bookings stay in your app. If your app keeps its schedule in a calendar service, your backend talks to that service and the agent talks to your backend.
#How does a booking agent know which user it is talking to?
Your backend signs a short-lived token saying who is signed in and passes it to the widget. Vatio verifies it and forwards it to your API on every call a private tool makes.
#Can someone book or cancel another user's appointment through the agent?
Not when the booking tools are private. A private Vatio tool cannot take a user id as a parameter, and the deploy fails if it tries. Your API reads the user from the verified token instead, so asking for someone else's appointment by name, email or id reaches nothing but the signed-in user's own.
#What happens when the booking agent gets something wrong after launch?
Someone on your team reports the reply in the Vatio inbox and writes what should have happened. You run /vatio-improve in your coding agent, which fixes the cause and pushes a preview, and Vatio replays every reported case against it. You review the results and approve the update. Nothing is published on its own.
#Related guides
- Connect your backend: Give the agent your API as tools, and let signed-in users reach their own data with a token your backend signs.
- Hand off to a person: When the agent passes a conversation to your team, what happens inside and outside office hours, and how it comes back.
- Your daily improvement routine: Report mistakes in the inbox, /vatio-improve has your coding agent fix them, and you approve the update.
- Support for signed-in users: Answer from your docs, check the user's own account, troubleshoot and open a ticket with what was tried.