In the previous article, we connected an agent to FavCRM and discovered its 165 typed tools.
Now we build something real: a booking flow that takes actual bookings — services, open slots, a confirmed booking, and a customer record that persists.
No mock arrays. Every call in this walkthrough writes a row.
The flow we are building
create_service → get_available_slots → create_booking → confirm_booking
↓
customer record persisted
The examples use the favcrm CLI because it is reproducible. An agent in Cursor or Claude makes the exact same MCP calls — it just decides the arguments itself.
Always inspect a tool before the first call:
favcrm tool describe create_service
The schema tells you the required fields. The examples below are minimal — your real arguments come from that schema.
Step 1 — Create a service
A service is the thing a customer books: a haircut, a class, a consultation.
favcrm tool call create_service '{
"name": "Intro Consultation",
"durationMinutes": 30,
"price": "0.00"
}'
The call returns a service id. That id is real and persisted — list it back to confirm:
favcrm tool call list_services '{}'
Step 2 — Check available slots
get_available_slots reads the service duration, staff and resource availability, and existing bookings, then returns the open times.
favcrm tool call get_available_slots '{
"serviceId": "<service-id>",
"date": "2026-06-01"
}'
This is the part you would otherwise build yourself — a calendar that knows about capacity and clashes. Here it is one call.
Step 3 — Create the booking
favcrm tool call create_booking '{
"serviceId": "<service-id>",
"start": "2026-06-01T14:30:00Z",
"customer": { "name": "Amy Chan", "email": "amy@example.com" }
}'
create_booking is a write tool. It checks the slot is still free, rejects double-bookings, and persists the row. If the slot was taken between Step 2 and Step 3, the call fails cleanly — the agent can re-read slots and retry.
Step 4 — Confirm it
A created booking is not necessarily a confirmed one. confirm_booking moves it into a confirmed state — the trigger for reminders and, if you want it, a payment link.
favcrm tool call confirm_booking '{ "bookingId": "<booking-id>" }'
Step 5 — The customer record built itself
You never called a "create customer" step. The customer passed to create_booking was upserted into the CRM automatically — a real crm_accounts record with contact details, booking history, and tags.
Verify it:
favcrm tool call list_bookings '{}'
favcrm tool call get_booking_detail '{ "bookingId": "<booking-id>" }'
The booking detail carries the linked customer. Next time the same email books, it attaches to the same record — history accumulates without a data model you had to design.
Optional — Take payment
If the service has a price, issue an invoice:
favcrm tool call create_invoice '{ "bookingId": "<booking-id>" }'
That returns a payable invoice backed by Stripe. Still one call; still a real record.
What you just did
You stood up a booking backend — services, availability, clash detection, confirmations, customer records, payments — without designing a schema, writing a migration, or reconciling a webhook.
The agent decided what to do. FavCRM was where it happened. Every step returned a persisted record, not a stub.
That is the practical meaning of a headless CRM: your AI builds the interface, and a real backend stands behind every call.
Try the flow on a free workspace: favcrm.io/campaigns/headless-crm
Top comments (0)