Base address: https://linesteward.com. Every request needs an API key (authentication). The OpenAPI 3.1 file: /v1/openapi.json.
Businesses, facts and documents
GET/v1/businesses
List businesses. Every business in the key's account, oldest first.
Scope: businesses:read
Answer: 200, BusinessList
Each item in data: Business.
POST/v1/businesses
Add a business. Add a business (a location with its own receptionist line) to the key's account. It shares the account's plan and minutes, so it needs a plan with room for another location (Enterprise). Sign-ups for a new account happen in the app.
Scope: businesses:write
Parameters
| Name | In | Type | About |
|---|---|---|---|
Idempotency-Key | header | string | Any unique string (a UUID). A retry with the same key and body gets the first answer for 24 hours instead of doing it twice. |
Body (JSON)
| Field | Type | About |
|---|---|---|
name required | string | |
business_type required | one of salon, restaurant, garage, clinic, trades, estate_agent, other | |
website | string (or null) | |
timezone | string |
json{
"business_type": "salon",
"name": "Kettlewell Hair, Ilkley",
"website": "https://kettlewellhair.example"
}Answer: 201, Business
GET/v1/businesses/{business_id}
Get a business. One business: details, opening hours, the owner's texts, and the receptionist's name and greeting.
Scope: businesses:read
Parameters
| Name | In | Type | About |
|---|---|---|---|
business_id required | path | string |
Answer: 200, Business
PATCH/v1/businesses/{business_id}
Update a business. Change only the fields you send. The receptionist uses the change from its next call.
Scope: businesses:write
Parameters
| Name | In | Type | About |
|---|---|---|---|
business_id required | path | string |
Body (JSON)
| Field | Type | About |
|---|---|---|
name | string (or null) | |
timezone | string (or null) | |
address | string (or null) | |
phone | string (or null) | |
website | string (or null) | |
hours | object (or null) | Local opening times per day (mon to sun); a missing or empty day is closed. |
closures | list of string (or null) | Local dates closed all day, YYYY-MM-DD. |
about | string (or null) | About your business, in your words. |
receptionist_role | string (or null) | What the receptionist should do for callers. |
greeting | string (or null) | The receptionist's first words. It always says it is an AI first; this follows. |
agent_name | string (or null) |
json{
"greeting": "Hello, you're through to Kettlewell Hair. How can I help?",
"hours": {
"mon": [
[
"09:00",
"17:30"
]
],
"sat": [
[
"10:00",
"14:00"
]
],
"tue": [
[
"09:00",
"17:30"
]
]
}
}Answer: 200, Business
PUT/v1/businesses/{business_id}/knowledge
Replace the facts. Replace one set of facts (services, prices, answers) the receptionist uses on calls. manual is the set typed in the app; website the set read from the website. The other set stays.
Scope: knowledge:write
Parameters
| Name | In | Type | About |
|---|---|---|---|
business_id required | path | string |
Body (JSON)
| Field | Type | About |
|---|---|---|
facts required | list of Fact | |
source | one of manual, website | Which set of facts to replace: manual (the ones typed in the app) or website (the ones read from the website). The other set is kept. |
json{
"facts": [
{
"duration_minutes": 60,
"kind": "service",
"price": 48,
"title": "Women's cut and finish"
},
{
"body": "Yes, free parking behind the salon.",
"kind": "faq",
"title": "Is there parking?"
}
]
}Answer: 200, Saved
POST/v1/businesses/{business_id}/documents
Upload a document. A PDF, Word (.docx), text, Markdown or CSV file the receptionist answers callers from. Send the file as the body and its name as ?name=. It is read in the background (status processing, then ready or failed; see it in the app). Only its text is kept; the file is deleted once read. The same limits as the app apply.
Scope: knowledge:write
Parameters
| Name | In | Type | About |
|---|---|---|---|
business_id required | path | string | |
name | query | string | The file's name, like price-list.pdf. |
Idempotency-Key | header | string | Any unique string (a UUID). A retry with the same key and body gets the first answer for 24 hours instead of doing it twice. |
Body: The file itself, as the body (not a form).
Answer: 202, Document
Calls
GET/v1/calls
List calls. Calls, newest first: time, length, outcome, summary and the caller's number (callers' personal data). The transcript is on GET /v1/calls/{call_id}.
Scope: calls:read
Parameters
| Name | In | Type | About |
|---|---|---|---|
business_id | query | string (or null) | Only this business (its id). |
since | query | string (or null) | Only from this time on (ISO 8601). |
until | query | string (or null) | Only before this time (ISO 8601). |
outcome | query | one of booked, changed, cancelled, message, info, transferred, abandoned, failed (or null) | |
cursor | query | string (or null) | next_cursor from the page before. |
limit | query | integer | Up to 100; 25 if not given. |
Answer: 200, CallPage
Each item in data: Call.
GET/v1/calls/{call_id}
Get a call. One call. Add ?include=transcript for what was said: it holds callers' personal data, so ask for it only when you need it and keep it no longer than your own retention time.
Scope: calls:read
Parameters
| Name | In | Type | About |
|---|---|---|---|
call_id required | path | string | |
include | query | transcript (or null) |
Answer: 200, CallDetail
Messages
GET/v1/messages
List messages. Messages the receptionist took, newest first.
Scope: messages:read
Parameters
| Name | In | Type | About |
|---|---|---|---|
business_id | query | string (or null) | Only this business (its id). |
status | query | one of open, done, all | |
urgent | query | boolean (or null) | Only urgent ones. |
since | query | string (or null) | Only from this time on (ISO 8601). |
until | query | string (or null) | Only before this time (ISO 8601). |
cursor | query | string (or null) | next_cursor from the page before. |
limit | query | integer | Up to 100; 25 if not given. |
Answer: 200, MessagePage
Each item in data: Message.
Bookings
GET/v1/bookings
List bookings. Bookings, newest made first. Filter by when they start with starts_after / starts_before.
Scope: bookings:read
Parameters
| Name | In | Type | About |
|---|---|---|---|
business_id | query | string (or null) | Only this business (its id). |
status | query | one of confirmed, cancelled, completed, no_show (or null) | |
starts_after | query | string (or null) | |
starts_before | query | string (or null) | |
cursor | query | string (or null) | next_cursor from the page before. |
limit | query | integer | Up to 100; 25 if not given. |
Answer: 200, BookingPage
Each item in data: Booking.
POST/v1/bookings
Make a booking. Book a time with the same rules the receptionist uses on calls: the skill must be on, the time must be open, on the booking grid, after the notice and within the days ahead, the service must be one the business lists, and there must be room. No text is sent to the customer. Not for businesses that book into a connected booking system, or trades job visits.
Scope: bookings:write
Parameters
| Name | In | Type | About |
|---|---|---|---|
Idempotency-Key | header | string | Any unique string (a UUID). A retry with the same key and body gets the first answer for 24 hours instead of doing it twice. |
Body (JSON)
| Field | Type | About |
|---|---|---|
business_id required | string | |
kind | one of appointment, table | |
starts_at required | string (date-time) | With its UTC offset, like 2026-11-04T10:30:00+00:00. It must be a free time the receptionist would offer: open, on the booking grid, after the notice. |
service | string (or null) | A bookable service's name, when the business lists services (appointments). |
party_size | integer (or null) | Tables: how many people. |
customer_name required | string | |
customer_phone | string (or null) | |
notes | string (or null) |
json{
"business_id": "3f0c6a8e-1b2d-4c5e-8f90-a1b2c3d4e5f6",
"customer_name": "Sam Patel",
"customer_phone": "+447700900123",
"kind": "appointment",
"service": "Women's cut and finish",
"starts_at": "2026-11-04T10:30:00+00:00"
}Answer: 201, Booking
GET/v1/bookings/{booking_id}
Get a booking. One booking.
Scope: bookings:read
Parameters
| Name | In | Type | About |
|---|---|---|---|
booking_id required | path | string |
Answer: 200, Booking
DELETE/v1/bookings/{booking_id}
Cancel a booking. Cancel a confirmed booking that hasn't started, with at least the business's cancellation notice. No text is sent to the customer. Cancelling twice is fine.
Scope: bookings:write
Parameters
| Name | In | Type | About |
|---|---|---|---|
booking_id required | path | string |
Answer: 200, Booking
Webhooks
GET/v1/webhooks
List webhooks. Each business has at most one webhook address: the same one Settings, "Send to other apps" sets in the app. The signing secret is never shown again.
Scope: webhooks:write
Answer: 200, WebhookList
Each item in data: Webhook.
POST/v1/webhooks
Set a webhook. Set the business's webhook address and events. It replaces the one set before (in the app too), and makes a new signing secret, shown once in the answer.
Scope: webhooks:write
Parameters
| Name | In | Type | About |
|---|---|---|---|
Idempotency-Key | header | string | Any unique string (a UUID). A retry with the same key and body gets the first answer for 24 hours instead of doing it twice. |
Body (JSON)
| Field | Type | About |
|---|---|---|
business_id required | string | |
url required | string | |
events | list of one of call.completed, message.created, booking.created, booking.cancelled (or null) | Default: all four events. |
json{
"business_id": "3f0c6a8e-1b2d-4c5e-8f90-a1b2c3d4e5f6",
"events": [
"call.completed",
"booking.created"
],
"url": "https://example.com/hooks/receptionist"
}Answer: 201, WebhookCreated
DELETE/v1/webhooks/{webhook_id}
Delete a webhook. Stop sending events to it. Events not sent yet are dropped.
Scope: webhooks:write
Parameters
| Name | In | Type | About |
|---|---|---|---|
webhook_id required | path | string |
Answer: 200, Deleted
Objects
Business
| Field | Type | About |
|---|---|---|
id required | string | |
name required | string | |
business_type required | string | |
timezone required | string | |
address | string (or null) | |
phone | string (or null) | |
website | string (or null) | |
hours | object | |
closures | list of any | |
about | string (or null) | |
receptionist_role | string (or null) | |
greeting | string (or null) | |
agent_name | string (or null) | |
created_at | string (date-time) (or null) |
Call
| Field | Type | About |
|---|---|---|
id required | string | |
business_id required | string | |
started_at required | string (date-time) | |
ended_at | string (date-time) (or null) | |
seconds | integer | |
outcome | string (or null) | |
summary | string (or null) | |
caller_phone | string (or null) | |
language | string (or null) |
CallDetail
| Field | Type | About |
|---|---|---|
id required | string | |
business_id required | string | |
started_at required | string (date-time) | |
ended_at | string (date-time) (or null) | |
seconds | integer | |
outcome | string (or null) | |
summary | string (or null) | |
caller_phone | string (or null) | |
language | string (or null) | |
transcript | list of any (or null) | Only with ?include=transcript. Callers' personal data. |
Message
| Field | Type | About |
|---|---|---|
id required | string | |
business_id required | string | |
call_id | string (or null) | |
kind required | string | |
caller_name required | string | |
caller_phone | string (or null) | |
body required | string | |
urgent | boolean | |
callback_time | string (or null) | |
address | string (or null) | |
status required | string | |
created_at required | string (date-time) |
Booking
| Field | Type | About |
|---|---|---|
id required | string | |
business_id required | string | |
call_id | string (or null) | |
kind required | string | |
ref required | string | |
status required | string | |
service | string (or null) | |
starts_at required | string (date-time) | |
ends_at required | string (date-time) | |
party_size | integer | |
customer_name required | string | |
customer_phone | string (or null) | |
notes | string (or null) | |
source required | string | |
created_at | string (date-time) (or null) | |
cancelled_at | string (date-time) (or null) |
Webhook
| Field | Type | About |
|---|---|---|
id required | string | |
business_id required | string | |
url required | string | |
events required | list of string | |
enabled | boolean | |
last_status | integer (or null) | |
last_error | string (or null) | |
created_at | string (date-time) (or null) |
WebhookCreated
| Field | Type | About |
|---|---|---|
id required | string | |
business_id required | string | |
url required | string | |
events required | list of string | |
enabled | boolean | |
last_status | integer (or null) | |
last_error | string (or null) | |
created_at | string (date-time) (or null) | |
signing_secret required | string | Shown once. Use it to check the X-Receptionist-Signature header. |
Document
| Field | Type | About |
|---|---|---|
id required | string | |
name required | string | |
kind required | string | |
bytes required | integer | |
status required | string |
ErrorBody
| Field | Type | About |
|---|---|---|
error required | ErrorDetail |