# API reference

> Every Ordstage API endpoint and webhook.

Source: https://www.ordstage.com/docs/reference/

Base URL: `https://api.ordstage.com`. The machine-readable OpenAPI document: https://www.ordstage.com/docs/openapi.json

## GET /v1/calendars

**List calendars**

Calendars this key can see, with their resources (rooms, doctors, tables) and services: the ids the other booking endpoints take.

## GET /v1/calendars/{calendar_id}/availability

**Check availability**

Free times, the same answer the assistant gets. `channel=online` is what the assistant offers; `team` also counts places kept for walk-ins or closed online.

## GET /v1/calendars/{calendar_id}/bookings

**List bookings**

Every booking in a calendar from every source (the assistant, the team, other systems), by date range or changed since a time.

## PUT /v1/calendars/{calendar_id}/bookings/{external_id}

**Create or update a booking**

Record a booking made in your system under your own id. Sending it again updates it, so retries are safe. It is recorded even when the time is full (it already happened); the answer then says `overbooked: true` and the team gets an alert.

## DELETE /v1/calendars/{calendar_id}/bookings/{external_id}

**Cancel a booking**

Cancel a booking you sent earlier, by your id. Cancelling one that was never sent answers `booking: null`.

## PUT /v1/calendars/{calendar_id}/blocks/{external_id}

**Close time**

Close a resource (maintenance, a private event) or stop online selling for the whole calendar (`scope: online`), under your own id. Sending it again replaces it.

## DELETE /v1/calendars/{calendar_id}/blocks/{external_id}

**Reopen time**

Remove a closure you sent earlier, by your id.

## GET /v1/business

**Get the business**

The business's profile and branding.

## PATCH /v1/business

**Update the business**

Change the name, website, colours, timezone or language; only the fields sent change. A logo can be removed here; a new one is chosen by previewing the website in Settings.

## GET /v1/members

**List members**

Everyone on the team, oldest first. Filter by role.

## GET /v1/members/{member_id}

**Get a member**

One member of the team.

## PATCH /v1/members/{member_id}

**Change a member's role**

Give a member another role. Only an owner's key can make or change owners, and the last active owner can't be demoted.

## DELETE /v1/members/{member_id}

**Remove a member**

Remove someone from the team. Their account stays (they may belong to other businesses); their conversations are unassigned and their personal connections here stop. The last active owner can't be removed, and the key's creator can't remove themselves.

## POST /v1/members/{member_id}/send_password_reset

**Email a password reset**

Email the member a link to choose a new password (valid 30 minutes). The link goes only to their own address; nothing about the password is ever returned or set by the API. An admin's key can't do this for an owner.

## GET /v1/invitations

**List invitations**

Invitations sent to join the team, oldest first. Filter by status.

## POST /v1/invitations

**Invite someone**

Email an invitation to join the team with a role (valid 7 days). Seats are counted, including pending invitations.

## GET /v1/invitations/{invitation_id}

**Get an invitation**

One invitation. Its link is never shown; only the invited address receives it.

## POST /v1/invitations/{invitation_id}/resend

**Resend an invitation**

Send the invitation again with a fresh link valid 7 days (the old link stops). At most once a minute.

## POST /v1/invitations/{invitation_id}/revoke

**Revoke an invitation**

Stop an invitation from being accepted. Its link stops working.

## GET /v1/webhooks

**List webhooks**

The business's webhook endpoints, oldest first. Secrets are never listed.

## POST /v1/webhooks

**Create a webhook**

Add an endpoint. The answer has its signing secret, shown only this once. Choose events, or none for every event (including ones added later). At most 10 per business.

## GET /v1/webhooks/{webhook_id}

**Get a webhook**

One webhook endpoint (never its secret).

## PATCH /v1/webhooks/{webhook_id}

**Update a webhook**

Change its name, address, events or calendars; only the fields sent change.

## DELETE /v1/webhooks/{webhook_id}

**Delete a webhook**

Remove an endpoint and its delivery history. Nothing more is sent to it.

## POST /v1/webhooks/{webhook_id}/pause

**Pause a webhook**

Stop sending. Events keep queuing and are sent after it is resumed (each is retried for about a day).

## POST /v1/webhooks/{webhook_id}/resume

**Resume a webhook**

Start sending again (also after it was paused for failing), with the failure count reset.

