Skip to main content

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.json defines the steps (what happens, in what order)
  • schema.json defines 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 CaseWhat's Actually Needed
Nail salonService → staff → slot
RestaurantParty size → date → table
Dental clinicTreatment type → duration → provider
Sales callQualify → calendar slot
Fitness classClass 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)

TypeRendersUsed for
selectCard grid / avatar list / dropdown / radioService, staff, party size, options
calendarTime-slot pickerCal.com slots or IDAP availability
formTypeform-style sequential fieldsContact info, notes, qualifiers
confirmSummary + submitAlways 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

  1. POST cal-internal/api/v2/bookings — creates calendar event, returns cal_booking_uid
  2. INSERT INTO norm_cli_<client>.bookings — full booking row + flow_snapshot
  3. UPSERT INTO norm_cli_<client>.contacts — customer becomes a CRM contact (email or phone is the dedup key)
  4. INSERT INTO norm_cli_<client>.business_flags — writes contacted flag on the business
  5. SendGrid confirmation email + SMS (optional)
  6. SpiderPublish renders the confirm step

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.