SpiderBook
Universal booking engine for SpiderIQ. Any business category, any flow, zero custom code. Configured entirely through IDAP. Rendered by SpiderPublish. Built by AI agents via MCP.
What SpiderBook Is
SpiderBook is not a booking tool. It is a JSON-driven booking flow engine where:
flow.jsondefines the steps (what happens, in what order)schema.jsondefines the fields (what the customer fills at each step)
Two JSONs configure any booking type for any business. The LLM writes the JSONs via MCP. The client never touches a UI builder.
IDAP stores the flow + schema
↓
SpiderPublish renders the booking block
↓
Customer completes the flow
↓
Booking written back to IDAP
↓
Customer auto-created as IDAP contact
Why This Exists
Cal.com solves one thing: calendar-based appointment scheduling. It does not handle:
| Use Case | What's Actually Needed |
|---|---|
| Nail salon | Service → staff → slot |
| Restaurant | Party size → date → table |
| Dental clinic | Treatment type → duration → provider |
| Sales call | Qualify → calendar slot |
| Fitness class | Class type → instructor → slot |
A single configurable engine covers all of these. Cal.com is not replaced — it is reduced to its correct role: calendar sync adapter.
The Three-Layer Architecture
IDAP owns data, flows, schemas, templates, bookings, contacts
Cal.com owns calendar sync, slot availability, event creation
SpiderPublish owns rendering, booking block, dynamic landing pages
MCP owns the agent configuration interface
IDAP Resources
SpiderBook adds four resource types to IDAP. Each is per-client (lives in norm_cli_<client>.* schemas):
idap://booking_flows/{id} ← the two-JSON engine config, scoped to a business
idap://booking_templates/{id} ← reusable flows, scoped to account level
idap://services/{id} ← service catalog (e.g. "Gel Manicure 60min $45")
idap://bookings/{id} ← confirmed bookings + full answer snapshot
booking_templates_global is a single shared table — agents clone from it into their own client's booking_templates.
Cal.com — Calendar Sync Only
Cal.com (github.com/calcom/cal.com, MIT license, pinned to v6.2.0) is self-hosted on a dedicated VPS via Docker Compose. It is never exposed to end customers directly.
What Cal.com handles:
- Google Calendar two-way sync
- Office 365 / Outlook two-way sync
- Zoom / Daily.co video link generation
- Availability rules (working hours, buffer times, date ranges)
- Event-type durations
What Cal.com does NOT handle:
- The booking UI (SpiderPublish owns this)
- Service catalogs (IDAP owns this)
- Staff assignment (IDAP owns this)
- Table/resource availability (IDAP owns this)
- Customer data (IDAP owns this)
Cal.com exposes its availability engine via its v2 REST API. SpiderBook queries Cal.com for available slots and writes confirmed bookings back. Cal.com is a slot resolver, nothing more. All Cal.com calls are proxied through the SpiderIQ FastAPI gateway — Cal.com is internal infrastructure, not a public endpoint.
SpiderPublish — The Booking Block
SpiderPublish exposes a booking block type:
{
"type": "booking",
"flow_id": "bf_xxx",
"prefill": {
"business_id": "{{ business.idap_id }}",
"salesperson": "{{ salesperson.name }}"
},
"theme": {
"primary_color": "var(--primary)",
"button_label": "Book Now"
}
}
The block resolves flow_id from IDAP, loads the flow + schema, and renders the step sequence as a Tier 4 React component (Shadow-DOM-isolated). It can also be embedded in dynamic landing pages:
---
template: dynamic_landing
---
{% assign biz = business %}
<h1>Hi {{ biz.name }}, ready to fill your calendar?</h1>
<p>Based in {{ biz.city }} · {{ biz.reviews_count }} Google reviews</p>
{% booking flow_id: biz.booking_flow_id %}
See SpiderPublish overview for how blocks fit into pages.
The Two-JSON Engine
Step types (4 total — covers every category)
| Type | Renders | Used for |
|---|---|---|
select | Card grid / avatar list / dropdown / radio | Service, staff, party size, options |
calendar | Time-slot picker | Cal.com slots or IDAP availability |
form | Typeform-style sequential fields | Contact info, notes, qualifiers |
confirm | Summary + submit | Always the final step |
Full type reference: Flow reference.
Field types (11 total)
text, email, phone, tel, textarea, select, checkbox, consent, number, date, time.
Full type reference: Schema reference.
Vertical examples
Nail salon (5 steps) — service → staff → slot → contact → confirm
{
"steps": [
{ "type": "select", "id": "service" },
{ "type": "select", "id": "staff" },
{ "type": "calendar", "id": "slot" },
{ "type": "form", "id": "contact" },
{ "type": "confirm", "id": "summary" }
]
}
Restaurant (4 steps) — party → slot → contact → confirm
{
"steps": [
{ "type": "form", "id": "party" },
{ "type": "calendar", "id": "slot" },
{ "type": "form", "id": "contact" },
{ "type": "confirm", "id": "summary" }
]
}
Sales call (3 steps) — qualify → slot → confirm
{
"steps": [
{ "type": "form", "id": "qualify" },
{ "type": "calendar", "id": "slot" },
{ "type": "confirm", "id": "summary" }
]
}
The flow.json is the spine; the schema.json is the meat. Each step id in flow.steps[] MUST have a matching key in schema (validated server-side by the BookingFlow Pydantic model). See Flow reference and Schema reference.
Slot Resolution
When the runtime reaches a calendar step, it picks the resolver from schema[step].source:
if schema.source == "cal.com":
GET cal-internal/api/v2/slots/available
?eventTypeId={cal_event_type_id}
&startTime={window_start}
&endTime={window_end}
&duration={service.duration_minutes} ← from previous step's answer
&staffCalendarId={staff.cal_user_id} ← from previous step's answer
if schema.source == "idap://availability":
GET /api/v1/idap/availability
?business_id={business_id}
&party_size={party.size} ← from previous step's answer
&date_range={window}
The runtime also writes a short-lived slot hold (15 min default) to prevent two customers grabbing the same slot during checkout (G2 — see Customer self-service flow).
On Confirmed Submission
POST cal-internal/api/v2/bookings— creates calendar event, returnscal_booking_uidINSERT INTO norm_cli_<client>.bookings— full booking row +flow_snapshotUPSERT INTO norm_cli_<client>.contacts— customer becomes a CRM contact (email or phone is the dedup key)INSERT INTO norm_cli_<client>.business_flags— writescontactedflag on the business- SendGrid confirmation email + SMS (optional)
- SpiderPublish renders the
confirmstep
The customer is now an IDAP contact. They are enrichable, verifiable, and reachable through every SpiderIQ worker.
Template Library Compounding
Every flow built for a client can be promoted to a global template. After enough clients:
business.category = "hair_salon"
→ Agent: "Found 3 templates for hair salons.
Using 'hair-salon-standard' (used 23 times).
Customizing for {{ business.name }}..."
Templates improve with usage. The platform learns. New clients onboard faster.
The official seed library ships 3 templates on day 1 — see Schema reference § Seed templates — and grows automatically as agents publish new ones.
Where to Go Next
- Flow reference — every step type, every key
- Schema reference — every field type, every option, every seed template
- Agent guide — end-to-end MCP session: from "set up bookings for Marina's Nail Studio" to a live URL in under two minutes
For the broader site-builder context, see SpiderPublish.