## POST /v1/webhooks/{webhook_id}/rotate_secret

**Rotate a webhook's secret**

A new signing secret, shown only in this answer. Every delivery from now on (retries too) is signed with it; the old one stops at once.

## POST /v1/webhooks/{webhook_id}/test

**Send a test event**

Send a signed `ping` now and answer with what the endpoint replied.

## GET /v1/webhooks/{webhook_id}/deliveries

**List deliveries**

What was sent to this webhook, oldest first; filter by status.

## POST /v1/webhooks/{webhook_id}/deliveries/{delivery_id}/retry

**Retry a delivery**

Queue a delivery again now. It is signed with the current secret and sent only if the business still allows the event.

## GET /v1/api_keys

**List API keys**

The business's keys (test keys: the sandbox's), oldest first: names, scopes, status and last use. Never a secret.

## GET /v1/api_keys/{key_id}

**Get an API key**

One key's name, scopes, status and last use. Never its secret.

## GET /v1/assistants

**List assistants**

The business's assistants, oldest first.

## GET /v1/assistants/{assistant_id}

**Get an assistant**

One assistant, with where its setup stands (`lifecycle`).

## POST /v1/assistants/{assistant_id}/publish

**Publish an assistant**

Publish the current setup and make the assistant answer with it, exactly like Publish in Studio. Refused while required setup is missing.

## POST /v1/assistants/{assistant_id}/pause

**Pause an assistant**

Stop the assistant answering customers; new messages go to the team. Pausing a paused assistant changes nothing.

## POST /v1/assistants/{assistant_id}/resume

**Resume an assistant**

Make a paused assistant answer again. Only a published setup without blocking problems can be resumed.

## POST /v1/assistants/{assistant_id}/test_messages

**Send a test message**

Send a message to the sandbox's assistant and get its reply, like the test chat in Studio: nothing reaches a customer. Pass the returned session_id to continue the same conversation.

## GET /v1/assistants/{assistant_id}/knowledge_sources

**List knowledge sources**

What the assistant learns from (pages, files, sheets), oldest first.

## POST /v1/assistants/{assistant_id}/knowledge_sources

**Add a web page**

Add a public web page for the assistant to learn from. It is read right away (`status` processing, then ready or error); publish the assistant for customers to get it.

## POST /v1/assistants/{assistant_id}/knowledge_sources/{source_id}/refresh

**Refresh a knowledge source**

Read the source again; what it said before stays in use until the new text is ready.

## DELETE /v1/assistants/{assistant_id}/knowledge_sources/{source_id}

**Remove a knowledge source**

The assistant stops using it at once. A source a published release used is kept (archived) for that release's history.

## GET /v1/conversations

**List conversations**

Customer conversations oldest first; filter by status, who handles them, contact or channel.

## GET /v1/conversations/{conversation_id}

**Get a conversation**

One conversation, with its channel, contact, status and labels.

## PATCH /v1/conversations/{conversation_id}

**Update a conversation**

Change its priority, assignee, team, or whether it waits for the customer (pending); only the fields sent change.

## GET /v1/conversations/{conversation_id}/messages

**List messages**

The conversation's messages oldest first. Internal notes between teammates are never included.

## POST /v1/conversations/{conversation_id}/reply

**Reply to a conversation**

