Agent Guide
This guide is for AI agents (Claude Code, Cursor, Windsurf, Antigravity) wiring SpiderBook into a client. It walks through all 15 MCP tools in the order an agent uses them.
If you're new to SpiderBook, read Concept first.
Setup (one-time)
Add the SpiderIQ MCP server to your project. The booking tools ship in the kitchen-sink @spideriq/mcp package:
{
"mcpServers": {
"spideriq": {
"command": "npx",
"args": ["-y", "@spideriq/mcp"],
"env": {
"SPIDERIQ_FORMAT": "yaml",
"npm_config_registry": "https://npm.spideriq.ai"
}
}
}
}
Then bind your project once:
npx spideriq use <project_id>
This writes spideriq.json so every tool call auto-scopes to your client. See SpiderPublish § quick install for the full multi-package install (mail / leads / gate atomic packages).
The 15 booking tools
The MCP surface is grouped by IDAP resource. Every destructive tool defaults to dry_run=true and returns a confirm_token you must echo back to mutate.
Templates (3)
| Tool | Mutates? | Description |
|---|---|---|
booking_template_list({ category? }) | no | List global templates, optionally filtered by category. |
booking_template_get({ template_id }) | no | Get a single template, including its full flow + schema. |
booking_template_clone({ template_id, business_id, overrides? }) | yes | Clone a global template into the client's own booking_flows, applying overrides. |
Flows (5)
| Tool | Mutates? | Description |
|---|---|---|
booking_flow_create({ business_id, flow, schema, cal_event_type_id? }) | yes | Create a new flow from raw JSON (skipping templates). |
booking_flow_get({ flow_id }) | no | Get a flow by id. |
booking_flow_update({ flow_id, changes }) | yes | Patch a flow. Pass partial flow / schema / cal_event_type_id etc. |
booking_flow_publish({ flow_id }) | yes | Move a flow from draft → active. Required before customers can book. |
booking_flow_preview({ flow_id }) | no | Returns a preview URL for human review. |
Services (3)
| Tool | Mutates? | Description |
|---|---|---|
service_create({ business_id, name, duration_minutes, price_cents?, staff_ids? }) | yes | Add a service to the catalog. |
service_update({ service_id, changes }) | yes | Patch a service (price, duration, staff list, active flag). |
service_delete({ service_id }) | yes | Soft-delete (sets active=false). |
Bookings (4)
| Tool | Mutates? | Description |
|---|---|---|
booking_list({ business_id?, status?, since?, limit? }) | no | Cursor-paginated list of confirmed bookings. |
booking_get({ booking_id }) | no | Full booking row + answer snapshot. |
booking_reschedule({ booking_id, new_slot_start }) | yes | Move an existing booking to a new slot. |
booking_cancel({ booking_id, reason? }) | yes | Cancel a booking. Triggers Cal.com cancellation + customer email. |
dry_run, then confirm
Every mutating tool returns a preview when called without a confirm_token:
→ booking_flow_publish({ flow_id: "bf_abc" })
← {
"dry_run": true,
"preview": {
"flow_id": "bf_abc",
"current_status": "draft",
"next_status": "active",
"validation_errors": []
},
"confirm_token": "cft_01HZ…",
"expires_in_seconds": 300
}
Echo the token back to mutate:
→ booking_flow_publish({ flow_id: "bf_abc", confirm_token: "cft_01HZ…" })
← { "ok": true, "flow_id": "bf_abc", "status": "active", "published_at": "2026-04-18T10:12:33Z" }
The same convention applies to every flow / template / service / booking tool that mutates state. See SpiderPublish § dry_run for the same pattern in the content tools.
End-to-end example — Marina's Nail Studio
The user prompts:
Set up bookings for Marina's Nail Studio. 3 staff: Marina, Olena, Svitlana. Services: Gel Manicure 60min $45, Classic Pedicure 45min $35, Nail Art 30min $25. Open Tue–Sat 10am–7pm.
The agent runs:
1. Find a starting template
→ booking_template_list({ category: "nail_salon" })
← [{ template_id: "bt_…", name: "nail-salon-default", usage_count: 47, … }]
2. Clone it into Marina's business
→ booking_template_clone({
template_id: "bt_nail-salon-default",
business_id: "biz_marina-nails",
overrides: {
name: "Marina's Nail Studio — Booking",
cal_event_type_id: 42
}
})
← { dry_run: true, preview: { … }, confirm_token: "cft_01HZ…" }
3. Confirm the clone
→ booking_template_clone({ template_id: "bt_…", business_id: "biz_…",
overrides: { … }, confirm_token: "cft_01HZ…" })
← { ok: true, flow_id: "bf_marina-nail-studio" }
4. Add the 3 services (run with dry_run, then confirm — abbreviated below)
→ service_create({ business_id: "biz_…", name: "Gel Manicure",
duration_minutes: 60, price_cents: 4500,
staff_ids: [marina_id, olena_id, svitlana_id], confirm_token: "…" })
→ service_create({ … "Classic Pedicure", 45, 3500 … })
→ service_create({ … "Nail Art", 30, 2500 … })
5. Preview before publishing
→ booking_flow_preview({ flow_id: "bf_marina-nail-studio" })
← { preview_url: "https://book-preview.spideriq.ai/bf_…?token=…" }
6. Publish
→ booking_flow_publish({ flow_id: "bf_marina-nail-studio" })
← { dry_run: true, preview: { … }, confirm_token: "cft_…" }
→ booking_flow_publish({ flow_id: "bf_marina-nail-studio", confirm_token: "cft_…" })
← { ok: true, status: "active", published_at: "…" }
7. Embed in a page (uses SpiderPublish content tools)
→ content_create_page({
slug: "book",
template: "landing",
blocks: [{ type: "booking", flow_id: "bf_marina-nail-studio" }]
})
→ content_publish_page({ id: "…", confirm_token: "…" })
→ content_deploy_production({ confirm_token: "…" })
Total wall-clock time: ~2 minutes. Marina now has a live booking URL.
Customer self-service
Once booked, the customer gets a confirmation email with a signed manage-link:
https://book.spideriq.ai/manage/?token=<HMAC-signed-jwt>
That link maps to the public manage endpoints (no API key required — the token is the auth):
| HTTP | Endpoint | Purpose |
|---|---|---|
GET | /api/v1/booking/public/manage/{booking_id}?token=… | Show the booking, current slot, cancellation deadline. |
POST | /api/v1/booking/public/manage/{booking_id}/reschedule?token=… | Pick a new slot. Atomic via slot-hold (G2). |
POST | /api/v1/booking/public/manage/{booking_id}/cancel?token=… | Cancel within the cancellation window. |
The agent never has to wire this up — the manage link is generated automatically when a booking is confirmed. See app/api/v1/booking/public.py for the full surface.
Inspecting & operating bookings
Day-to-day operations (the kind a salesperson or owner does):
→ booking_list({ business_id: "biz_marina-nails", status: "confirmed", since: "2026-04-18", limit: 50 })
← [
{ booking_id: "bk_…", slot_start: "2026-04-19T14:00:00Z", contact: { name: "…", phone: "…" }, service: "Gel Manicure" },
…
]
→ booking_get({ booking_id: "bk_…" })
← { booking_id, flow_id, slot_start, slot_end, answers: { … }, flow_snapshot: { … }, status: "confirmed", … }
# Reschedule (always dry_run first)
→ booking_reschedule({ booking_id: "bk_…", new_slot_start: "2026-04-20T11:00:00Z" })
← { dry_run: true, preview: { current_slot: "…", new_slot: "…", cal_will_notify: true }, confirm_token: "cft_…" }
→ booking_reschedule({ booking_id: "bk_…", new_slot_start: "…", confirm_token: "cft_…" })
← { ok: true, status: "rescheduled" }
# Cancel
→ booking_cancel({ booking_id: "bk_…", reason: "Customer requested" })
← { dry_run: true, preview: { …, refund_required: false }, confirm_token: "cft_…" }
→ booking_cancel({ booking_id: "bk_…", reason: "…", confirm_token: "cft_…" })
← { ok: true, status: "cancelled" }
CLI equivalents
If you prefer the terminal, every MCP tool maps to a CLI subcommand:
spideriq booking templates list --category nail_salon
spideriq booking templates clone <template_id> --business <business_id>
spideriq booking flows list
spideriq booking flows publish <flow_id> # interactive — prompts y/N
spideriq booking flows publish <flow_id> --yolo # skip prompt
spideriq booking flows preview <flow_id> # opens browser
spideriq booking services list --business <business_id>
spideriq booking services add --business <business_id> --name "Gel Manicure" --duration 60 --price 4500
spideriq booking bookings list --business <business_id> --status confirmed --since 2026-04-01
spideriq booking bookings cancel <booking_id> --reason "Customer request"
--json produces machine-readable output (no chalk colors), --yolo skips interactive confirms, --confirm <token> re-uses an existing dry-run token.
Common mistakes
| Mistake | Why it bites you | Fix |
|---|---|---|
Calling booking_flow_publish without confirm_token and assuming it published | Returns dry_run=true, status stays draft. | Always echo the token back. |
Forgetting to add services before publishing a nail-salon-default clone | The service step renders an empty card grid. | service_create for every service before publish. |
Hand-editing a flow's cal_event_type_id after publish | Cal.com bookings are tied to the old event type. | Create a new event type in Cal.com first, then booking_flow_update. |
Using booking_template_list() without scoping to the client | Returns global seeds only. | That's intentional — for client-owned templates use the dashboard. |
Reusing a stale confirm_token (>5 min old) | Tokens expire after 300s. | Re-run the dry_run call to mint a fresh one. |
Where to go next
- Concept — the architecture in 200 words.
- Flow reference — every step type, every key.
- Schema reference — every field type, every option, and the seed templates.
- SpiderPublish AGENTS.md — how the booking block fits into pages and dynamic landings.
GET /api/v1/content/help?format=yaml— token-efficient agent reference (the same content as this site, machine-readable).