Public API
Sidebar: Settings → API Documentation
The public API lets your own systems create leads, drive your CRM and read or write bookings. Everything it does is scoped to one organization.
The full, always-current reference — every endpoint, every field, an example payload, the status codes and a request console that sends real calls with one of your keys — lives inside the dashboard. This page explains how it fits together.
Authentication and scope
Requests carry an API key from Settings → API Keys. The organization is read from the key itself: a key can never read or change another organization’s data, and there is no organization parameter to get wrong.
Keys are shown once. FlyB stores an encrypted fingerprint and cannot recover a key — only replace it. Revoking a key stops every integration using it immediately.
What the API covers
Discovery
List funnels and stages returns every CRM funnel with its stages in board order. Start here to collect the identifiers the other endpoints expect.
Contacts
| Endpoint | Notes |
|---|---|
| Retrieve a contact | By ID |
| Create a contact | A matching phone or e-mail merges into the existing record instead of duplicating it, and the response reports merged: true along with any field conflicts. Send stage_id to place the contact in the CRM in the same call |
| Update a contact | Only the fields you send; anything omitted keeps its stored value |
| Delete a contact | Permanent, along with its CRM positions and history |
A contact also carries its lead owner and its lead score, both read-only: the owner is decided by your capture rules and CRM automations (and only an admin may hand a lead over), and the score is awarded by your scoring rules, which record why alongside every point. They are published because “who owns this lead and how hot is it” is exactly what an agent deciding who to contact next is asking — but a number an integration could simply write would make the history a fiction.
CRM
Add or move to a stage places a contact in a stage — moving it if it is already in that funnel, keeping its history, or entering the funnel otherwise. Stage automations (auto-tags, auto-qualify) run as usual.
Qualify a contact, Mark as won and Mark as lost close or flag the position. Marking as lost accepts one of the predefined loss-reason codes, or your own short text — the predefined codes group better in reports.
Calendars and bookings
| Endpoint | Notes |
|---|---|
| List calendars | Every calendar with its custom questions, weekly availability rules, vacation periods, minimum notice (min_notice_hours) and public link |
| List days with availability | Days with at least one free time. Booked times, times held for a guest whose payment is clearing, Google Calendar conflicts, vacations and the calendar’s minimum notice are already excluded |
| List times of a day | Each with the slot_id the booking endpoint expects back |
| List days and times | Both of the above in one round trip — meant for agents that want the whole picture at once |
| Book an appointment | Name the time with slot_id or starts_at; name the guest with contact_id or with a contact object. send_confirmation_email: false books silently — no confirmation e-mail, and no WhatsApp confirmation either when the calendar sends one. whatsapp_opt_in records the guest’s consent to WhatsApp messages: false sends them nothing on WhatsApp, true records that they agreed; omitted, the calendar’s WhatsApp messages go out as they always did |
| List bookings | Filter by guest, calendar and date range |
| Retrieve a booking | With the Meet link and the guest’s self-service reschedule URL |
| Cancel a booking | By ID, or found from the guest’s e-mail or phone |
Two behaviours are worth knowing:
show_allbypasses the calendar’s slot presentation rule and returns every free time. It defaults to false, so by default the API offers exactly what the public page offers. Booking itself never applies the rule — a hidden slot is a real opening.- Time zones. Every response carries a
timezone, andstarts_at/ends_atare absolute instants. Send atimezoneparameter to read and render dates on another clock; omit it to speak the calendar’s own. - Paid calendars. On a calendar that charges for bookings, a booking made through the API is confirmed without the online payment — the integration is your own. Charge the guest your own way, or send them the booking page instead.
Phone numbers
phone is accepted with or without the dial code. These two bodies create the same
contact, and both come back with country_code filled:
{ "name": "Ana", "phone": "5511987654321" }
{ "name": "Ana", "phone": "11987654321" }A number sent without a dial code is read against your organization’s country
(Settings → Organization). A number that already carries one is
recognised and not given a second copy — which is what lets you point an integration at
the API without normalising its output first. Symbols and spaces are ignored, a leading
+ is honoured, and a number belonging to another country is recognised from its own
dial code.
Send country_code when you know it — a number that could be read either way is then
read the way you meant. It is never required.
The same reading is applied to the guest’s phone when booking, and to the
booking page’s ?phone=, so a
number reaches the same contact however it arrived.
Errors
Errors name the field that failed, because the callers of this API are often AI agents and a vague message costs a round trip:
querystring.days: Expected number, received nanTwo conflict responses carry extra information rather than just failing:
- 409 on booking — the time is gone; the response includes that day’s remaining free times.
- 409 on cancelling — more than one upcoming booking matched; the response includes
the candidates, so you can send a
booking_id, narrow the search, or cancel them all.
Other statuses: 400 validation, 401 missing or invalid key, 403 plan contact limit reached (creating a contact directly — booking never returns it, the guest’s contact is always created), 404 not found in this organization, 409 no open position in the funnel, 500 retryable.
The request console
Each endpoint page has a Try it panel that sends a real request using a key you pick. The request runs on FlyB’s servers, so the key’s secret never has to be typed into the page — and it hits the real endpoint, scoped to your organization. Records really are created, changed or deleted. Destructive calls require an explicit confirmation first.
Every page also gives you a ready-to-paste cURL.
Fair use
The API is provided for integrating your own systems. Automated harvesting, load that threatens platform stability, and use as a way around plan limits are prohibited — see the Acceptable Use Policy. FlyB may apply rate limits.