Documentation
Base URL https://use-flywheel.com/api/v1.
This page lists every endpoint that exists. Endpoints from the roadmap are not listed
here and are not in the machine-readable spec either.
Quickstart
Two calls from nothing to a lead being answered. The first needs no credentials at all.
Before you can actually send
An account needs its own number.
POST /api/v1/accounts/{id}/phone-number
buys one, optionally in an area_code
you name, falling back to nearby codes and then toll-free. It's idempotent — calling it
twice returns the number you already have rather than billing you for a second one — and
it answers 503
rather than a validation error when there's simply no inventory, so retrying is the right
response.
curl -sX POST https://use-flywheel.com/api/v1/accounts \
-H 'Content-Type: application/json' \
-d '{"account":{"name":"Northside Plumbing","time_zone":"America/Chicago","vertical":"plumbing","serves_minors":false}}'
curl -sX POST https://use-flywheel.com/api/v1/accounts/$ID/contacts \
-H "Authorization: Bearer $TOKEN" \
-H 'Content-Type: application/json' \
-d '{"contact":{"first_name":"Alex","phone":"+13125550142"},"sms_consented":true,"start_conversation":true}'
Conventions
- Authentication
-
Authorization: Bearer <token>on every request exceptGET /healthandPOST /accounts. The token from signup is shown exactly once and stored only as a digest — we cannot recover it for you. - Identifiers
- Accounts and contacts are addressed by an opaque URL-safe slug. A sequential integer would advertise how many customers exist and invite guessing, so it isn't exposed. Conversations, messages, templates, flows, steps and webhook endpoints use integer ids.
- Scope
-
The account is always resolved from the URL and then authorised against your key,
never the other way round — so naming somebody else's account cannot widen your own
scope. A mismatch is
403 forbidden, not404. - Pagination
-
page(from 1) andper_page(default 50, maximum 200) on list endpoints. Ordered newest first. - Errors
-
Always
{"error": {"code", "message", "details"}}. Branch oncode; treatmessageas human-facing and liable to change.unauthorizedforbiddennot_foundvalidation_failedbad_requestconflictsend_failed
Endpoints
Health
-
GET
/healthLiveness. Unauthenticated. Checks Flywheel's own database, not just the process.
Accounts
-
POST
/accountsSign up. Unauthenticated. Returns the account and its first key. -
GET
/accounts/{account_id}Fetch an account. -
PATCH
/accounts/{account_id}Update an account. -
POST
/accounts/{account_id}/pauseStop all automation for this account. -
POST
/accounts/{account_id}/resumeStart it again.
Message templates
-
GET
/accounts/{account_id}/message-templatesYour overrides plus every default you have not overridden. -
POST
/accounts/{account_id}/message-templatesOverride a template by key. -
PATCH
/accounts/{account_id}/message-templates/{id}Edit an override. -
DELETE
/accounts/{account_id}/message-templates/{id}Drop the override and fall back to the default.
Flows
-
GET
/accounts/{account_id}/flowsEffective flows, with their steps. -
POST
/accounts/{account_id}/flowsCreate a flow. -
PATCH
/accounts/{account_id}/flows/{id}Update a flow. -
DELETE
/accounts/{account_id}/flows/{id}Delete a flow. -
GET
/accounts/{account_id}/flows/{flow_id}/stepsList the steps. -
POST
/accounts/{account_id}/flows/{flow_id}/stepsAdd a step. -
PATCH
/accounts/{account_id}/flows/{flow_id}/steps/{id}Update a step. -
DELETE
/accounts/{account_id}/flows/{flow_id}/steps/{id}Delete a step.
Contacts
-
GET
/accounts/{account_id}/contactsFilter by pipeline_state, external_ref, or open=true. -
POST
/accounts/{account_id}/contactsAdd a lead. start_conversation=true begins the sub-60-second reply. -
GET
/accounts/{account_id}/contacts/{id}Fetch a contact by slug. -
PATCH
/accounts/{account_id}/contacts/{id}Update a contact.
Conversations
-
GET
/accounts/{account_id}/conversationsFilter with needs_human=true. -
GET
/accounts/{account_id}/conversations/{id}With the full message history. -
GET
/conversations/{conversation_id}/messagesMessages, oldest first. -
POST
/conversations/{conversation_id}/messagesTake over. Pauses automation for 24 hours. -
POST
/conversations/{id}/resumeEnd a takeover early.
Webhook endpoints
-
GET
/accounts/{account_id}/webhook-endpointsList registered endpoints. -
POST
/accounts/{account_id}/webhook-endpointsRegister one. https only. The secret is returned once. -
DELETE
/accounts/{account_id}/webhook-endpoints/{id}Remove one.
Building a flow
A flow is a list of steps. Each step sends a template, expects an answer of a given shape,
writes the captured value somewhere on the contact, and decides where to go next.
choices is an ordered array because texting “2” must select the second
option; a JSON object would not preserve that order and the wrong answer would be captured.
{
"step": {
"position": 1,
"template_key": "intake.problem",
"expects": "choice",
"writes_to": "custom_fields.issue",
"choices": [
{ "value": "leak", "synonyms": ["leaking", "slab leak", "burst", "water everywhere"] },
{ "value": "clog", "synonyms": ["clogged", "backed up", "won't drain"] },
{ "value": "install", "synonyms": ["installation", "new water heater", "replace"] }
],
"branch_on": { "leak": 2, "clog": 3, "install": 3 },
"default_next_position": 4
}
}
Webhooks
Not yet delivering
You can register endpoints today and they are stored, validated and given a signing secret, but Flywheel is not dispatching events to them yet. Register now if you like — they'll start receiving when delivery ships — but don't build a dependency on receiving anything this week.
When it lands, deliveries are signed HMAC-SHA256 with the secret returned once at registration, and endpoints are disabled automatically after 20 consecutive failures so an outage on your side doesn't build a backlog we later flush at you all at once.
Flywheel pushes outward and never calls back into a subscriber synchronously. Putting someone else's uptime on the critical path of a sub-60-second reply is the one thing that architecture is designed to prevent.
Reading this as an agent?
Skip the HTML. /llms.txt is a plain-text brief covering what the product does, what it guarantees, and what it does not do yet. /openapi.json is the full machine contract. Signup needs no credentials, so you can go from reading to a working account without a human involved.