API Reference Your Bizstim data, from your own systems.
A JSON REST API for the clients, practitioners, staff, sessions, services and payments on your account. One key, one base URL, the same records you see in the app.
Getting started
The Bizstim API gives your own software access to the records on your account: read them today, with create, update and delete arriving one resource at a time. Every request is an HTTPS call to the base URL below with your API key in a header, and every reply is JSON.
The API is included in the Enterprise plan and during the free trial. See the plans, or start a trial.
Your first request
Replace YOUR_API_KEY with a key from your account (the next section) and list two clients:
curl "https://www.bizstim.com/api/v1/clients?limit=2" \
-H "X-API-KEY: YOUR_API_KEY" \
-H "Accept: application/json"<?php
$ch = curl_init('https://www.bizstim.com/api/v1/clients?limit=2');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ['X-API-KEY: YOUR_API_KEY', 'Accept: application/json'],
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);const response = await fetch('https://www.bizstim.com/api/v1/clients?limit=2', {
headers: { 'X-API-KEY': 'YOUR_API_KEY', 'Accept': 'application/json' }
});
const data = await response.json();The reply is a list envelope with the records in data:
{
"object": "list",
"total": 57,
"limit": 2,
"offset": 0,
"has_more": true,
"data": [
{
"object": "client",
"id": 42,
"first_name": "Jane",
"last_name": "Doe",
"address1": "12 Main Street",
"address2": "Suite 4",
"city": "Toronto",
"state": "ON",
"zipcode": "M5V 2T6",
"country": "Canada",
"p_phone": "416-555-0100",
"s_phone": "416-555-0101",
"text_phone": "416-555-0102",
"dob": "1990-05-20",
"status": "active",
"inactive_date": "2026-01-15",
"inactive_reason": "An example reason.",
"start_date": "2026-01-15",
"notes": "Prefers afternoon appointments.",
"timezone": "America/Toronto",
"send_welcome_message": 1,
"send_lesson_reminders": 1,
"add_session": 1,
"see_practs_information": 1,
"text_reminders": 1,
"use_records": 1,
"session_cancel": 1,
"ace": 1,
"hide_costs": 1,
"username": "jane.doe",
"email": "jane.doe@example.com"
},
{
"object": "client",
"id": 43,
"first_name": "Jane",
"last_name": "Doe",
"address1": "12 Main Street",
"address2": "Suite 4",
"city": "Toronto",
"state": "ON",
"zipcode": "M5V 2T6",
"country": "Canada",
"p_phone": "416-555-0100",
"s_phone": "416-555-0101",
"text_phone": "416-555-0102",
"dob": "1990-05-20",
"status": "active",
"inactive_date": "2026-01-15",
"inactive_reason": "An example reason.",
"start_date": "2026-01-15",
"notes": "Prefers afternoon appointments.",
"timezone": "America/Toronto",
"send_welcome_message": 1,
"send_lesson_reminders": 1,
"add_session": 1,
"see_practs_information": 1,
"text_reminders": 1,
"use_records": 1,
"session_cancel": 1,
"ace": 1,
"hide_costs": 1,
"username": "jane.doe",
"email": "jane.doe@example.com"
}
]
} Authentication
Every request carries your key in the X-API-KEY header. A key belongs to one Bizstim account and only ever reads or changes that account's records.
Creating a key
Log in to Bizstim, open Settings, then System, and find API Settings. Generate API Key creates the account's primary key, which may use every resource. Add Secondary API Key creates a key limited to the resources you tick; a secondary key answers 403 resource_not_allowed on anything else.
- Treat a key like a password: keep it on your server, never in a browser or a mobile app.
- To rotate a key, delete it and generate a new one. The old key stops working at once.
- A missing or unknown key answers
401 invalid_key.
Account gates
If the account's e-mail address is not verified, the subscription has lapsed, or the plan does not include the API, every request answers 403 with the matching code (see Errors). The message names the fix.
Making requests
Resources are plural nouns in the path; the HTTP verb says what to do.
| Request | Action | Reply |
|---|---|---|
GET /api/v1/clients | List | 200 with a list envelope |
GET /api/v1/clients/{id} | Retrieve | 200 with one record |
POST /api/v1/clients | Create | 201 with the new record |
PATCH /api/v1/clients/{id} | Update the fields you send (PUT is accepted as the same call) | 200 with the updated record |
DELETE /api/v1/clients/{id} | Delete | 200 with {"id", "object", "deleted": true} |
Create, update and delete are being added one resource at a time. Each resource page lists exactly the endpoints that are live today; a verb a resource does not support answers 405 with an Allow header naming the ones it does.
- Request bodies are JSON (
Content-Type: application/json), 256 KB at most. Lookups and filters are query parameters. Filters combine: a record is returned only when it matches every filter you send. The key goes in theX-API-KEYheader only, never in the URL. - Ids are integers. A record that does not exist on your account answers
404 not_found, including ids that belong to another account. - Length limits apply to the value as stored. The characters
& < > " 'are stored as HTML entities (5 or 6 characters each), so a value that fits the limit as sent but not once stored answers400naming the field. - Paths use hyphens (
/custom-fields); JSON fields use underscores (first_name). - Only JSON is returned. The
formatparameter of the retired API is ignored.
Responses
Every record carries object, the resource it belongs to, followed by the record's fields in a fixed order. Integers and decimals are JSON numbers, everything else is a string, and an empty value is null.
{
"object": "client",
"id": 42,
"first_name": "Jane",
"last_name": "Doe",
"address1": "12 Main Street",
"address2": "Suite 4",
"city": "Toronto",
"state": "ON",
"zipcode": "M5V 2T6",
"country": "Canada",
"p_phone": "416-555-0100",
"s_phone": "416-555-0101",
"text_phone": "416-555-0102",
"dob": "1990-05-20",
"status": "active",
"inactive_date": "2026-01-15",
"inactive_reason": "An example reason.",
"start_date": "2026-01-15",
"notes": "Prefers afternoon appointments.",
"timezone": "America/Toronto",
"send_welcome_message": 1,
"send_lesson_reminders": 1,
"add_session": 1,
"see_practs_information": 1,
"text_reminders": 1,
"use_records": 1,
"session_cancel": 1,
"ace": 1,
"hide_costs": 1,
"username": "jane.doe",
"email": "jane.doe@example.com"
} - Dates are
YYYY-MM-DDand timesHH:MM:SS, in the account's time zone (sessions name it in theirtimezonefield). - Each resource page lists the fields with their types. A field is never removed or renamed within v1; new fields may be added.
Pagination
Every list answers the same envelope. limit is the page size (1 to 1000, default 100) and offset the number of records to skip (default 0). total counts every record that matches the filters and has_more says whether another page exists.
{
"object": "list",
"total": 57,
"limit": 2,
"offset": 0,
"has_more": true,
"data": [
{
"object": "client",
"id": 42,
"first_name": "Jane",
"last_name": "Doe",
"address1": "12 Main Street",
"address2": "Suite 4",
"city": "Toronto",
"state": "ON",
"zipcode": "M5V 2T6",
"country": "Canada",
"p_phone": "416-555-0100",
"s_phone": "416-555-0101",
"text_phone": "416-555-0102",
"dob": "1990-05-20",
"status": "active",
"inactive_date": "2026-01-15",
"inactive_reason": "An example reason.",
"start_date": "2026-01-15",
"notes": "Prefers afternoon appointments.",
"timezone": "America/Toronto",
"send_welcome_message": 1,
"send_lesson_reminders": 1,
"add_session": 1,
"see_practs_information": 1,
"text_reminders": 1,
"use_records": 1,
"session_cancel": 1,
"ace": 1,
"hide_costs": 1,
"username": "jane.doe",
"email": "jane.doe@example.com"
},
{
"object": "client",
"id": 43,
"first_name": "Jane",
"last_name": "Doe",
"address1": "12 Main Street",
"address2": "Suite 4",
"city": "Toronto",
"state": "ON",
"zipcode": "M5V 2T6",
"country": "Canada",
"p_phone": "416-555-0100",
"s_phone": "416-555-0101",
"text_phone": "416-555-0102",
"dob": "1990-05-20",
"status": "active",
"inactive_date": "2026-01-15",
"inactive_reason": "An example reason.",
"start_date": "2026-01-15",
"notes": "Prefers afternoon appointments.",
"timezone": "America/Toronto",
"send_welcome_message": 1,
"send_lesson_reminders": 1,
"add_session": 1,
"see_practs_information": 1,
"text_reminders": 1,
"use_records": 1,
"session_cancel": 1,
"ace": 1,
"hide_costs": 1,
"username": "jane.doe",
"email": "jane.doe@example.com"
}
]
} To read everything, repeat the request with offset increased by limit until has_more is false. Lists are ordered by id, oldest first, so a new record appears at the end.
Errors
An error is an HTTP status plus one JSON shape: a stable code to branch on and a message for people.
{
"error": {
"code": "not_found",
"message": "No client with that id exists on this account."
}
} | Status | Code | When |
|---|---|---|
| 400 | invalid_request | A parameter is malformed: a limit out of range, a bad date, a missing required field. |
| 401 | invalid_key | No X-API-KEY header, or the key is unknown. |
| 403 | resource_not_allowed | The key is a secondary key that was not given this resource. |
| 403 | plan_not_allowed | The account's plan does not include the API. |
| 403 | account_unverified | The account's e-mail address has not been verified yet. |
| 403 | account_locked | The subscription has lapsed. Log in to the app to renew it. |
| 404 | unknown_resource | The path is not a v1 resource. |
| 404 | not_found | No record with that id exists on this account. |
| 405 | method_not_allowed | The verb is not supported here; the Allow header lists the ones that are. |
| 413 | payload_too_large | The request body is over 256 KB. |
| 409 | conflict | The change clashes with existing data: a duplicate, or a record that cannot be deleted. |
| 410 | gone | A URL of the retired API. Move to /api/v1/. |
| 429 | rate_limited | The hourly limit for this URL and key was reached. |
| 500 | server_error | Something failed on our side. Retry, then contact support. |
| 503 | maintenance | The service is down for maintenance. Retry shortly. |
Rate limits
During the trial each key may call each URL and verb 100 times per hour; on the Enterprise plan, 1,000 times. The count starts with the first call and resets an hour later. A request over the limit answers 429 rate_limited; wait for the window to pass. Read lists in pages of up to 1,000 rather than one record at a time.
Moving a large data set, or another case the standard limit does not fit? Contact support before you start: a higher limit can be set on your key for the time you need it.
Versioning
Every path carries the version, /api/v1/. Within v1 we add fields and endpoints and never remove or rename them; a change that would break your integration ships as a new version with its own path. GET /api/v1 lists the resources of the version you are calling.
The API that lived at /api/{method} before v1 has been retired. Its URLs answer 410 gone with a link here. Updates to the API are announced in the changelog.
Resources
One page per resource: the record's fields and types, every live endpoint with its parameters, and example requests and replies.
Agents
Third parties who pay for or oversee clients (a school district, a parent company): contact details, status, portal permissions and the login record.
Clients
The people your business serves: contact details, status, settings and the login record.
Discounts
Discount codes clients can apply when they pay, with their limits and the services they cover.
Locations
The places sessions happen, with their contact details and the practitioners who work there.
Packages
Session packages the account sells in package mode: a number of sessions for one price. Active and secret packages can be paid for; inactive ones are listed but cannot be; deleted ones are not returned.
Payments
Money received from clients (the revenues ledger), credits included.
Practitioners
The people who deliver your sessions: contact details, status, profile, permissions and the login record.
Services
What you sell: each service with its price, type, booking rules and, for tiered services, its group.
Sessions
Scheduled appointments, lessons and classes. Dates and times are in the account time zone (the timezone field).
Skills
The skill list practitioners are tagged with.
Staff
Your office users: contact details, status, profile, module permissions and the login record.
Timezones
The time zone names accepted in timezone fields, with their UTC offsets.
Users
Login records of everyone on the account (clients, practitioners, staff, agents and the owner), a user's weekly availability, and the custom fields the account defines.