Skip to main content

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:

.mcp.json
{
"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)

ToolMutates?Description
booking_template_list({ category? })noList global templates, optionally filtered by category.
booking_template_get({ template_id })noGet a single template, including its full flow + schema.
booking_template_clone({ template_id, business_id, overrides? })yesClone a global template into the client's own booking_flows, applying overrides.

Flows (5)

ToolMutates?Description
booking_flow_create({ business_id, flow, schema, cal_event_type_id? })yesCreate a new flow from raw JSON (skipping templates).
booking_flow_get({ flow_id })noGet a flow by id.
booking_flow_update({ flow_id, changes })yesPatch a flow. Pass partial flow / schema / cal_event_type_id etc.
booking_flow_publish({ flow_id })yesMove a flow from draftactive. Required before customers can book.
booking_flow_preview({ flow_id })noReturns a preview URL for human review.

Services (3)

ToolMutates?Description
service_create({ business_id, name, duration_minutes, price_cents?, staff_ids? })yesAdd a service to the catalog.
service_update({ service_id, changes })yesPatch a service (price, duration, staff list, active flag).
service_delete({ service_id })yesSoft-delete (sets active=false).

Bookings (4)

ToolMutates?Description
booking_list({ business_id?, status?, since?, limit? })noCursor-paginated list of confirmed bookings.
booking_get({ booking_id })noFull booking row + answer snapshot.
booking_reschedule({ booking_id, new_slot_start })yesMove an existing booking to a new slot.
booking_cancel({ booking_id, reason? })yesCancel 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):

HTTPEndpointPurpose
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

MistakeWhy it bites youFix
Calling booking_flow_publish without confirm_token and assuming it publishedReturns dry_run=true, status stays draft.Always echo the token back.
Forgetting to add services before publishing a nail-salon-default cloneThe service step renders an empty card grid.service_create for every service before publish.
Hand-editing a flow's cal_event_type_id after publishCal.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 clientReturns 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