Send a reply on the conversation's channel as the key's creator, like replying in the Inbox. Channel rules apply (for example WhatsApp's 24-hour window). The assistant keeps handling the conversation unless it is handed over.

## POST /v1/conversations/{conversation_id}/handover

**Hand over to the team**

The assistant stops answering and the team takes the conversation, optionally a teammate or Inbox team (who must be able to take it now).

## POST /v1/conversations/{conversation_id}/return_to_assistant

**Return to the assistant**

The assistant answers the conversation again.

## POST /v1/conversations/{conversation_id}/close

**Close a conversation**

Mark it resolved, like Done in the Inbox; the channel is told where it supports that. Closing a closed conversation changes nothing.

## POST /v1/conversations/{conversation_id}/reopen

**Reopen a conversation**

Open a resolved or closed conversation again.

## POST /v1/conversations/{conversation_id}/labels

**Add a label**

Label the conversation with one of the business's labels.

## DELETE /v1/conversations/{conversation_id}/labels/{label_id}

**Remove a label**

Take a label off the conversation.

## GET /v1/labels

**List labels**

The business's Inbox labels, to filter and label conversations with.

## GET /v1/contacts

**List contacts**

Contacts oldest first. Filter by exact email or phone, a tag, or search text.

## POST /v1/contacts

**Create a contact**

Add a contact with at least a name, an email or a phone number.

## GET /v1/contacts/{contact_id}

**Get a contact**

One contact, with its tags and attributes.

## PATCH /v1/contacts/{contact_id}

**Update a contact**

Change details, tags (replaced) or attributes (merged; null removes a key). Only the fields sent change.

## DELETE /v1/contacts/{contact_id}

**Delete a contact**

Delete a contact. Their conversations, orders, bookings and leads stay, without a contact.

## POST /v1/contacts/{contact_id}/tags

**Add or remove tags**

Add and remove tags without sending the whole list.

## POST /v1/contacts/{contact_id}/merge

**Merge a duplicate**

Merge `duplicate_id` into this contact: its conversations, orders, bookings, leads and channel identities move here, empty fields are filled from it, tags are joined, then it is deleted.

## GET /v1/leads

**List leads**

Leads oldest first. Filter by status.

## POST /v1/leads

**Create a lead**

Add someone who wants to be contacted: a name, what they need, and an email or phone.

## GET /v1/leads/{lead_id}

**Get a lead**

One lead, with its status and the team's note.

## PATCH /v1/leads/{lead_id}

**Update a lead**

Change its status (any status can follow any other; the first `contacted` sets contacted_at), the team's note, who follows up, or its details. Only the fields sent change.

## DELETE /v1/leads/{lead_id}

**Delete a lead**

Delete a lead. Its conversation and contact stay.

## Webhooks

- `booking.created`: Sent when a booking is made from any source (the assistant, the team, another system).
- `booking.confirmed`: Sent when the team approves a booking that waited for approval.
- `booking.updated`: Sent when a booking's time, resource, details or status changes.
- `booking.cancelled`: Sent when a booking is cancelled, by the customer, the team or a system.
- `booking.conflict`: Sent when bookings from different sources overlap beyond a resource's capacity (for example a booking you pushed for a time that was already full). The team is alerted too.
- `availability.closed`: Sent when time is closed for online booking across a calendar (`scope: online`).
- `assistant.published`: Sent when an assistant's setup is published (from Studio or the API) and it starts answering with it.
- `assistant.paused`: Sent when an assistant stops answering customers; conversations go to the team.
- `assistant.resumed`: Sent when a paused assistant answers customers again.
- `business.updated`: Sent when the business's name, website, logo, colours, timezone or language change, from Settings or the API.
- `contact.created`: Sent when a contact is created anywhere: a new person messages the business, an order or booking is made, or the API.
- `contact.updated`: Sent when a contact's details, tags or attributes change.
- `contact.deleted`: Sent when a contact is deleted; `contact` is how it was.
- `contact.merged`: Sent when a duplicate is merged into a contact: `contact` is the one kept, `merged_contact_id` the one removed.
- `lead.created`: Sent when someone asks to be contacted (through the assistant) or a lead is added through the API.
- `lead.updated`: Sent when a lead's details, note, assignee or status change.
- `lead.status_changed`: Sent when a lead moves to another status.
- `lead.deleted`: Sent when a lead is deleted; `lead` is how it was.
- `conversation.handed_over`: Sent when a conversation moves from the assistant to the team (the assistant, a teammate or the API handed it over).
- `conversation.returned_to_assistant`: Sent when the team gives a conversation back to the assistant to answer.
- `conversation.closed`: Sent when a conversation is resolved or closed, from the Inbox or the API.
- `conversation.reopened`: Sent when a resolved or closed conversation is opened again by the team or the API.
- `conversation.updated`: Sent when a conversation's priority, assignee, team, labels or waiting status change.
- `conversation.reply_sent`: Sent when a teammate's reply is sent from the Inbox or the API (not the assistant's own answers).
- `member.invited`: Sent when an invitation is created, in Settings or by the API.
- `member.invitation_revoked`: Sent when a pending invitation is revoked.
- `member.role_changed`: Sent when a member's role changes.
- `member.removed`: Sent when someone is removed from the team (their account stays).
- `ping`: Sent when you press Send test on a webhook in Developers settings.
