fonio.ai — dynamic integration
The dynamic integration looks up every RocketLead ID at runtime — no hardcoded locationMap, no manual maintenance when calendars or appointment types change. The trade-off is more API calls per booking and a slightly more complex flow editor in fonio.
For most deployments, prefer the static integration. Use this dynamic pattern when:
- You manage multiple RocketLead organizations from one fonio agent.
- Calendars, appointment types, or resources change weekly and manual map maintenance is annoying.
- You want the agent to discover new locations automatically as they’re added in the RocketLead console.
Architecture
Section titled “Architecture”[Caller] → [fonio.ai voice agent] ├─ list calendars ├─ pick calendar by name match ├─ list appointment types for that calendar ├─ get availability ├─ list tables → pick the destination Lead Pool ├─ create lead └─ create bookingSix fonio actions wired together. Each one’s output feeds the next.
Prerequisites
Section titled “Prerequisites”- A fonio.ai account with a configured voice agent.
- A RocketLead Connect API token with
writescope. Generate one in Settings → API Tokens. - The base URL:
https://api.rocketlead.io. Authorization: Bearer rocketlead_live_<secret>on every action.
Step 1 — List calendars
Section titled “Step 1 — List calendars”Action listCalendars:
- Method:
GET - URL:
https://api.rocketlead.io/connect/calendar/
Response: array of Calendar objects (id, name, tzdata, etc. — see Lookups → Calendars).
In the agent’s prompt, match the caller’s stated location to a calendar by name:
“Which location? I see Bernau and Ahrensfelde.” → caller picks “Bernau” → variable
calendarId= the calendar whosename === "Bernau".
Step 2 — List appointment types for that calendar
Section titled “Step 2 — List appointment types for that calendar”Action listAppointmentTypes:
- Method:
GET - URL:
https://api.rocketlead.io/connect/appointment_types/
Response: array of AppointmentType objects with id, name, bookingLength, searchLength.
Filter to types relevant to the chosen calendar by checking each appointment type’s resource mappings — but in practice the agent often offers a single “trial” appointment type, so a hardcoded match by name === "Trial Session" is fine and simpler.
If you need to be precise, use /connect/appointment_type_resource_mappings/?calendarId={calendarId} (via Lookups) to get only the appointment types actually bookable on that calendar.
Step 3 — Get availability
Section titled “Step 3 — Get availability”Action checkAvailability:
- Method:
GET - URL:
https://api.rocketlead.io/connect/availability/?calendarId={calendarId}&appointmentTypeId={appointmentTypeId}&start={start}&end={end}
Inputs:
calendarIdfrom step 1.appointmentTypeIdfrom step 2.start/endderived from the caller’s window.
Response: see Availability. Filter data[] by available: true and pick a slot. Each entry carries resourceId, resourceSlotId, startDateTime, endDateTime — you’ll pass all four into the booking call.
Step 4 — Find the destination Lead Pool and default lead state
Section titled “Step 4 — Find the destination Lead Pool and default lead state”Action getLeadPool:
- Method:
GET - URL:
https://api.rocketlead.io/connect/tables/{tableId}
You can either hardcode the tableId (the destination Lead Pool rarely changes) or run an extra GET /connect/tables/ first to pick by name.
Response: Table with leadStates[]. Pick the one with default: true — that’s the initial pipeline state for new entries.
// Variables to extract:// leadStateId = response.leadStates.find(s => s.default).idStep 5 — Create the lead
Section titled “Step 5 — Create the lead”Action createLead:
- Method:
POST - URL:
https://api.rocketlead.io/connect/table_entries/ - Headers:
Authorization: Bearer {RL_TOKEN}Content-Type: application/json
- Body:
{"tableId": "{tableId}","leadStateId": "{leadStateId}","data": {"firstName": "{firstName}","lastName": "{lastName}","phone": "{phone}","email": "{email}"}}
Response: the created TableEntry. Save id as leadId for step 6.
Step 6 — Create the booking
Section titled “Step 6 — Create the booking”Action bookAppointment:
- Method:
POST - URL:
https://api.rocketlead.io/connect/calendar_bookings/ - Body:
{"calendarId": "{calendarId}","appointmentTypeId": "{appointmentTypeId}","resourceId": "{resourceId}","resourceSlotId": "{resourceSlotId}","startDateTime": "{startDateTime}","endDateTime": "{endDateTime}","tableEntryId": "{leadId}","name": "{firstName} {lastName}"}
resourceId and resourceSlotId come from the slot picked in step 3. tableEntryId is the lead from step 5.
What happens after the call
Section titled “What happens after the call”Identical to the static pattern — the same automation events fire regardless of which integration shape you use:
| Event | When |
|---|---|
table.entry.added | Lead created (step 5) |
calendar.appointment.booked | Booking created (step 6) |
calendar.appointment.hasStarted | At appointment start |
calendar.appointment.hasEnded | At appointment end |
See Automation triggers.
Optimization: cache between calls
Section titled “Optimization: cache between calls”Steps 1, 2, and 4 return data that rarely changes within a single shift. Cache them at the agent-session level (or even agent-startup) instead of re-fetching every call:
- Once per agent startup: calendars, appointment types, lead pool + default state.
- Per call: availability, lead create, booking create — three calls instead of six.
This brings the dynamic pattern’s per-call latency close to the static pattern’s, without the manual map maintenance.
Trade-offs
Section titled “Trade-offs”| Static | Dynamic (this guide) | |
|---|---|---|
| API calls per booking | 3 | 6 (or 3 with caching) |
| Latency | Lowest | Higher (cascading lookups) |
| Setup complexity | One variable map | Six actions, more flow logic |
| Maintenance | Manual when IDs change | Automatic |
| Best for | Single-org, stable schedules | Multi-org, frequently-changing schedules |
Troubleshooting
Section titled “Troubleshooting”| Symptom | Cause |
|---|---|
| Agent can’t find the calendar | The caller’s location string doesn’t match any calendar name exactly — train fonio’s intent extraction to handle synonyms or fuzzy matches. |
403 Forbidden on POST | Token has read only — issue a write token. |
400 The selected time slot is no longer available | Race — retry with fresh availability. |
400 No default lead state | The Lead Pool has no default: true lead state. Set one in the console. |
Related
Section titled “Related”- fonio.ai — static integration (recommended) — the simpler pattern for most deployments.
- Connect API → Workflows → Bootstrap an integration — same lookup pattern in plain JS.
- Availability resource.
- Bookings resource.
- Lookups resource — full reference for the listing endpoints used in steps 1, 2, 4.