REST API Reference
All calls with parameters, status codes and schemas.
This page describes the API in full. If you are setting the API up for the first time, better start with the step-by-step guide.
The base address is /api on your own installation, in the examples therefore https://example.com/api.
All calls are made from your server, not from your customers' browsers: no CORS headers are sent, and the token must not be built into a website or an app.
On this page
- Authentication
- GET /schedules
- GET /reasons
- GET /days
- GET /slots
- GET /forms
- POST /bookings
- Schemas
- Common error responses
- Machine-readable description
Authentication
Every call needs a token. You generate it in the admin area under Configuration → API.
The token starts with apm_, followed by 64 characters, and is shown only once.
There is exactly one token per installation: a new token invalidates the previous one immediately.
Authorization: Bearer apm_your-token
Without a valid token every call answers with 401 {"error":"Unauthorized"}.
If the API is not activated in the admin area, it answers with 503 API disabled.
GET/schedules
Returns all appointment calendars of the installation. You use the returned id values as the parameter schedule in all further calls.
Status codes
| Status | Meaning |
|---|---|
| 200 | List of the appointment calendars |
| 401 | Token is missing or invalid |
| 429 | Request limit reached |
| 503 | The API is not activated or the installation is not yet complete |
Response 200
{
"data": [
{ "id": 1, "name": "Main location" },
{ "id": 2, "name": "Branch" }
]
}
Call with curl
curl -H "Authorization: Bearer apm_your-token" \
https://example.com/api/schedules
GET/reasons
Returns the appointment reasons (services) of an appointment calendar. If no appointment reasons are set up for the calendar, data is an empty array. duration is the duration in seconds.
Query parameters
| Name | Type | Required | Description | Example |
|---|---|---|---|---|
schedule |
integer ≥ 1 | required | Number of the appointment calendar from GET /schedules |
1 |
Status codes
| Status | Meaning |
|---|---|
| 200 | List of the appointment reasons (can be empty) |
| 401 | Token is missing or invalid |
| 404 | Appointment calendar not found |
| 429 | Request limit reached |
| 503 | The API is not activated or the installation is not yet complete |
Response 200
{
"data": [
{
"id": 1,
"name": "Consultation",
"description": "Standard consultation",
"duration": 1800
},
{
"id": 2,
"name": "Follow-up appointment",
"description": "",
"duration": 900
}
]
}
Call with curl
curl -H "Authorization: Bearer apm_your-token" \
"https://example.com/api/reasons?schedule=1"
GET/days
Returns the days on which at least one time is free for this appointment calendar and appointment reason. The format is YYYY-MM-DD. How far the list reaches into the future is determined by the settings of the appointment calendar.
Query parameters
| Name | Type | Required | Description | Example |
|---|---|---|---|---|
schedule |
integer ≥ 1 | required | Number of the appointment calendar | 1 |
reason |
integer ≥ 1 | required | Number of the appointment reason from GET /reasons |
1 |
Status codes
| Status | Meaning |
|---|---|
| 200 | List of the days with free appointments |
| 401 | Token is missing or invalid |
| 404 | Appointment calendar or appointment reason not found |
| 429 | Request limit reached |
| 503 | The API is not activated or the installation is not yet complete |
Response 200
{
"data": ["2026-05-23", "2026-05-24", "2026-05-26"]
}
Call with curl
curl -H "Authorization: Bearer apm_your-token" \
"https://example.com/api/days?schedule=1&reason=1"
GET/slots
Returns the free times of a day. The times are local times of the installation, the format is YYYY-MM-DD HH:MM:SS. If nothing is free on that day, data is an empty array.
Query parameters
| Name | Type | Required | Description | Example |
|---|---|---|---|---|
schedule |
integer ≥ 1 | required | Number of the appointment calendar | 1 |
reason |
integer ≥ 1 | required | Number of the appointment reason | 1 |
day |
string | required | Day in the format YYYY-MM-DD from GET /days |
2026-05-23 |
Status codes
| Status | Meaning |
|---|---|
| 200 | List of the free times (can be empty) |
| 401 | Token is missing or invalid |
| 404 | Appointment calendar, appointment reason or day not found |
| 429 | Request limit reached |
| 503 | The API is not activated or the installation is not yet complete |
Response 200
{
"data": [
"2026-05-23 09:00:00",
"2026-05-23 09:30:00",
"2026-05-23 10:00:00"
]
}
Call with curl
curl -H "Authorization: Bearer apm_your-token" \
"https://example.com/api/slots?schedule=1&reason=1&day=2026-05-23"
GET/forms
Returns the form fields that have to be filled in to book this time. You use the field names as keys in the object submission of POST /bookings. Always query the fields instead of hard-coding them in your own program. The field password is never returned.
Query parameters
| Name | Type | Required | Description | Example |
|---|---|---|---|---|
schedule |
integer ≥ 1 | required | Number of the appointment calendar | 1 |
reason |
integer ≥ 1 | required | Number of the appointment reason | 1 |
slot |
string | required | Time in the format YYYY-MM-DD HH:MM:SS. The blank has to be encoded as %20. |
2026-05-23%2009:00:00 |
Status codes
| Status | Meaning |
|---|---|
| 200 | Form fields, keyed by field name |
| 401 | Token is missing or invalid |
| 404 | Appointment calendar, appointment reason or time not found |
| 429 | Request limit reached |
| 503 | The API is not activated or the installation is not yet complete |
Response 200
{
"data": {
"first_name": {
"form_type": "textbox",
"input_type": "text",
"label": "First name",
"required": true,
"value": ""
},
"last_name": {
"form_type": "textbox",
"input_type": "text",
"label": "Last name",
"required": true,
"value": ""
},
"email": {
"form_type": "textbox",
"input_type": "email",
"label": "Email address",
"required": false,
"value": ""
},
"phone": {
"form_type": "textbox",
"input_type": "tel",
"label": "Phone number",
"required": false,
"value": ""
}
}
}
Call with curl
curl -H "Authorization: Bearer apm_your-token" \
"https://example.com/api/forms?schedule=1&reason=1&slot=2026-05-23%2009:00:00"
POST/bookings
Creates an appointment. First query the form fields through GET /forms and send their values in the object submission. The header must contain Content-Type: application/json.
Fields in the request body
| Name | Type | Required | Description | Example |
|---|---|---|---|---|
schedule |
integer ≥ 1 | required | Number of the appointment calendar | 1 |
reason |
integer ≥ 1 | required | Number of the appointment reason | 1 |
slot |
string | required | Time in the format YYYY-MM-DD HH:MM:SS, exactly 19 characters |
2026-05-23 09:00:00 |
submission |
object | required | Values for the field names from GET /forms |
Request body
{
"schedule": 1,
"reason": 1,
"slot": "2026-05-23 09:00:00",
"submission": {
"first_name": "Hans",
"last_name": "Pitt",
"email": "hans.pitt@example.com",
"phone": "+49 30 1234567"
}
}
Status codes
| Status | Meaning |
|---|---|
| 201 | The appointment has been created |
| 400 | Invalid request, missing required field or invalid JSON |
| 401 | Token is missing or invalid |
| 404 | Appointment calendar, appointment reason or time not found |
| 415 | Content-Type: application/json is missing |
| 429 | Request limit reached |
| 500 | Customer record or appointment could not be saved |
| 503 | The API is not activated or the installation is not yet complete |
Response 201
{
"booking_id": 142,
"booking_details_id": "a3f8c2d1e5b6",
"user_id": 87,
"slot": "2026-05-23T09:00:00Z"
}
Response 400 for a missing required field
{
"error": "Field is required",
"field": "email"
}
Call with curl
curl -X POST https://example.com/api/bookings \
-H "Authorization: Bearer apm_your-token" \
-H "Content-Type: application/json" \
-d '{
"schedule": 1,
"reason": 1,
"slot": "2026-05-23 09:00:00",
"submission": {
"first_name": "Hans",
"last_name": "Pitt",
"email": "hans.pitt@example.com"
}
}'
Schemas
The data structures that appear in the responses.
Schedule
{
"id": integer,
"name": string
}
Reason
{
"id": integer,
"name": string,
"description": string,
"duration": integer // seconds
}
FormField
{
"form_type": string,
"input_type": string,
"label": string,
"required": boolean,
"value": any
}
BookingRequest
{
"schedule": integer,
"reason": integer,
"slot": "YYYY-MM-DD HH:MM:SS",
"submission": {
"<fieldname>": <value>
}
}
BookingResponse
{
"booking_id": integer,
"booking_details_id": string,
"user_id": integer,
"slot": "2026-05-23T09:00:00Z" // UTC, ISO 8601
}
Error / BookingError
{
"error": string,
"field": string // optional
}
Common error responses
These responses can occur with every call.
| Status | Meaning | Explanation |
|---|---|---|
| 401 | Unauthorized | The token is missing, is wrong or has been replaced by a new one. Some servers strip the Authorization header; ask your provider if in doubt. |
| 404 | Not Found | The requested appointment calendar, appointment reason, day or time is no longer available. A missing or wrongly formatted parameter also produces 404, not 400. |
| 429 | Too Many Requests | The limit of 300 requests per minute and token is reached. The header Retry-After names the waiting time in seconds. |
| 503 | Service Unavailable | The API is not activated in the admin area (API disabled) or the installation is not yet complete (Not configured). Both cases affect every call. |
Errors are always returned as JSON and always have the same shape:
{ "error": "Description of the error" }
For a booking, a validation error names the field concerned as well:
{ "error": "Field is required", "field": "email" }
The complete list of error texts per call is part of the guide.
Machine-readable description
All calls are described as an OpenAPI file as per version 3.1 as well. With it you generate client libraries or load the API into tools such as Postman, Insomnia or Swagger UI.
In the file the address /api is entered under servers. This is deliberately a relative entry,
so that the file is valid on every installation. In your tool please enter the address of your own scheduler,
for example https://example.com/api. The same file is part of your installation as well, under /api/openapi.json.
Back to the guide.