Spotbookr Business API
Read and change a company's Spotbookr records from your own code, or from any tool that can call an API.
Start in four steps
- Get a key. The owner of the Spotbookr account signs in and opens Account, Integrations, names the key, chooses what it may do, and copies it. It is shown once.
- Check it works. Call
GET https://work.spotbookr.com/api/v1/pingwith the headerAuthorization: Bearer YOUR_KEY. The answer names the company and says what the key may do. - Read something.
GET /api/v1/contactslists contacts. Every list has the same shape:data,total,limit,offset. - Change something.
POSTadds a record and answers 201 with it;PATCHchanges only the fields you send;DELETEremoves it where that is allowed.
The rules
- A key on every call, in the header
Authorization: Bearer YOUR_KEY. Never put a key in a web address or in code that runs in a browser. - A key has a level: add leads only, read only, or read and change. It can also be limited to parts of the account.
- JSON in, JSON out. Send
Content-Type: application/jsonwith POST and PATCH. Dates areYYYY-MM-DD; amounts are in the account's currency. - Pages.
?limit=50&offset=50asks for the next page. The most in one page is 200. - Limit. 120 calls a minute from one address.
- Automations and webhooks still run on what the API adds or changes.
- A company's own fields travel inside
custom, by name:{"custom": {"Region": "North"}}.
When a call is refused
The answer is {"error": {"code": "...", "message": "..."}}. The message says what to change.
| 400 | bad_request | The body was not a JSON object. |
| 401 | unauthorized | The key is missing, wrong or revoked. |
| 403 | forbidden | The key is not allowed to do this: it is leads only, read only, or limited to other parts of the account. |
| 404 | not_found | There is no record with that id in your account. |
| 409 | duplicate, in_use, not_allowed, product_off | The change conflicts with what is there: an email already used, a contact that still has deals, a board rule, or a product your company does not have. |
| 422 | invalid | A field is missing or has a value that is not accepted. The message names it. |
| 429 | rate_limited | More than 120 calls in a minute from one address. |
Examples
Command line
curl https://work.spotbookr.com/api/v1/ping -H "Authorization: Bearer YOUR_KEY"
curl -X POST https://work.spotbookr.com/api/v1/contacts \
-H "Authorization: Bearer YOUR_KEY" -H "Content-Type: application/json" \
-d '{"name": "Dana Reyes", "email": "dana@example.com", "company": "Example Co", "tags": ["newsletter"]}'
curl -X PATCH https://work.spotbookr.com/api/v1/tasks/123 \
-H "Authorization: Bearer YOUR_KEY" -H "Content-Type: application/json" -d '{"done": true}'
Python
import requests
api = "https://work.spotbookr.com/api/v1"
headers = {"Authorization": "Bearer YOUR_KEY"}
# every contact, a page at a time
offset = 0
while True:
page = requests.get(api + "/contacts", headers=headers, params={"limit": 200, "offset": offset}).json()
for contact in page["data"]:
print(contact["name"], contact["email"])
offset += page["limit"]
if offset >= page["total"]:
break
# open a deal for a contact
r = requests.post(api + "/deals", headers=headers, json={"name": "Website rebuild", "contact_id": 42, "value": 4800})
if r.status_code != 201:
print(r.json()["error"]["message"])
JavaScript (on a server, not in a browser)
const api = "https://work.spotbookr.com/api/v1";
const headers = { Authorization: "Bearer " + process.env.SPOTBOOKR_KEY, "Content-Type": "application/json" };
const res = await fetch(api + "/tasks", {
method: "POST", headers,
body: JSON.stringify({ title: "Call back about pricing", due: "2026-11-03", contact_id: 42 }),
});
const body = await res.json();
if (!res.ok) throw new Error(body.error.message);
console.log(body.data.id);Every call
General
| GET | /api/v1/ping | Check a key works. Answers with your company name. |
| GET | /api/v1/users | The people on your account, for use as owner_id or assignee_id. |
Contacts
| GET | /api/v1/contacts | List contacts. Filters: q, email, status, company_id. |
| POST | /api/v1/contacts | Add a contact. Needs name. Also: email, phone, title, company, status, source, tags, address, notes, owner_id, custom. |
| GET | /api/v1/contacts/{id} | One contact. |
| PATCH | /api/v1/contacts/{id} | Change any of the same fields. Send only what changes. |
| DELETE | /api/v1/contacts/{id} | Delete a contact that has no deals. |
Companies
| GET | /api/v1/companies | List companies. Filter: q. |
| POST | /api/v1/companies | Add a company. Needs name. Also: website, phone, industry, address, notes, tags, owner_id, custom. |
| GET | /api/v1/companies/{id} | One company. |
| PATCH | /api/v1/companies/{id} | Change a company. Renaming it renames it on its people. |
Deals
| GET | /api/v1/deals | List deals. Filters: stage (a stage name, or open), pipeline (a pipeline name), contact_id, owner_id. |
| POST | /api/v1/deals | Open a deal. Needs name and contact_id. Also: value, pipeline, stage, expected_close, description, owner_id, tags, custom. |
| GET | /api/v1/deals/{id} | One deal. |
| PATCH | /api/v1/deals/{id} | Change a deal, or move it between your open stages. Winning, losing and reopening are done in Spotbookr. |
Tasks
| GET | /api/v1/tasks | List CRM tasks. Filters: done (true or false), owner_id, contact_id, deal_id. |
| POST | /api/v1/tasks | Add a task. Needs title. Also: due, owner_id, contact_id, deal_id, notes. |
| GET | /api/v1/tasks/{id} | One task. |
| PATCH | /api/v1/tasks/{id} | Change a task, or finish it with "done": true. |
| DELETE | /api/v1/tasks/{id} | Delete a task. |
Project Management
| GET | /api/v1/projects | List projects, each with the names of its columns. |
| GET | /api/v1/projects/{id}/items | A project's work items. Filters: done, assignee_id, type. |
| POST | /api/v1/projects/{id}/items | Add a work item. Needs title. Also: type, description, assignee_id, priority, start_date, due_date, labels. |
| GET | /api/v1/items/{id} | One work item. |
| PATCH | /api/v1/items/{id} | Change a work item, or move it with "status": the name of a column. The board's own rules apply. |
Invoicing (read only)
| GET | /api/v1/invoices | List invoices with their totals, balance and state. Filter: state. |
| GET | /api/v1/estimates | List estimates. |
Scheduling (read only)
| GET | /api/v1/appointments | Appointments, meetings and work sessions between two dates. Filters: from, to (YYYY-MM-DD; the next 30 days when left out). |
| GET | /api/v1/services | The services customers can book. |
Leads
| POST | /api/v1/leads | Add a lead in one call: a contact, a deal in your first stage and a task to reply. Needs name and email. Also: company, phone, message, source. |
The fields of each record
Contact
name text, requiredemail text, unique among your contactsphone texttitle textcompany text; the company record is found or made from itstatus Lead, Prospect, Customer or Inactivesource texttags list of textaddress textnotes textowner_id a person's idcustom your own fields, by nameid, company_id, created_at read onlyCompany
name text, required, uniquewebsite textphone textindustry textaddress textnotes texttags list of textowner_id a person's idcustom your own fields, by nameDeal
name text, requiredcontact_id a contact's id, required when openingvalue number, in your account's currencypipeline the name of one of your pipelines, set when opening; your first pipeline when left outstage one of the open stage names of the deal's pipelineexpected_close datedescription textowner_id a person's idtags list of textcustom your own fields, by nameopen read only: false once won or lostTask
title text, requireddue datedone true or falseowner_id a person's idcontact_id a contact's iddeal_id a deal's idnotes textWork item
title text, requiredtype one of the project's types, such as Task, Story or Bugdescription textstatus the name of one of the project's columnspriority Lowest, Low, Medium, High or Highestassignee_id a person's idstart_date, due_date datelabels list of textkey, done, status_category read onlyBeing told, instead of asking
To hear the moment something happens, such as a deal being won or an invoice being paid, use webhooks: Spotbookr sends a signed message to an address of yours. They are set up under Account, Integrations, and work alongside the API.
Questions about the API: support@spotbookr.com. More help is in the Help Center.
