Skip to content

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.
[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 booking

Six fonio actions wired together. Each one’s output feeds the next.

  • A fonio.ai account with a configured voice agent.
  • A RocketLead Connect API token with write scope. Generate one in Settings → API Tokens.
  • The base URL: https://api.rocketlead.io.
  • Authorization: Bearer rocketlead_live_<secret> on every action.

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 whose name === "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.

Action checkAvailability:

  • Method: GET
  • URL:
    https://api.rocketlead.io/connect/availability/?calendarId={calendarId}&appointmentTypeId={appointmentTypeId}&start={start}&end={end}

Inputs:

  • calendarId from step 1.
  • appointmentTypeId from step 2.
  • start / end derived 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).id

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.

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.

Identical to the static pattern — the same automation events fire regardless of which integration shape you use:

EventWhen
table.entry.addedLead created (step 5)
calendar.appointment.bookedBooking created (step 6)
calendar.appointment.hasStartedAt appointment start
calendar.appointment.hasEndedAt appointment end

See Automation triggers.

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.

StaticDynamic (this guide)
API calls per booking36 (or 3 with caching)
LatencyLowestHigher (cascading lookups)
Setup complexityOne variable mapSix actions, more flow logic
MaintenanceManual when IDs changeAutomatic
Best forSingle-org, stable schedulesMulti-org, frequently-changing schedules
SymptomCause
Agent can’t find the calendarThe 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 POSTToken has read only — issue a write token.
400 The selected time slot is no longer availableRace — retry with fresh availability.
400 No default lead stateThe Lead Pool has no default: true lead state. Set one in the console.