Introduction
About
This is the documentation for the Shlott REST API. The API is the primary way to interact with Shlott programmatically, and can be used to build integrations and custom tooling for your flying club or aviation organisation. This page documents the available authentication methods, all API endpoints, example request payloads, and the expected HTTP responses.
API access is included with Shlott Pro groups. Every group can use Shlott's core booking features on the free plan, and Pro adds extras such as the API, while helping to fund the continued development of Shlott. If your group is on the free plan and you would like to use the API, you can upgrade to Pro at any time from the Subscription page in Shlott.
Overview
The Shlott REST API is a token-based API that sits alongside the Shlott web platform, providing a focused integration layer for third-party tools. The API exposes a curated set of endpoints covering calendars, bookings, and club membership — with more endpoints being added over time.
The API is secured using token-based authentication via Laravel Sanctum. Most endpoints are protected and require a valid API key. To obtain an API key, log into Shlott, go to Manage Account, click the API tile and then click Generate API Key. The key must be included in the Authorization header of every subsequent request, preceded by Bearer — for example: Authorization: Bearer your-api-key-here.
Each API key is specific to the group it was created for, so switch to the group in Shlott before generating the key. A key only works for that group, so to use the API with several groups, create a key in each one (you can create as many keys as you need). A key stops working if the group stops being Pro, or if you leave or are removed from the group. If a request includes a group_id it must be the group the key was created for; if it is left out, that group is used.
API keys expire 30 days after they are created, after which requests return 401 Unauthenticated and you must generate a new one. To invalidate a key sooner, remove it from the API page in Shlott.
The API accepts and returns JSON only.
Getting started
- Generate an API key in Shlott (see above) while you are in the group you want to use.
- Send a request with the key in the
Authorizationheader. This one lists a day's bookings on the calendar:
curl "https://api.shlott.com/v1/calendar/day?date=2025-06-21" \
-H "Authorization: Bearer your-api-key-here" \
-H "Accept: application/json"
You do not need to send group_id, because the key already belongs to one group. If you do send it, it must be that group. A 200 response contains the day's resources and bookings as JSON. If you get a 401, 403 or 422, see Errors below.
Errors
Every error response is JSON and includes a human-readable message. The status code tells you what went wrong:
| Status | Meaning |
|---|---|
401 |
The API key is missing, wrong, revoked or has expired (keys last 30 days). Body: {"message": "Unauthenticated."} |
403 |
The key is valid but is not allowed to do this. For example the key belongs to a different group, the group is no longer Pro, or the action needs a group administrator. |
404 |
The thing you asked for does not exist, or is not in your group. |
422 |
The request failed validation. errors lists each field and what is wrong with it. |
429 |
Too many requests. Wait for the number of seconds in the Retry-After header, then try again. |
Notice: the message text is written for people. It can change and may be translated, so never match on it in your code. Use the HTTP status code instead.
Validation errors look like this:
{
"message": "The date field is required.",
"errors": { "date": ["The date field is required."] }
}
Rate limits
Each user can make up to 60 requests per minute across the API, however many keys they have. Some endpoints that send emails or delete data have tighter limits, which are noted on the endpoint. Every response carries X-RateLimit-Limit and X-RateLimit-Remaining headers so you can see how much of your allowance is left. When you go over, you get a 429 response with a Retry-After header. Please do not poll the API repeatedly: fetch what you need and cache it.
Pagination
Most list endpoints return everything in one response. Endpoints that can return a lot of data, currently List Members, are paginated: send page (from 1) and per_page (1 to 100, default 25), and read total, page, per_page and has_more in the response. Keep requesting the next page until has_more is false. Each endpoint's documentation says whether it is paginated.
Support
Support for the Shlott API is limited. While we are happy to assist with questions about specific endpoints or unexpected behaviour, we are unable to provide assistance with writing code, building integrations, or connecting the API to your own website or application. Developers are expected to have the necessary technical knowledge to work with a REST API independently.
Authenticating requests
To authenticate requests, include an Authorization header with the value "Bearer {YOUR_AUTH_KEY}".
All authenticated endpoints are marked with a requires authentication badge in the documentation below.
You can obtain an API key by logging into Shlott, going to Manage Account and clicking the API tile, then clicking Generate API Key.
Calendar
Calendar Sort Order
requires authentication
Categories and roles in the order the calendar shows them. count is the number of live
objects (categories) or members (roles) in each.
Example request:
const url = new URL(
"https://api.shlott.com/v1/manage/sort-order"
);
const params = {
"group_id": "5",
};
Object.keys(params)
.forEach(key => url.searchParams.append(key, params[key]));
const headers = {
"Authorization": "Bearer {YOUR_AUTH_KEY}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());$client = new \GuzzleHttp\Client();
$url = 'https://api.shlott.com/v1/manage/sort-order';
$response = $client->get(
$url,
[
'headers' => [
'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
'Content-Type' => 'application/json',
'Accept' => 'application/json',
],
'query' => [
'group_id' => '5',
],
]
);
$body = $response->getBody();
print_r(json_decode((string) $body));import requests
import json
url = 'https://api.shlott.com/v1/manage/sort-order'
params = {
'group_id': '5',
}
headers = {
'Authorization': 'Bearer {YOUR_AUTH_KEY}',
'Content-Type': 'application/json',
'Accept': 'application/json'
}
response = requests.request('GET', url, headers=headers, params=params)
response.json()Example response (200, Order):
{
"data": [
{
"id": 3,
"type": "resource",
"name": "Courts",
"count": 4
},
{
"id": 2,
"type": "role",
"name": "Coach",
"count": 6
}
]
}
Example response (403, Not a group admin):
{
"message": "Forbidden."
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Save Calendar Sort Order
requires authentication
The complete new order of categories and roles, first to last. Hidden ones are moved after them, as on the website.
Example request:
const url = new URL(
"https://api.shlott.com/v1/manage/sort-order"
);
const headers = {
"Authorization": "Bearer {YOUR_AUTH_KEY}",
"Content-Type": "application/json",
"Accept": "application/json",
};
let body = {
"group_id": 5,
"order": [
"architecto"
]
};
fetch(url, {
method: "PUT",
headers,
body: JSON.stringify(body),
}).then(response => response.json());$client = new \GuzzleHttp\Client();
$url = 'https://api.shlott.com/v1/manage/sort-order';
$response = $client->put(
$url,
[
'headers' => [
'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
'Content-Type' => 'application/json',
'Accept' => 'application/json',
],
'json' => [
'group_id' => 5,
'order' => ['architecto'],
],
]
);
$body = $response->getBody();
print_r(json_decode((string) $body));import requests
import json
url = 'https://api.shlott.com/v1/manage/sort-order'
payload = {
"group_id": 5,
"order": [
"architecto"
]
}
headers = {
'Authorization': 'Bearer {YOUR_AUTH_KEY}',
'Content-Type': 'application/json',
'Accept': 'application/json'
}
response = requests.request('PUT', url, headers=headers, json=payload)
response.json()Example response (200):
{
"message": "The order has been saved."
}
Example response (403, Not a group admin):
{
"message": "Forbidden."
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Get Calendar Settings
requires authentication
The values on the website's calendar settings page, plus the objects that can be picked as the
default month-view object (value is resource_<id> or member_<id>; 0 means none).
Example request:
const url = new URL(
"https://api.shlott.com/v1/manage/calendar-settings"
);
const params = {
"group_id": "5",
};
Object.keys(params)
.forEach(key => url.searchParams.append(key, params[key]));
const headers = {
"Authorization": "Bearer {YOUR_AUTH_KEY}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());$client = new \GuzzleHttp\Client();
$url = 'https://api.shlott.com/v1/manage/calendar-settings';
$response = $client->get(
$url,
[
'headers' => [
'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
'Content-Type' => 'application/json',
'Accept' => 'application/json',
],
'query' => [
'group_id' => '5',
],
]
);
$body = $response->getBody();
print_r(json_decode((string) $body));import requests
import json
url = 'https://api.shlott.com/v1/manage/calendar-settings'
params = {
'group_id': '5',
}
headers = {
'Authorization': 'Bearer {YOUR_AUTH_KEY}',
'Content-Type': 'application/json',
'Accept': 'application/json'
}
response = requests.request('GET', url, headers=headers, params=params)
response.json()Example response (200, Settings):
{
"calendar_intervals": 2,
"hide_member_details_on_calendar": false,
"hide_unavailable_all_day_rows": false,
"flag_booking_if_member_has_not_booked_in": 0,
"week_starts_on": 1,
"default_overview_object": "0",
"overview_amber_limit": 3,
"overview_red_limit": 5,
"objects": [
{
"value": "resource_8",
"label": "Court 1 (Courts)"
}
]
}
Example response (403, Not a group admin):
{
"message": "Forbidden."
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Update Calendar Settings
requires authentication
Same rules as the website: the calendar interval must suit the group's opening times, the amber limit must be below the red limit, and the default object must belong to the group.
Example request:
const url = new URL(
"https://api.shlott.com/v1/manage/calendar-settings"
);
const headers = {
"Authorization": "Bearer {YOUR_AUTH_KEY}",
"Content-Type": "application/json",
"Accept": "application/json",
};
let body = {
"group_id": 5,
"calendar_intervals": 2,
"hide_member_details_on_calendar": false,
"hide_unavailable_all_day_rows": false,
"flag_booking_if_member_has_not_booked_in": 0,
"week_starts_on": 1,
"default_overview_object": "0",
"overview_amber_limit": 3,
"overview_red_limit": 5
};
fetch(url, {
method: "PUT",
headers,
body: JSON.stringify(body),
}).then(response => response.json());$client = new \GuzzleHttp\Client();
$url = 'https://api.shlott.com/v1/manage/calendar-settings';
$response = $client->put(
$url,
[
'headers' => [
'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
'Content-Type' => 'application/json',
'Accept' => 'application/json',
],
'json' => [
'group_id' => 5,
'calendar_intervals' => 2,
'hide_member_details_on_calendar' => false,
'hide_unavailable_all_day_rows' => false,
'flag_booking_if_member_has_not_booked_in' => 0,
'week_starts_on' => 1,
'default_overview_object' => '0',
'overview_amber_limit' => 3,
'overview_red_limit' => 5,
],
]
);
$body = $response->getBody();
print_r(json_decode((string) $body));import requests
import json
url = 'https://api.shlott.com/v1/manage/calendar-settings'
payload = {
"group_id": 5,
"calendar_intervals": 2,
"hide_member_details_on_calendar": false,
"hide_unavailable_all_day_rows": false,
"flag_booking_if_member_has_not_booked_in": 0,
"week_starts_on": 1,
"default_overview_object": "0",
"overview_amber_limit": 3,
"overview_red_limit": 5
}
headers = {
'Authorization': 'Bearer {YOUR_AUTH_KEY}',
'Content-Type': 'application/json',
'Accept': 'application/json'
}
response = requests.request('PUT', url, headers=headers, json=payload)
response.json()Example response (200):
{
"message": "Settings updated."
}
Example response (403, Not a group admin):
{
"message": "Forbidden."
}
Example response (422, Invalid):
{
"message": "The overview red limit field must be greater than overview amber limit.",
"errors": {
"overview_red_limit": [
"..."
]
}
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Get Calendar Day
requires authentication
Returns all resources and role members (e.g. instructors) for the specified group on the given date, together with their bookings and unavailable time blocks. The response also includes the group's opening hours and minimum booking duration for that day so a client can render a full calendar grid without making additional requests.
Resources and role members that are marked as not visible, or whose availability
record shows them as off for the requested day, are omitted. Supply resource_id
to narrow the result to a single row.
When the group hides member details on the calendar, a non-admin sees "Booked" as the
slot_text, and no booked_for, reason or note, on bookings they are not involved in
(they did not create it, and it is not booked for, booked with or shared with them). Admins
always see everything. Requests are limited to 60 a minute per user.
Example request:
const url = new URL(
"https://api.shlott.com/v1/calendar/day"
);
const params = {
"date": "2025-06-21",
"group_id": "5",
"resource_id": "12",
"show_all_rows": "0",
};
Object.keys(params)
.forEach(key => url.searchParams.append(key, params[key]));
const headers = {
"Authorization": "Bearer {YOUR_AUTH_KEY}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());$client = new \GuzzleHttp\Client();
$url = 'https://api.shlott.com/v1/calendar/day';
$response = $client->get(
$url,
[
'headers' => [
'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
'Content-Type' => 'application/json',
'Accept' => 'application/json',
],
'query' => [
'date' => '2025-06-21',
'group_id' => '5',
'resource_id' => '12',
'show_all_rows' => '0',
],
]
);
$body = $response->getBody();
print_r(json_decode((string) $body));import requests
import json
url = 'https://api.shlott.com/v1/calendar/day'
params = {
'date': '2025-06-21',
'group_id': '5',
'resource_id': '12',
'show_all_rows': '0',
}
headers = {
'Authorization': 'Bearer {YOUR_AUTH_KEY}',
'Content-Type': 'application/json',
'Accept': 'application/json'
}
response = requests.request('GET', url, headers=headers, params=params)
response.json()Example response (200, Day returned successfully):
{
"date": "2025-06-21",
"day_start": "08:00",
"day_end": "18:00",
"minimum_booking_duration": 30,
"slot_minutes": 15,
"closed": false,
"rows_hidden": false,
"resources": [
{
"id": 3,
"name": "G-ABCD",
"type": "resource",
"category_id": 4,
"category_name": "Aircraft",
"subheading": "Cessna 172",
"expiry_warning": null,
"color": "#269DD9",
"available_from": "08:00",
"available_to": "18:00",
"bookings": [
{
"id": 120,
"start": "09:00",
"end": "11:00",
"color": "#269DD9",
"slot_text": "Jane Smith with John Doe",
"booked_for": "Jane Smith",
"booked_for_member_id": 12,
"reason": "Trial Flight",
"note": "First lesson",
"resource_unavailable": false,
"booking_overlaps_yesterday": false,
"booking_overlaps_tomorrow": false,
"im_booked": false,
"im_shared": false
}
]
}
]
}
Example response (401, Unauthenticated: no or invalid API key):
{
"message": "Unauthenticated."
}
Example response (403, Authenticated user is not a member of this group):
{
"message": "Forbidden."
}
Example response (422, Validation error):
{
"message": "The date field is required.",
"errors": {
"date": [
"The date field is required."
]
}
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Get Calendar Week
requires authentication
The website's week view: every visible resource and role member for the group with the
bookings of the seven days of the week containing date. The week starts on the group's
week_starts_on day (0 Sunday, 1 Monday). Built by the same code as the website
(BookingModule::getWeekBookings), so hidden member details, multi-day bookings and
days off behave identically. off days only apply to Pro groups.
Bookings carry hidden: true when the group hides member details and the caller may not
see them (GET /booking/{id} answers 403 for those).
Example request:
const url = new URL(
"https://api.shlott.com/v1/calendar/week"
);
const params = {
"date": "2025-06-21",
"group_id": "5",
};
Object.keys(params)
.forEach(key => url.searchParams.append(key, params[key]));
const headers = {
"Authorization": "Bearer {YOUR_AUTH_KEY}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());$client = new \GuzzleHttp\Client();
$url = 'https://api.shlott.com/v1/calendar/week';
$response = $client->get(
$url,
[
'headers' => [
'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
'Content-Type' => 'application/json',
'Accept' => 'application/json',
],
'query' => [
'date' => '2025-06-21',
'group_id' => '5',
],
]
);
$body = $response->getBody();
print_r(json_decode((string) $body));import requests
import json
url = 'https://api.shlott.com/v1/calendar/week'
params = {
'date': '2025-06-21',
'group_id': '5',
}
headers = {
'Authorization': 'Bearer {YOUR_AUTH_KEY}',
'Content-Type': 'application/json',
'Accept': 'application/json'
}
response = requests.request('GET', url, headers=headers, params=params)
response.json()Example response (200, Week returned successfully):
{
"week_starts_on": 1,
"start_date": "2025-06-16",
"end_date": "2025-06-22",
"days": [
"2025-06-16",
"2025-06-17",
"2025-06-18",
"2025-06-19",
"2025-06-20",
"2025-06-21",
"2025-06-22"
],
"categories": [
{
"id": 1,
"name": "Aircraft",
"type": "resource",
"rows": [
{
"id": 3,
"name": "G-ABCD",
"type": "resource",
"subheading": "Cessna 172",
"color": "#3366cc",
"expiry_warning": null,
"days": [
{
"date": "2025-06-16",
"off": false,
"bookings": [
{
"id": 101,
"all_day_unavailable": false,
"start": "09:00",
"label": "Jane Smith",
"resource_unavailable": false,
"hidden": false,
"booking_overlaps_yesterday": false,
"booking_overlaps_tomorrow": false
}
]
}
]
}
]
}
]
}
Example response (401, Unauthenticated: no or invalid API key):
{
"message": "Unauthenticated."
}
Example response (403, Authenticated user is not a member of this group):
{
"message": "Forbidden."
}
Example response (422, Validation error):
{
"message": "The date field is required.",
"errors": {
"date": [
"The date field is required."
]
}
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Get Calendar Month
requires authentication
The website's month overview (/calendar/month): for one resource or role member, the
hours booked on each day of the month, with the amber/red limits that colour them. Built
by the same code as the website (BookingModule::objectBookingHours).
objects lists what can be picked (value is resource_12 or member_5). When no
object is sent the group's default overview object is used, or the only object if there
is just one; otherwise selected is null and days is empty. names follows the group's
"hide member information" setting; when names are hidden count is the number of bookings.
Example request:
const url = new URL(
"https://api.shlott.com/v1/calendar/month"
);
const params = {
"date": "2025-06-21",
"group_id": "5",
"object": "resource_12",
};
Object.keys(params)
.forEach(key => url.searchParams.append(key, params[key]));
const headers = {
"Authorization": "Bearer {YOUR_AUTH_KEY}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());$client = new \GuzzleHttp\Client();
$url = 'https://api.shlott.com/v1/calendar/month';
$response = $client->get(
$url,
[
'headers' => [
'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
'Content-Type' => 'application/json',
'Accept' => 'application/json',
],
'query' => [
'date' => '2025-06-21',
'group_id' => '5',
'object' => 'resource_12',
],
]
);
$body = $response->getBody();
print_r(json_decode((string) $body));import requests
import json
url = 'https://api.shlott.com/v1/calendar/month'
params = {
'date': '2025-06-21',
'group_id': '5',
'object': 'resource_12',
}
headers = {
'Authorization': 'Bearer {YOUR_AUTH_KEY}',
'Content-Type': 'application/json',
'Accept': 'application/json'
}
response = requests.request('GET', url, headers=headers, params=params)
response.json()Example response (200, Month returned successfully):
{
"month": "2025-06",
"week_starts_on": 1,
"amber_limit": 6,
"red_limit": 10,
"objects": [
{
"value": "resource_12",
"name": "G-ABCD",
"category_name": "Aircraft"
}
],
"selected": "resource_12",
"subheading": "Cessna 172",
"expiry_warning": null,
"days": [
{
"date": "2025-06-01",
"hours": 0,
"count": 0,
"names": [],
"entries": [],
"level": "not-busy"
},
{
"date": "2025-06-02",
"hours": 7.5,
"count": 2,
"names": [
"Jane Smith",
"John Doe"
],
"entries": [],
"level": "busy"
}
]
}
Example response (401, Unauthenticated: no or invalid API key):
{
"message": "Unauthenticated."
}
Example response (403, Authenticated user is not a member of this group):
{
"message": "Forbidden."
}
Example response (422, Validation error):
{
"message": "The date field is required.",
"errors": {
"date": [
"The date field is required."
]
}
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
iCal
Calendar Sync
requires authentication
The website's Calendar Sync page: the user's private subscription link (feed_url, to add
to Google, Apple or Microsoft calendars) or has_link: false when none exists yet.
masked_url is the same link with the secret hidden, for showing on screen.
Example request:
const url = new URL(
"https://api.shlott.com/v1/user/calendar-sync"
);
const headers = {
"Authorization": "Bearer {YOUR_AUTH_KEY}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());$client = new \GuzzleHttp\Client();
$url = 'https://api.shlott.com/v1/user/calendar-sync';
$response = $client->get(
$url,
[
'headers' => [
'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
'Content-Type' => 'application/json',
'Accept' => 'application/json',
],
]
);
$body = $response->getBody();
print_r(json_decode((string) $body));import requests
import json
url = 'https://api.shlott.com/v1/user/calendar-sync'
headers = {
'Authorization': 'Bearer {YOUR_AUTH_KEY}',
'Content-Type': 'application/json',
'Accept': 'application/json'
}
response = requests.request('GET', url, headers=headers)
response.json()Example response (200):
{
"has_link": true,
"feed_url": "https://www.shlott.com/calendar/feed/abc.ics",
"masked_url": "https://www.shlott.com/calendar/feed/••••••••abcd.ics",
"last_accessed_at": "2026-10-28T09:00:00+00:00",
"last_accessed": "3 hours ago"
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Generate or Regenerate Calendar Link
requires authentication
Creates the link, or replaces it with a new one (the old link stops working at once).
Example request:
const url = new URL(
"https://api.shlott.com/v1/user/calendar-sync/regenerate"
);
const headers = {
"Authorization": "Bearer {YOUR_AUTH_KEY}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "POST",
headers,
}).then(response => response.json());$client = new \GuzzleHttp\Client();
$url = 'https://api.shlott.com/v1/user/calendar-sync/regenerate';
$response = $client->post(
$url,
[
'headers' => [
'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
'Content-Type' => 'application/json',
'Accept' => 'application/json',
],
]
);
$body = $response->getBody();
print_r(json_decode((string) $body));import requests
import json
url = 'https://api.shlott.com/v1/user/calendar-sync/regenerate'
headers = {
'Authorization': 'Bearer {YOUR_AUTH_KEY}',
'Content-Type': 'application/json',
'Accept': 'application/json'
}
response = requests.request('POST', url, headers=headers)
response.json()Example response (200):
{
"has_link": true,
"feed_url": "https://www.shlott.com/calendar/feed/abc.ics",
"masked_url": "https://www.shlott.com/calendar/feed/••••••••abcd.ics",
"last_accessed_at": null,
"last_accessed": null,
"message": "Your calendar link has been regenerated."
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Delete Calendar Link
requires authentication
Switches calendar syncing off and removes the link.
Example request:
const url = new URL(
"https://api.shlott.com/v1/user/calendar-sync"
);
const headers = {
"Authorization": "Bearer {YOUR_AUTH_KEY}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "DELETE",
headers,
}).then(response => response.json());$client = new \GuzzleHttp\Client();
$url = 'https://api.shlott.com/v1/user/calendar-sync';
$response = $client->delete(
$url,
[
'headers' => [
'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
'Content-Type' => 'application/json',
'Accept' => 'application/json',
],
]
);
$body = $response->getBody();
print_r(json_decode((string) $body));import requests
import json
url = 'https://api.shlott.com/v1/user/calendar-sync'
headers = {
'Authorization': 'Bearer {YOUR_AUTH_KEY}',
'Content-Type': 'application/json',
'Accept': 'application/json'
}
response = requests.request('DELETE', url, headers=headers)
response.json()Example response (200):
{
"has_link": false,
"feed_url": null,
"masked_url": null,
"last_accessed_at": null,
"last_accessed": null,
"message": "Your calendar link has been deleted."
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Booking Calendar Link
requires authentication
A short-lived signed URL for the booking's .ics file (the website's "add to calendar"
download), for the app to open so the phone offers to add it to the user's calendar.
Give end_id too for a recurring series just created (start_id / end_id from the
create response); a series only covers bookings the caller created.
Example request:
const url = new URL(
"https://api.shlott.com/v1/booking/564/calendar-link"
);
const params = {
"end_id": "1250",
};
Object.keys(params)
.forEach(key => url.searchParams.append(key, params[key]));
const headers = {
"Authorization": "Bearer {YOUR_AUTH_KEY}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());$client = new \GuzzleHttp\Client();
$url = 'https://api.shlott.com/v1/booking/564/calendar-link';
$response = $client->get(
$url,
[
'headers' => [
'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
'Content-Type' => 'application/json',
'Accept' => 'application/json',
],
'query' => [
'end_id' => '1250',
],
]
);
$body = $response->getBody();
print_r(json_decode((string) $body));import requests
import json
url = 'https://api.shlott.com/v1/booking/564/calendar-link'
params = {
'end_id': '1250',
}
headers = {
'Authorization': 'Bearer {YOUR_AUTH_KEY}',
'Content-Type': 'application/json',
'Accept': 'application/json'
}
response = requests.request('GET', url, headers=headers, params=params)
response.json()Example response (200):
{
"url": "https://example.test/signed"
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Documents
List Object Documents
requires authentication
The files on the website's object page (Pro groups only). Separate from Get Object because it asks Azure storage, which can be slow, so the app loads it after the page is up.
Example request:
const url = new URL(
"https://api.shlott.com/v1/objects/8/documents"
);
const params = {
"group_id": "5",
};
Object.keys(params)
.forEach(key => url.searchParams.append(key, params[key]));
const headers = {
"Authorization": "Bearer {YOUR_AUTH_KEY}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());$client = new \GuzzleHttp\Client();
$url = 'https://api.shlott.com/v1/objects/8/documents';
$response = $client->get(
$url,
[
'headers' => [
'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
'Content-Type' => 'application/json',
'Accept' => 'application/json',
],
'query' => [
'group_id' => '5',
],
]
);
$body = $response->getBody();
print_r(json_decode((string) $body));import requests
import json
url = 'https://api.shlott.com/v1/objects/8/documents'
params = {
'group_id': '5',
}
headers = {
'Authorization': 'Bearer {YOUR_AUTH_KEY}',
'Content-Type': 'application/json',
'Accept': 'application/json'
}
response = requests.request('GET', url, headers=headers, params=params)
response.json()Example response (200, Documents):
{
"pro": true,
"documents": [
{
"name": "Manual.pdf",
"size": 120394
}
]
}
Example response (200, Free group):
{
"pro": false,
"documents": []
}
Example response (403, Not a member):
{
"message": "Forbidden."
}
Example response (404):
{
"message": "Object not found."
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
List Group Documents
requires authentication
The group-wide files on the website's Group Documents page (Pro groups only), open to any member.
Example request:
const url = new URL(
"https://api.shlott.com/v1/groups/5/documents"
);
const headers = {
"Authorization": "Bearer {YOUR_AUTH_KEY}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());$client = new \GuzzleHttp\Client();
$url = 'https://api.shlott.com/v1/groups/5/documents';
$response = $client->get(
$url,
[
'headers' => [
'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
'Content-Type' => 'application/json',
'Accept' => 'application/json',
],
]
);
$body = $response->getBody();
print_r(json_decode((string) $body));import requests
import json
url = 'https://api.shlott.com/v1/groups/5/documents'
headers = {
'Authorization': 'Bearer {YOUR_AUTH_KEY}',
'Content-Type': 'application/json',
'Accept': 'application/json'
}
response = requests.request('GET', url, headers=headers)
response.json()Example response (200, Documents):
{
"pro": true,
"documents": [
{
"name": "Rules.pdf",
"size": 120394
}
]
}
Example response (200, Free group):
{
"pro": false,
"documents": []
}
Example response (403, Not a member):
{
"message": "Forbidden."
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Get Group Document Link
requires authentication
A short-lived signed URL for one group document. Pro groups only.
Example request:
const url = new URL(
"https://api.shlott.com/v1/groups/5/documents/link"
);
const params = {
"file": "Rules.pdf",
};
Object.keys(params)
.forEach(key => url.searchParams.append(key, params[key]));
const headers = {
"Authorization": "Bearer {YOUR_AUTH_KEY}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());$client = new \GuzzleHttp\Client();
$url = 'https://api.shlott.com/v1/groups/5/documents/link';
$response = $client->get(
$url,
[
'headers' => [
'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
'Content-Type' => 'application/json',
'Accept' => 'application/json',
],
'query' => [
'file' => 'Rules.pdf',
],
]
);
$body = $response->getBody();
print_r(json_decode((string) $body));import requests
import json
url = 'https://api.shlott.com/v1/groups/5/documents/link'
params = {
'file': 'Rules.pdf',
}
headers = {
'Authorization': 'Bearer {YOUR_AUTH_KEY}',
'Content-Type': 'application/json',
'Accept': 'application/json'
}
response = requests.request('GET', url, headers=headers, params=params)
response.json()Example response (200):
{
"url": "https://example.test/signed"
}
Example response (404):
{
"message": "Document not found."
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Get Document Link
requires authentication
A short-lived signed URL for one document, for the app to open in a browser view (the browser can't send the API key). Pro groups only.
Example request:
const url = new URL(
"https://api.shlott.com/v1/objects/8/documents/link"
);
const params = {
"group_id": "5",
"file": "Manual.pdf",
};
Object.keys(params)
.forEach(key => url.searchParams.append(key, params[key]));
const headers = {
"Authorization": "Bearer {YOUR_AUTH_KEY}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());$client = new \GuzzleHttp\Client();
$url = 'https://api.shlott.com/v1/objects/8/documents/link';
$response = $client->get(
$url,
[
'headers' => [
'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
'Content-Type' => 'application/json',
'Accept' => 'application/json',
],
'query' => [
'group_id' => '5',
'file' => 'Manual.pdf',
],
]
);
$body = $response->getBody();
print_r(json_decode((string) $body));import requests
import json
url = 'https://api.shlott.com/v1/objects/8/documents/link'
params = {
'group_id': '5',
'file': 'Manual.pdf',
}
headers = {
'Authorization': 'Bearer {YOUR_AUTH_KEY}',
'Content-Type': 'application/json',
'Accept': 'application/json'
}
response = requests.request('GET', url, headers=headers, params=params)
response.json()Example response (200):
{
"url": "https://api.example.com/v1/objects/8/documents/file?...&signature=..."
}
Example response (403, Not a member, or not a Pro group):
{
"message": "Forbidden."
}
Example response (404):
{
"message": "Document not found."
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Groups
Search Groups
Returns public groups whose name matches the given search term, along with their country and postcode so a new user can identify the right group before joining. The term is split on whitespace and every word must appear somewhere in the group name, in any order, so a search for "group test" also matches a group named "Test Group".
For security reasons, at most 5 groups are returned, best match first (exact name, then names starting with the term, then other matches, each alphabetical). There is no pagination, so a user must refine the search term to find a group that is not shown.
This endpoint is unauthenticated so it can be used during sign-up, before a user has an account or has joined any group.
Example request:
const url = new URL(
"https://api.shlott.com/v1/groups/search"
);
const params = {
"name": "group test",
};
Object.keys(params)
.forEach(key => url.searchParams.append(key, params[key]));
const headers = {
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());$client = new \GuzzleHttp\Client();
$url = 'https://api.shlott.com/v1/groups/search';
$response = $client->get(
$url,
[
'headers' => [
'Content-Type' => 'application/json',
'Accept' => 'application/json',
],
'query' => [
'name' => 'group test',
],
]
);
$body = $response->getBody();
print_r(json_decode((string) $body));import requests
import json
url = 'https://api.shlott.com/v1/groups/search'
params = {
'name': 'group test',
}
headers = {
'Content-Type': 'application/json',
'Accept': 'application/json'
}
response = requests.request('GET', url, headers=headers, params=params)
response.json()Example response (200, Groups found):
{
"groups": [
{
"id": 5,
"name": "Test Group",
"country": "United Kingdom",
"postcode": "EX1 1AA"
},
{
"id": 12,
"name": "My Test Group",
"country": "United Kingdom",
"postcode": "EX2 2BB"
}
]
}
Example response (200, No groups found):
{
"groups": []
}
Example response (422, Validation error):
{
"message": "The name field is required.",
"errors": {
"name": [
"The name field is required."
]
}
}
Example response (429, Too many searches (30 a minute per IP address)):
{
"message": "Too Many Attempts."
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
List Groups
requires authentication
Returns every group the authenticated user is an active member of. Use the group id
values with the endpoints that take a group_id.
Example request:
const url = new URL(
"https://api.shlott.com/v1/groups"
);
const headers = {
"Authorization": "Bearer {YOUR_AUTH_KEY}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());$client = new \GuzzleHttp\Client();
$url = 'https://api.shlott.com/v1/groups';
$response = $client->get(
$url,
[
'headers' => [
'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
'Content-Type' => 'application/json',
'Accept' => 'application/json',
],
]
);
$body = $response->getBody();
print_r(json_decode((string) $body));import requests
import json
url = 'https://api.shlott.com/v1/groups'
headers = {
'Authorization': 'Bearer {YOUR_AUTH_KEY}',
'Content-Type': 'application/json',
'Accept': 'application/json'
}
response = requests.request('GET', url, headers=headers)
response.json()Example response (200, Groups returned successfully):
{
"groups": [
{
"id": 5,
"name": "Anytown Flying Club",
"postcode": "EX1 1AA",
"pro": true
}
]
}
Example response (401, Unauthenticated: no or invalid API key):
{
"message": "Unauthenticated."
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Leave Group
requires authentication
Removes the authenticated user's membership of a group. A group's only administrator cannot leave: another admin must be assigned first. If the group was the user's primary group, another group they belong to becomes primary.
Example request:
const url = new URL(
"https://api.shlott.com/v1/groups/5"
);
const headers = {
"Authorization": "Bearer {YOUR_AUTH_KEY}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "DELETE",
headers,
}).then(response => response.json());$client = new \GuzzleHttp\Client();
$url = 'https://api.shlott.com/v1/groups/5';
$response = $client->delete(
$url,
[
'headers' => [
'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
'Content-Type' => 'application/json',
'Accept' => 'application/json',
],
]
);
$body = $response->getBody();
print_r(json_decode((string) $body));import requests
import json
url = 'https://api.shlott.com/v1/groups/5'
headers = {
'Authorization': 'Bearer {YOUR_AUTH_KEY}',
'Content-Type': 'application/json',
'Accept': 'application/json'
}
response = requests.request('DELETE', url, headers=headers)
response.json()Example response (200, Left the group):
{
"remaining_groups": 1
}
Example response (401, Unauthenticated: no or invalid API key):
{
"message": "Unauthenticated."
}
Example response (404, Not a member of the group):
{
"message": "You are not a member of this group."
}
Example response (422, Only admin):
{
"message": "Please assign another admin before leaving this group."
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Manage Group Overview
requires authentication
Whether the caller can manage the group (any member may ask) and, for admins, how many objects, categories and roles it has.
Example request:
const url = new URL(
"https://api.shlott.com/v1/manage"
);
const params = {
"group_id": "5",
};
Object.keys(params)
.forEach(key => url.searchParams.append(key, params[key]));
const headers = {
"Authorization": "Bearer {YOUR_AUTH_KEY}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());$client = new \GuzzleHttp\Client();
$url = 'https://api.shlott.com/v1/manage';
$response = $client->get(
$url,
[
'headers' => [
'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
'Content-Type' => 'application/json',
'Accept' => 'application/json',
],
'query' => [
'group_id' => '5',
],
]
);
$body = $response->getBody();
print_r(json_decode((string) $body));import requests
import json
url = 'https://api.shlott.com/v1/manage'
params = {
'group_id': '5',
}
headers = {
'Authorization': 'Bearer {YOUR_AUTH_KEY}',
'Content-Type': 'application/json',
'Accept': 'application/json'
}
response = requests.request('GET', url, headers=headers, params=params)
response.json()Example response (200, Admin):
{
"can_manage": true,
"objects": 12,
"categories": 3,
"roles": 2
}
Example response (200, Not an admin):
{
"can_manage": false
}
Example response (403, Not a member):
{
"message": "Forbidden."
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Delete Group: Check
requires authentication
What the app shows before the Delete Group form: the group's name and, if it still has an
active Pro subscription, blocked: true with the reason (the subscription must be cancelled
first, as on the website).
Example request:
const url = new URL(
"https://api.shlott.com/v1/manage/delete-group"
);
const params = {
"group_id": "5",
};
Object.keys(params)
.forEach(key => url.searchParams.append(key, params[key]));
const headers = {
"Authorization": "Bearer {YOUR_AUTH_KEY}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());$client = new \GuzzleHttp\Client();
$url = 'https://api.shlott.com/v1/manage/delete-group';
$response = $client->get(
$url,
[
'headers' => [
'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
'Content-Type' => 'application/json',
'Accept' => 'application/json',
],
'query' => [
'group_id' => '5',
],
]
);
$body = $response->getBody();
print_r(json_decode((string) $body));import requests
import json
url = 'https://api.shlott.com/v1/manage/delete-group'
params = {
'group_id': '5',
}
headers = {
'Authorization': 'Bearer {YOUR_AUTH_KEY}',
'Content-Type': 'application/json',
'Accept': 'application/json'
}
response = requests.request('GET', url, headers=headers, params=params)
response.json()Example response (200):
{
"name": "Flying Club",
"blocked": false,
"message": null
}
Example response (403, Not a group admin):
{
"message": "Forbidden."
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Delete Group
requires authentication
Permanently deletes the group with its bookings, objects and members. The website's rules: administrators only, the group's name and the admin's password must be typed, a reason is required, and an active Pro subscription must be cancelled first. Cannot be undone.
Example request:
const url = new URL(
"https://api.shlott.com/v1/manage/group"
);
const headers = {
"Authorization": "Bearer {YOUR_AUTH_KEY}",
"Content-Type": "application/json",
"Accept": "application/json",
};
let body = {
"group_id": 5,
"group_name": "Flying Club",
"password": "secret123",
"reason": "Club closed",
"also_delete_account": true
};
fetch(url, {
method: "DELETE",
headers,
body: JSON.stringify(body),
}).then(response => response.json());$client = new \GuzzleHttp\Client();
$url = 'https://api.shlott.com/v1/manage/group';
$response = $client->delete(
$url,
[
'headers' => [
'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
'Content-Type' => 'application/json',
'Accept' => 'application/json',
],
'json' => [
'group_id' => 5,
'group_name' => 'Flying Club',
'password' => 'secret123',
'reason' => 'Club closed',
'also_delete_account' => true,
],
]
);
$body = $response->getBody();
print_r(json_decode((string) $body));import requests
import json
url = 'https://api.shlott.com/v1/manage/group'
payload = {
"group_id": 5,
"group_name": "Flying Club",
"password": "secret123",
"reason": "Club closed",
"also_delete_account": true
}
headers = {
'Authorization': 'Bearer {YOUR_AUTH_KEY}',
'Content-Type': 'application/json',
'Accept': 'application/json'
}
response = requests.request('DELETE', url, headers=headers, json=payload)
response.json()Example response (200):
{
"message": "Group Successfully Deleted"
}
Example response (403, Not a group admin):
{
"message": "Forbidden."
}
Example response (422, Wrong name or password):
{
"message": "Invalid group name provided.",
"errors": {
"group_name": [
"Invalid group name provided."
]
}
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Members
Get Group Member
requires authentication
Returns the complete profile for a single member of the specified group. This includes their personal details (name, email, phone), postal address, emergency contact information, their permission level within the group (Read Only, Member, or Admin), any role they hold (e.g. Instructor, Examiner), and any custom fields the group administrator has defined for members.
The authenticated user must themselves be an active member of the same group to access this information. If they are not a member of that group, a 403 Forbidden response is returned. Custom fields that have been deleted by the group administrator are automatically excluded from the response.
Non-admins also get a 403 when the group hides its member list from non-admins, or when that member has chosen to hide their details from other members.
Example request:
const url = new URL(
"https://api.shlott.com/v1/group_member/5/12"
);
const headers = {
"Authorization": "Bearer {YOUR_AUTH_KEY}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());$client = new \GuzzleHttp\Client();
$url = 'https://api.shlott.com/v1/group_member/5/12';
$response = $client->get(
$url,
[
'headers' => [
'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
'Content-Type' => 'application/json',
'Accept' => 'application/json',
],
]
);
$body = $response->getBody();
print_r(json_decode((string) $body));import requests
import json
url = 'https://api.shlott.com/v1/group_member/5/12'
headers = {
'Authorization': 'Bearer {YOUR_AUTH_KEY}',
'Content-Type': 'application/json',
'Accept': 'application/json'
}
response = requests.request('GET', url, headers=headers)
response.json()Example response (200, Member found):
{
"first_name": "Jane",
"last_name": "Smith",
"email": "[email protected]",
"phone": "01234 567890",
"mobile": "07700 900000",
"permission_level": "Member",
"role": "Instructor",
"address_1": "10 Runway Lane",
"address_2": null,
"address_3": null,
"city": "Anytown",
"state": "Exampleshire",
"postcode": "EX1 1AA",
"country": "United Kingdom",
"emergency_contact_name": "Bob Smith",
"emergency_contact_relationship": "Spouse",
"emergency_contact_number": "07700 900001",
"custom_fields": [
{
"id": "field_abc123",
"name": "Licence Number",
"type": "text",
"value": "PPL/12345"
}
],
"notes": "Prefers morning slots."
}
Example response (401, Unauthenticated — no or invalid API key):
{
"message": "Unauthenticated."
}
Example response (403, Authenticated user is not a member of this group, or the profile is private):
{
"message": "Forbidden."
}
Example response (404, No member with this ID exists in this group):
{
"message": "Member not found."
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
List Members
requires authentication
One page of the group's members, searchable by name, email or role. Non-admins get a 403
when the group hides its member list from them, and never see members who hide their
details. status is expired or expiring when one of the member's expiry-date custom
fields is past or within 30 days; can_manage says whether the caller is a group admin.
Example request:
const url = new URL(
"https://api.shlott.com/v1/members"
);
const params = {
"group_id": "5",
"search": "architecto",
"page": "1",
"per_page": "25",
};
Object.keys(params)
.forEach(key => url.searchParams.append(key, params[key]));
const headers = {
"Authorization": "Bearer {YOUR_AUTH_KEY}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());$client = new \GuzzleHttp\Client();
$url = 'https://api.shlott.com/v1/members';
$response = $client->get(
$url,
[
'headers' => [
'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
'Content-Type' => 'application/json',
'Accept' => 'application/json',
],
'query' => [
'group_id' => '5',
'search' => 'architecto',
'page' => '1',
'per_page' => '25',
],
]
);
$body = $response->getBody();
print_r(json_decode((string) $body));import requests
import json
url = 'https://api.shlott.com/v1/members'
params = {
'group_id': '5',
'search': 'architecto',
'page': '1',
'per_page': '25',
}
headers = {
'Authorization': 'Bearer {YOUR_AUTH_KEY}',
'Content-Type': 'application/json',
'Accept': 'application/json'
}
response = requests.request('GET', url, headers=headers, params=params)
response.json()Example response (200, Page of members):
{
"data": [
{
"id": 12,
"first_name": "Jane",
"last_name": "Smith",
"email": "[email protected]",
"role": "Instructor",
"permission_level": "Member",
"status": null
}
],
"total": 41,
"page": 1,
"per_page": 25,
"has_more": true,
"can_manage": true,
"removed_count": 2
}
Example response (403, Not a member, or the group hides its member list):
{
"message": "Forbidden."
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Member Form Options
requires authentication
What the add and edit forms need: the group's roles, countries, the time choices for role availability, whether the group has a paid subscription (per-day availability) and whether the free member limit has been reached.
Example request:
const url = new URL(
"https://api.shlott.com/v1/members/options"
);
const params = {
"group_id": "5",
};
Object.keys(params)
.forEach(key => url.searchParams.append(key, params[key]));
const headers = {
"Authorization": "Bearer {YOUR_AUTH_KEY}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());$client = new \GuzzleHttp\Client();
$url = 'https://api.shlott.com/v1/members/options';
$response = $client->get(
$url,
[
'headers' => [
'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
'Content-Type' => 'application/json',
'Accept' => 'application/json',
],
'query' => [
'group_id' => '5',
],
]
);
$body = $response->getBody();
print_r(json_decode((string) $body));import requests
import json
url = 'https://api.shlott.com/v1/members/options'
params = {
'group_id': '5',
}
headers = {
'Authorization': 'Bearer {YOUR_AUTH_KEY}',
'Content-Type': 'application/json',
'Accept': 'application/json'
}
response = requests.request('GET', url, headers=headers, params=params)
response.json()Example response (200, Options):
{
"pro": true,
"date_format": "DMY",
"free_limit_reached": false,
"roles": [
{
"id": 2,
"name": "Instructor",
"visible": true
}
],
"countries": [
{
"code": "GB",
"name": "United Kingdom"
}
],
"times": [
"08:00",
"08:30"
]
}
Example response (403, Not a group admin):
{
"message": "Forbidden."
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Removed Members
requires authentication
Members who left or were removed (archived), who an admin can restore or delete for good.
Example request:
const url = new URL(
"https://api.shlott.com/v1/members/removed"
);
const params = {
"group_id": "5",
};
Object.keys(params)
.forEach(key => url.searchParams.append(key, params[key]));
const headers = {
"Authorization": "Bearer {YOUR_AUTH_KEY}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());$client = new \GuzzleHttp\Client();
$url = 'https://api.shlott.com/v1/members/removed';
$response = $client->get(
$url,
[
'headers' => [
'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
'Content-Type' => 'application/json',
'Accept' => 'application/json',
],
'query' => [
'group_id' => '5',
],
]
);
$body = $response->getBody();
print_r(json_decode((string) $body));import requests
import json
url = 'https://api.shlott.com/v1/members/removed'
params = {
'group_id': '5',
}
headers = {
'Authorization': 'Bearer {YOUR_AUTH_KEY}',
'Content-Type': 'application/json',
'Accept': 'application/json'
}
response = requests.request('GET', url, headers=headers, params=params)
response.json()Example response (200, Removed members):
{
"data": [
{
"id": 9,
"name": "Bob Jones",
"email": "[email protected]",
"role": null,
"permission_level": "Member",
"reason": "left"
}
]
}
Example response (403, Not a group admin):
{
"message": "Forbidden."
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Expiring Members
requires authentication
Members with an expiry-date custom field that has passed or ends within 30 days.
Example request:
const url = new URL(
"https://api.shlott.com/v1/members/expiring"
);
const params = {
"group_id": "5",
};
Object.keys(params)
.forEach(key => url.searchParams.append(key, params[key]));
const headers = {
"Authorization": "Bearer {YOUR_AUTH_KEY}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());$client = new \GuzzleHttp\Client();
$url = 'https://api.shlott.com/v1/members/expiring';
$response = $client->get(
$url,
[
'headers' => [
'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
'Content-Type' => 'application/json',
'Accept' => 'application/json',
],
'query' => [
'group_id' => '5',
],
]
);
$body = $response->getBody();
print_r(json_decode((string) $body));import requests
import json
url = 'https://api.shlott.com/v1/members/expiring'
params = {
'group_id': '5',
}
headers = {
'Authorization': 'Bearer {YOUR_AUTH_KEY}',
'Content-Type': 'application/json',
'Accept': 'application/json'
}
response = requests.request('GET', url, headers=headers, params=params)
response.json()Example response (200, Expiring members):
{
"data": [
{
"id": 12,
"name": "Jane Smith",
"email": "[email protected]",
"status": "expiring"
}
]
}
Example response (403, Not a group admin):
{
"message": "Forbidden."
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Check Email Before Adding
requires authentication
The first step of adding a member, as on the website: what happens if this email is added.
status is new (the app asks for name, permission and role), existing (they already
have a Shlott account, which is added as it is), removed (they were in this group before
and can be restored), member (already in the group) or blacklisted.
Example request:
const url = new URL(
"https://api.shlott.com/v1/members/check-email"
);
const headers = {
"Authorization": "Bearer {YOUR_AUTH_KEY}",
"Content-Type": "application/json",
"Accept": "application/json",
};
let body = {
"group_id": 5,
"email": "[email protected]"
};
fetch(url, {
method: "POST",
headers,
body: JSON.stringify(body),
}).then(response => response.json());$client = new \GuzzleHttp\Client();
$url = 'https://api.shlott.com/v1/members/check-email';
$response = $client->post(
$url,
[
'headers' => [
'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
'Content-Type' => 'application/json',
'Accept' => 'application/json',
],
'json' => [
'group_id' => 5,
'email' => '[email protected]',
],
]
);
$body = $response->getBody();
print_r(json_decode((string) $body));import requests
import json
url = 'https://api.shlott.com/v1/members/check-email'
payload = {
"group_id": 5,
"email": "[email protected]"
}
headers = {
'Authorization': 'Bearer {YOUR_AUTH_KEY}',
'Content-Type': 'application/json',
'Accept': 'application/json'
}
response = requests.request('POST', url, headers=headers, json=payload)
response.json()Example response (200, Existing Shlott user):
{
"status": "existing",
"first_name": "Jane",
"last_name": "Smith",
"message": "..."
}
Example response (403, Not a group admin):
{
"message": "Forbidden."
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Add Member
requires authentication
Adds a member to the group: someone with an existing Shlott account keeps their own details,
and a new email address creates the account (the member gets a welcome email to set their
password). A previous member is reinstated. Groups on the free plan are limited in size.
To create a role at the same time send new_role (a name) instead of role.
Example request:
const url = new URL(
"https://api.shlott.com/v1/members"
);
const headers = {
"Authorization": "Bearer {YOUR_AUTH_KEY}",
"Content-Type": "application/json",
"Accept": "application/json",
};
let body = {
"group_id": 5,
"email": "[email protected]",
"first_name": "architecto",
"last_name": "architecto",
"permission_level": 2,
"role": 16,
"new_role": "architecto"
};
fetch(url, {
method: "POST",
headers,
body: JSON.stringify(body),
}).then(response => response.json());$client = new \GuzzleHttp\Client();
$url = 'https://api.shlott.com/v1/members';
$response = $client->post(
$url,
[
'headers' => [
'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
'Content-Type' => 'application/json',
'Accept' => 'application/json',
],
'json' => [
'group_id' => 5,
'email' => '[email protected]',
'first_name' => 'architecto',
'last_name' => 'architecto',
'permission_level' => 2,
'role' => 16,
'new_role' => 'architecto',
],
]
);
$body = $response->getBody();
print_r(json_decode((string) $body));import requests
import json
url = 'https://api.shlott.com/v1/members'
payload = {
"group_id": 5,
"email": "[email protected]",
"first_name": "architecto",
"last_name": "architecto",
"permission_level": 2,
"role": 16,
"new_role": "architecto"
}
headers = {
'Authorization': 'Bearer {YOUR_AUTH_KEY}',
'Content-Type': 'application/json',
'Accept': 'application/json'
}
response = requests.request('POST', url, headers=headers, json=payload)
response.json()Example response (201, Added):
{
"message": "Member added.",
"id": 31
}
Example response (403, Not a group admin):
{
"message": "Forbidden."
}
Example response (422, Refused):
{
"message": "...",
"errors": {
"email": [
"..."
]
}
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Get Member For Editing
requires authentication
A member's complete editable record: details, role and permission, availability (when the
role has any), address, emergency contact, the group's custom fields with their values, and
notes. Group admins only; everyone else uses Get Group Member. Dates are YYYY-MM-DD and
times HH:MM. A member with no email address (editable false) cannot be changed, as on the
website.
Example request:
const url = new URL(
"https://api.shlott.com/v1/members/12"
);
const params = {
"group_id": "5",
};
Object.keys(params)
.forEach(key => url.searchParams.append(key, params[key]));
const headers = {
"Authorization": "Bearer {YOUR_AUTH_KEY}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());$client = new \GuzzleHttp\Client();
$url = 'https://api.shlott.com/v1/members/12';
$response = $client->get(
$url,
[
'headers' => [
'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
'Content-Type' => 'application/json',
'Accept' => 'application/json',
],
'query' => [
'group_id' => '5',
],
]
);
$body = $response->getBody();
print_r(json_decode((string) $body));import requests
import json
url = 'https://api.shlott.com/v1/members/12'
params = {
'group_id': '5',
}
headers = {
'Authorization': 'Bearer {YOUR_AUTH_KEY}',
'Content-Type': 'application/json',
'Accept': 'application/json'
}
response = requests.request('GET', url, headers=headers, params=params)
response.json()Example response (401):
Show headers
cache-control: no-cache, private
content-type: application/json
{
"message": "Unauthenticated."
}
Example response (403, Not a group admin):
{
"message": "Forbidden."
}
Example response (404, No such member):
{
"message": "Member not found."
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Update Member
requires authentication
Saves the edit form. The email address cannot be changed. Sending role as null removes the
role (and its availability). availability only applies when the member has a role; per-day
rows are used on paid groups and everyday start/end otherwise. custom_fields is keyed by
field id; checkbox fields take an array of the ticked options, dates are YYYY-MM-DD, and a
field left out keeps its value.
Example request:
const url = new URL(
"https://api.shlott.com/v1/members/12"
);
const headers = {
"Authorization": "Bearer {YOUR_AUTH_KEY}",
"Content-Type": "application/json",
"Accept": "application/json",
};
let body = {
"group_id": 5,
"first_name": "architecto",
"last_name": "architecto",
"permission_level": 16,
"role": 16,
"color": "architecto",
"note": "architecto"
};
fetch(url, {
method: "PUT",
headers,
body: JSON.stringify(body),
}).then(response => response.json());$client = new \GuzzleHttp\Client();
$url = 'https://api.shlott.com/v1/members/12';
$response = $client->put(
$url,
[
'headers' => [
'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
'Content-Type' => 'application/json',
'Accept' => 'application/json',
],
'json' => [
'group_id' => 5,
'first_name' => 'architecto',
'last_name' => 'architecto',
'permission_level' => 16,
'role' => 16,
'color' => 'architecto',
'note' => 'architecto',
],
]
);
$body = $response->getBody();
print_r(json_decode((string) $body));import requests
import json
url = 'https://api.shlott.com/v1/members/12'
payload = {
"group_id": 5,
"first_name": "architecto",
"last_name": "architecto",
"permission_level": 16,
"role": 16,
"color": "architecto",
"note": "architecto"
}
headers = {
'Authorization': 'Bearer {YOUR_AUTH_KEY}',
'Content-Type': 'application/json',
'Accept': 'application/json'
}
response = requests.request('PUT', url, headers=headers, json=payload)
response.json()Example response (200, Saved):
{
"message": "Member details updated."
}
Example response (403, Not a group admin):
{
"message": "Forbidden."
}
Example response (404, No such member):
{
"message": "Member not found."
}
Example response (422, Refused):
{
"message": "...",
"errors": {}
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Archive Member
requires authentication
Removes a member from the group but keeps their record, so they can be restored later. Admins cannot archive themselves or the group's last admin.
Example request:
const url = new URL(
"https://api.shlott.com/v1/members/12/archive"
);
const headers = {
"Authorization": "Bearer {YOUR_AUTH_KEY}",
"Content-Type": "application/json",
"Accept": "application/json",
};
let body = {
"group_id": 5
};
fetch(url, {
method: "POST",
headers,
body: JSON.stringify(body),
}).then(response => response.json());$client = new \GuzzleHttp\Client();
$url = 'https://api.shlott.com/v1/members/12/archive';
$response = $client->post(
$url,
[
'headers' => [
'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
'Content-Type' => 'application/json',
'Accept' => 'application/json',
],
'json' => [
'group_id' => 5,
],
]
);
$body = $response->getBody();
print_r(json_decode((string) $body));import requests
import json
url = 'https://api.shlott.com/v1/members/12/archive'
payload = {
"group_id": 5
}
headers = {
'Authorization': 'Bearer {YOUR_AUTH_KEY}',
'Content-Type': 'application/json',
'Accept': 'application/json'
}
response = requests.request('POST', url, headers=headers, json=payload)
response.json()Example response (200, Archived):
{
"message": "Member removed."
}
Example response (403, Not a group admin):
{
"message": "Forbidden."
}
Example response (422, Refused):
{
"message": "..."
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Restore Member
requires authentication
Brings a removed member back and sends them the email that matches their account state. Counts towards the free plan's member limit.
Example request:
const url = new URL(
"https://api.shlott.com/v1/members/12/restore"
);
const headers = {
"Authorization": "Bearer {YOUR_AUTH_KEY}",
"Content-Type": "application/json",
"Accept": "application/json",
};
let body = {
"group_id": 5
};
fetch(url, {
method: "POST",
headers,
body: JSON.stringify(body),
}).then(response => response.json());$client = new \GuzzleHttp\Client();
$url = 'https://api.shlott.com/v1/members/12/restore';
$response = $client->post(
$url,
[
'headers' => [
'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
'Content-Type' => 'application/json',
'Accept' => 'application/json',
],
'json' => [
'group_id' => 5,
],
]
);
$body = $response->getBody();
print_r(json_decode((string) $body));import requests
import json
url = 'https://api.shlott.com/v1/members/12/restore'
payload = {
"group_id": 5
}
headers = {
'Authorization': 'Bearer {YOUR_AUTH_KEY}',
'Content-Type': 'application/json',
'Accept': 'application/json'
}
response = requests.request('POST', url, headers=headers, json=payload)
response.json()Example response (200, Restored):
{
"message": "Member restored."
}
Example response (403, Not a group admin):
{
"message": "Forbidden."
}
Example response (404, No such member):
{
"message": "Member not found."
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Delete Member Impact
requires authentication
What permanently deleting a member would remove, for the confirmation screen.
Example request:
const url = new URL(
"https://api.shlott.com/v1/members/12/delete-impact"
);
const params = {
"group_id": "5",
};
Object.keys(params)
.forEach(key => url.searchParams.append(key, params[key]));
const headers = {
"Authorization": "Bearer {YOUR_AUTH_KEY}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());$client = new \GuzzleHttp\Client();
$url = 'https://api.shlott.com/v1/members/12/delete-impact';
$response = $client->get(
$url,
[
'headers' => [
'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
'Content-Type' => 'application/json',
'Accept' => 'application/json',
],
'query' => [
'group_id' => '5',
],
]
);
$body = $response->getBody();
print_r(json_decode((string) $body));import requests
import json
url = 'https://api.shlott.com/v1/members/12/delete-impact'
params = {
'group_id': '5',
}
headers = {
'Authorization': 'Bearer {YOUR_AUTH_KEY}',
'Content-Type': 'application/json',
'Accept': 'application/json'
}
response = requests.request('GET', url, headers=headers, params=params)
response.json()Example response (200, Impact):
{
"name": "Bob Jones",
"bookings": 14,
"future_bookings": 2,
"shared_bookings": 1,
"attended_bookings": 0,
"training_records": 0,
"tickets": 0
}
Example response (403, Not a group admin):
{
"message": "Forbidden."
}
Example response (422, Cannot be deleted):
{
"message": "..."
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Delete Member Permanently
requires authentication
Permanently deletes a member and what goes with them (their bookings, preferences, training records and tickets). Cannot be undone, so the admin must type the member's full name and their own password, as on the website. Admins cannot delete themselves or the last admin.
Example request:
const url = new URL(
"https://api.shlott.com/v1/members/12"
);
const headers = {
"Authorization": "Bearer {YOUR_AUTH_KEY}",
"Content-Type": "application/json",
"Accept": "application/json",
};
let body = {
"group_id": 5,
"member_name": "Bob Jones",
"password": "|]|{+-"
};
fetch(url, {
method: "DELETE",
headers,
body: JSON.stringify(body),
}).then(response => response.json());$client = new \GuzzleHttp\Client();
$url = 'https://api.shlott.com/v1/members/12';
$response = $client->delete(
$url,
[
'headers' => [
'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
'Content-Type' => 'application/json',
'Accept' => 'application/json',
],
'json' => [
'group_id' => 5,
'member_name' => 'Bob Jones',
'password' => '|]|{+-',
],
]
);
$body = $response->getBody();
print_r(json_decode((string) $body));import requests
import json
url = 'https://api.shlott.com/v1/members/12'
payload = {
"group_id": 5,
"member_name": "Bob Jones",
"password": "|]|{+-"
}
headers = {
'Authorization': 'Bearer {YOUR_AUTH_KEY}',
'Content-Type': 'application/json',
'Accept': 'application/json'
}
response = requests.request('DELETE', url, headers=headers, json=payload)
response.json()Example response (200, Deleted):
{
"message": "Member deleted."
}
Example response (403, Not a group admin):
{
"message": "Forbidden."
}
Example response (422, Refused):
{
"message": "...",
"errors": {
"member_name": [
"..."
]
}
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Resend Activation Email
requires authentication
Sends the member the welcome email again (with a fresh set-password link when they have not yet chosen a password).
Example request:
const url = new URL(
"https://api.shlott.com/v1/members/12/resend-activation"
);
const headers = {
"Authorization": "Bearer {YOUR_AUTH_KEY}",
"Content-Type": "application/json",
"Accept": "application/json",
};
let body = {
"group_id": 5
};
fetch(url, {
method: "POST",
headers,
body: JSON.stringify(body),
}).then(response => response.json());$client = new \GuzzleHttp\Client();
$url = 'https://api.shlott.com/v1/members/12/resend-activation';
$response = $client->post(
$url,
[
'headers' => [
'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
'Content-Type' => 'application/json',
'Accept' => 'application/json',
],
'json' => [
'group_id' => 5,
],
]
);
$body = $response->getBody();
print_r(json_decode((string) $body));import requests
import json
url = 'https://api.shlott.com/v1/members/12/resend-activation'
payload = {
"group_id": 5
}
headers = {
'Authorization': 'Bearer {YOUR_AUTH_KEY}',
'Content-Type': 'application/json',
'Accept': 'application/json'
}
response = requests.request('POST', url, headers=headers, json=payload)
response.json()Example response (200, Sent):
{
"message": "Activation email sent."
}
Example response (403, Not a group admin):
{
"message": "Forbidden."
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Send Password Reset Link
requires authentication
Emails the member a password reset link. Admins never set another member's password.
Example request:
const url = new URL(
"https://api.shlott.com/v1/members/12/password-reset"
);
const headers = {
"Authorization": "Bearer {YOUR_AUTH_KEY}",
"Content-Type": "application/json",
"Accept": "application/json",
};
let body = {
"group_id": 5
};
fetch(url, {
method: "POST",
headers,
body: JSON.stringify(body),
}).then(response => response.json());$client = new \GuzzleHttp\Client();
$url = 'https://api.shlott.com/v1/members/12/password-reset';
$response = $client->post(
$url,
[
'headers' => [
'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
'Content-Type' => 'application/json',
'Accept' => 'application/json',
],
'json' => [
'group_id' => 5,
],
]
);
$body = $response->getBody();
print_r(json_decode((string) $body));import requests
import json
url = 'https://api.shlott.com/v1/members/12/password-reset'
payload = {
"group_id": 5
}
headers = {
'Authorization': 'Bearer {YOUR_AUTH_KEY}',
'Content-Type': 'application/json',
'Accept': 'application/json'
}
response = requests.request('POST', url, headers=headers, json=payload)
response.json()Example response (200, Sent):
{
"message": "Password Reset Link sent. Please allow up to 30 minutes for delivery."
}
Example response (403, Not a group admin):
{
"message": "Forbidden."
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Objects
List Objects
requires authentication
Every live object with its category, and when it was last booked and next booked.
categories is for grouping and for the add form (it says whether a category exists at all).
Example request:
const url = new URL(
"https://api.shlott.com/v1/manage/objects"
);
const params = {
"group_id": "5",
};
Object.keys(params)
.forEach(key => url.searchParams.append(key, params[key]));
const headers = {
"Authorization": "Bearer {YOUR_AUTH_KEY}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());$client = new \GuzzleHttp\Client();
$url = 'https://api.shlott.com/v1/manage/objects';
$response = $client->get(
$url,
[
'headers' => [
'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
'Content-Type' => 'application/json',
'Accept' => 'application/json',
],
'query' => [
'group_id' => '5',
],
]
);
$body = $response->getBody();
print_r(json_decode((string) $body));import requests
import json
url = 'https://api.shlott.com/v1/manage/objects'
params = {
'group_id': '5',
}
headers = {
'Authorization': 'Bearer {YOUR_AUTH_KEY}',
'Content-Type': 'application/json',
'Accept': 'application/json'
}
response = requests.request('GET', url, headers=headers, params=params)
response.json()Example response (200, Objects):
{
"data": [
{
"id": 8,
"name": "Court 1",
"category_id": 3,
"category_name": "Courts",
"last_booking": null,
"next_booking": "2026-10-20 09:00:00",
"future_bookings": 2
}
],
"free_limit_reached": false
}
Example response (403, Not a group admin):
{
"message": "Forbidden."
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Add Object
requires authentication
The new object starts with the group's opening times.
Example request:
const url = new URL(
"https://api.shlott.com/v1/manage/objects"
);
const headers = {
"Authorization": "Bearer {YOUR_AUTH_KEY}",
"Content-Type": "application/json",
"Accept": "application/json",
};
let body = {
"group_id": 5,
"category_id": 3,
"name": "Court 3",
"subheading": "Indoor",
"values": []
};
fetch(url, {
method: "POST",
headers,
body: JSON.stringify(body),
}).then(response => response.json());$client = new \GuzzleHttp\Client();
$url = 'https://api.shlott.com/v1/manage/objects';
$response = $client->post(
$url,
[
'headers' => [
'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
'Content-Type' => 'application/json',
'Accept' => 'application/json',
],
'json' => [
'group_id' => 5,
'category_id' => 3,
'name' => 'Court 3',
'subheading' => 'Indoor',
'values' => [],
],
]
);
$body = $response->getBody();
print_r(json_decode((string) $body));import requests
import json
url = 'https://api.shlott.com/v1/manage/objects'
payload = {
"group_id": 5,
"category_id": 3,
"name": "Court 3",
"subheading": "Indoor",
"values": []
}
headers = {
'Authorization': 'Bearer {YOUR_AUTH_KEY}',
'Content-Type': 'application/json',
'Accept': 'application/json'
}
response = requests.request('POST', url, headers=headers, json=payload)
response.json()Example response (201):
{
"message": "Object successfully created.",
"id": 21
}
Example response (403, Not a group admin):
{
"message": "Forbidden."
}
Example response (422, Invalid, or free plan limit):
{
"message": "The name field is required.",
"errors": {
"name": [
"..."
]
}
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Update Object
requires authentication
Changing the category replaces the object's property values with the ones sent (the new category has its own fields). Opening-time availability is left untouched.
Example request:
const url = new URL(
"https://api.shlott.com/v1/manage/objects/8"
);
const headers = {
"Authorization": "Bearer {YOUR_AUTH_KEY}",
"Content-Type": "application/json",
"Accept": "application/json",
};
let body = {
"group_id": 5,
"category_id": 3,
"name": "Court 3",
"subheading": "Indoor",
"values": []
};
fetch(url, {
method: "PUT",
headers,
body: JSON.stringify(body),
}).then(response => response.json());$client = new \GuzzleHttp\Client();
$url = 'https://api.shlott.com/v1/manage/objects/8';
$response = $client->put(
$url,
[
'headers' => [
'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
'Content-Type' => 'application/json',
'Accept' => 'application/json',
],
'json' => [
'group_id' => 5,
'category_id' => 3,
'name' => 'Court 3',
'subheading' => 'Indoor',
'values' => [],
],
]
);
$body = $response->getBody();
print_r(json_decode((string) $body));import requests
import json
url = 'https://api.shlott.com/v1/manage/objects/8'
payload = {
"group_id": 5,
"category_id": 3,
"name": "Court 3",
"subheading": "Indoor",
"values": []
}
headers = {
'Authorization': 'Bearer {YOUR_AUTH_KEY}',
'Content-Type': 'application/json',
'Accept': 'application/json'
}
response = requests.request('PUT', url, headers=headers, json=payload)
response.json()Example response (200):
{
"message": "Object successfully updated."
}
Example response (403, Not a group admin):
{
"message": "Forbidden."
}
Example response (404):
{
"message": "Object not found."
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Delete Object
requires authentication
Hides the object from the calendar and lists; the website can restore it. Its bookings stay.
Example request:
const url = new URL(
"https://api.shlott.com/v1/manage/objects/8"
);
const params = {
"group_id": "5",
};
Object.keys(params)
.forEach(key => url.searchParams.append(key, params[key]));
const headers = {
"Authorization": "Bearer {YOUR_AUTH_KEY}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "DELETE",
headers,
}).then(response => response.json());$client = new \GuzzleHttp\Client();
$url = 'https://api.shlott.com/v1/manage/objects/8';
$response = $client->delete(
$url,
[
'headers' => [
'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
'Content-Type' => 'application/json',
'Accept' => 'application/json',
],
'query' => [
'group_id' => '5',
],
]
);
$body = $response->getBody();
print_r(json_decode((string) $body));import requests
import json
url = 'https://api.shlott.com/v1/manage/objects/8'
params = {
'group_id': '5',
}
headers = {
'Authorization': 'Bearer {YOUR_AUTH_KEY}',
'Content-Type': 'application/json',
'Accept': 'application/json'
}
response = requests.request('DELETE', url, headers=headers, params=params)
response.json()Example response (200):
{
"message": "Object successfully deleted"
}
Example response (403, Not a group admin):
{
"message": "Forbidden."
}
Example response (404):
{
"message": "Object not found."
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Get Object Details
requires authentication
Used by the Day/Week object page and by the admin edit form. properties[].value is a
string (YYYY-MM-DD for date / expiry_date; show it in date_format order: DMY, MDY or YMD)
or, for checkbox properties, an array of the ticked options. expiry_warning is
red / yellow / null and only set for expiry_date properties. availability.days is only
present on Pro groups (the website only lets Pro groups set days individually).
Example request:
const url = new URL(
"https://api.shlott.com/v1/objects/8"
);
const params = {
"group_id": "5",
};
Object.keys(params)
.forEach(key => url.searchParams.append(key, params[key]));
const headers = {
"Authorization": "Bearer {YOUR_AUTH_KEY}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());$client = new \GuzzleHttp\Client();
$url = 'https://api.shlott.com/v1/objects/8';
$response = $client->get(
$url,
[
'headers' => [
'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
'Content-Type' => 'application/json',
'Accept' => 'application/json',
],
'query' => [
'group_id' => '5',
],
]
);
$body = $response->getBody();
print_r(json_decode((string) $body));import requests
import json
url = 'https://api.shlott.com/v1/objects/8'
params = {
'group_id': '5',
}
headers = {
'Authorization': 'Bearer {YOUR_AUTH_KEY}',
'Content-Type': 'application/json',
'Accept': 'application/json'
}
response = requests.request('GET', url, headers=headers, params=params)
response.json()Example response (200, Object):
{
"id": 8,
"name": "Court 1",
"subheading": "Indoor",
"category_id": 3,
"category_name": "Courts",
"date_format": "DMY",
"properties": [
{
"id": 0,
"name": "Surface",
"type": "text",
"value": "Clay",
"expiry_warning": null
}
],
"availability": {
"start": "08:00",
"end": "22:00",
"pro": true,
"days": [
{
"day": "monday",
"start": "08:00",
"end": "22:00",
"off": false
}
]
}
}
Example response (403, Not a member):
{
"message": "Forbidden."
}
Example response (404):
{
"message": "Object not found."
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Categories
List Categories
requires authentication
The group's live categories in calendar order, with their property definitions (hidden
properties included, flagged deleted) and how many objects each holds.
Example request:
const url = new URL(
"https://api.shlott.com/v1/manage/categories"
);
const params = {
"group_id": "5",
};
Object.keys(params)
.forEach(key => url.searchParams.append(key, params[key]));
const headers = {
"Authorization": "Bearer {YOUR_AUTH_KEY}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());$client = new \GuzzleHttp\Client();
$url = 'https://api.shlott.com/v1/manage/categories';
$response = $client->get(
$url,
[
'headers' => [
'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
'Content-Type' => 'application/json',
'Accept' => 'application/json',
],
'query' => [
'group_id' => '5',
],
]
);
$body = $response->getBody();
print_r(json_decode((string) $body));import requests
import json
url = 'https://api.shlott.com/v1/manage/categories'
params = {
'group_id': '5',
}
headers = {
'Authorization': 'Bearer {YOUR_AUTH_KEY}',
'Content-Type': 'application/json',
'Accept': 'application/json'
}
response = requests.request('GET', url, headers=headers, params=params)
response.json()Example response (200, Categories):
{
"data": [
{
"id": 3,
"name": "Courts",
"objects": 4,
"properties": [
{
"id": 0,
"name": "Surface",
"type": "select",
"options": "Clay,Grass",
"required": true,
"deleted": false
}
]
}
],
"free_limit_reached": false,
"date_format": "DMY"
}
Example response (403, Not a group admin):
{
"message": "Forbidden."
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Add Category
requires authentication
Example request:
const url = new URL(
"https://api.shlott.com/v1/manage/categories"
);
const headers = {
"Authorization": "Bearer {YOUR_AUTH_KEY}",
"Content-Type": "application/json",
"Accept": "application/json",
};
let body = {
"group_id": 5,
"name": "Courts",
"properties": [
"architecto"
]
};
fetch(url, {
method: "POST",
headers,
body: JSON.stringify(body),
}).then(response => response.json());$client = new \GuzzleHttp\Client();
$url = 'https://api.shlott.com/v1/manage/categories';
$response = $client->post(
$url,
[
'headers' => [
'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
'Content-Type' => 'application/json',
'Accept' => 'application/json',
],
'json' => [
'group_id' => 5,
'name' => 'Courts',
'properties' => ['architecto'],
],
]
);
$body = $response->getBody();
print_r(json_decode((string) $body));import requests
import json
url = 'https://api.shlott.com/v1/manage/categories'
payload = {
"group_id": 5,
"name": "Courts",
"properties": [
"architecto"
]
}
headers = {
'Authorization': 'Bearer {YOUR_AUTH_KEY}',
'Content-Type': 'application/json',
'Accept': 'application/json'
}
response = requests.request('POST', url, headers=headers, json=payload)
response.json()Example response (201):
{
"message": "Calendar Category successfully created.",
"id": 9
}
Example response (403, Not a group admin):
{
"message": "Forbidden."
}
Example response (422, Free plan limit):
{
"message": "Sorry! You have already exceeded free usage of Shlott. Please subscribe pro to continue ..."
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Update Category
requires authentication
Send every property the category should keep. Existing ones carry their id; new ones leave it
out and are given the next free id. Set deleted to hide a property (or false to restore it).
A property left out of the list is hidden.
Example request:
const url = new URL(
"https://api.shlott.com/v1/manage/categories/9"
);
const headers = {
"Authorization": "Bearer {YOUR_AUTH_KEY}",
"Content-Type": "application/json",
"Accept": "application/json",
};
let body = {
"group_id": 5,
"name": "Courts",
"properties": [
"architecto"
]
};
fetch(url, {
method: "PUT",
headers,
body: JSON.stringify(body),
}).then(response => response.json());$client = new \GuzzleHttp\Client();
$url = 'https://api.shlott.com/v1/manage/categories/9';
$response = $client->put(
$url,
[
'headers' => [
'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
'Content-Type' => 'application/json',
'Accept' => 'application/json',
],
'json' => [
'group_id' => 5,
'name' => 'Courts',
'properties' => ['architecto'],
],
]
);
$body = $response->getBody();
print_r(json_decode((string) $body));import requests
import json
url = 'https://api.shlott.com/v1/manage/categories/9'
payload = {
"group_id": 5,
"name": "Courts",
"properties": [
"architecto"
]
}
headers = {
'Authorization': 'Bearer {YOUR_AUTH_KEY}',
'Content-Type': 'application/json',
'Accept': 'application/json'
}
response = requests.request('PUT', url, headers=headers, json=payload)
response.json()Example response (200):
{
"message": "Calendar Category successfully updated."
}
Example response (403, Not a group admin):
{
"message": "Forbidden."
}
Example response (404):
{
"message": "Category not found."
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Delete Category
requires authentication
Hides the category (and so its objects) from the calendar; the website can restore it.
Example request:
const url = new URL(
"https://api.shlott.com/v1/manage/categories/9"
);
const params = {
"group_id": "5",
};
Object.keys(params)
.forEach(key => url.searchParams.append(key, params[key]));
const headers = {
"Authorization": "Bearer {YOUR_AUTH_KEY}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "DELETE",
headers,
}).then(response => response.json());$client = new \GuzzleHttp\Client();
$url = 'https://api.shlott.com/v1/manage/categories/9';
$response = $client->delete(
$url,
[
'headers' => [
'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
'Content-Type' => 'application/json',
'Accept' => 'application/json',
],
'query' => [
'group_id' => '5',
],
]
);
$body = $response->getBody();
print_r(json_decode((string) $body));import requests
import json
url = 'https://api.shlott.com/v1/manage/categories/9'
params = {
'group_id': '5',
}
headers = {
'Authorization': 'Bearer {YOUR_AUTH_KEY}',
'Content-Type': 'application/json',
'Accept': 'application/json'
}
response = requests.request('DELETE', url, headers=headers, params=params)
response.json()Example response (200):
{
"message": "Calendar Category successfully deleted."
}
Example response (403, Not a group admin):
{
"message": "Forbidden."
}
Example response (404):
{
"message": "Category not found."
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Roles
List Roles
requires authentication
The group's roles in calendar order. visible roles appear as rows on the calendar; members
is how many members hold the role.
Example request:
const url = new URL(
"https://api.shlott.com/v1/manage/roles"
);
const params = {
"group_id": "5",
};
Object.keys(params)
.forEach(key => url.searchParams.append(key, params[key]));
const headers = {
"Authorization": "Bearer {YOUR_AUTH_KEY}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());$client = new \GuzzleHttp\Client();
$url = 'https://api.shlott.com/v1/manage/roles';
$response = $client->get(
$url,
[
'headers' => [
'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
'Content-Type' => 'application/json',
'Accept' => 'application/json',
],
'query' => [
'group_id' => '5',
],
]
);
$body = $response->getBody();
print_r(json_decode((string) $body));import requests
import json
url = 'https://api.shlott.com/v1/manage/roles'
params = {
'group_id': '5',
}
headers = {
'Authorization': 'Bearer {YOUR_AUTH_KEY}',
'Content-Type': 'application/json',
'Accept': 'application/json'
}
response = requests.request('GET', url, headers=headers, params=params)
response.json()Example response (200, Roles):
{
"data": [
{
"id": 2,
"name": "Coach",
"visible": true,
"members": 6
}
],
"free_limit_reached": false
}
Example response (403, Not a group admin):
{
"message": "Forbidden."
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Add Role
requires authentication
Example request:
const url = new URL(
"https://api.shlott.com/v1/manage/roles"
);
const headers = {
"Authorization": "Bearer {YOUR_AUTH_KEY}",
"Content-Type": "application/json",
"Accept": "application/json",
};
let body = {
"group_id": 5,
"name": "Coach"
};
fetch(url, {
method: "POST",
headers,
body: JSON.stringify(body),
}).then(response => response.json());$client = new \GuzzleHttp\Client();
$url = 'https://api.shlott.com/v1/manage/roles';
$response = $client->post(
$url,
[
'headers' => [
'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
'Content-Type' => 'application/json',
'Accept' => 'application/json',
],
'json' => [
'group_id' => 5,
'name' => 'Coach',
],
]
);
$body = $response->getBody();
print_r(json_decode((string) $body));import requests
import json
url = 'https://api.shlott.com/v1/manage/roles'
payload = {
"group_id": 5,
"name": "Coach"
}
headers = {
'Authorization': 'Bearer {YOUR_AUTH_KEY}',
'Content-Type': 'application/json',
'Accept': 'application/json'
}
response = requests.request('POST', url, headers=headers, json=payload)
response.json()Example response (201):
{
"message": "Role saved.",
"id": 7
}
Example response (403, Not a group admin):
{
"message": "Forbidden."
}
Example response (422, Free plan limit):
{
"message": "Sorry! You have already exceeded free usage of Shlott. Please subscribe pro to continue ..."
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Update Role
requires authentication
Rename a role, or hide it (visible false) so it no longer has a row on the calendar.
Example request:
const url = new URL(
"https://api.shlott.com/v1/manage/roles/7"
);
const headers = {
"Authorization": "Bearer {YOUR_AUTH_KEY}",
"Content-Type": "application/json",
"Accept": "application/json",
};
let body = {
"group_id": 5,
"name": "Head coach",
"visible": true
};
fetch(url, {
method: "PUT",
headers,
body: JSON.stringify(body),
}).then(response => response.json());$client = new \GuzzleHttp\Client();
$url = 'https://api.shlott.com/v1/manage/roles/7';
$response = $client->put(
$url,
[
'headers' => [
'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
'Content-Type' => 'application/json',
'Accept' => 'application/json',
],
'json' => [
'group_id' => 5,
'name' => 'Head coach',
'visible' => true,
],
]
);
$body = $response->getBody();
print_r(json_decode((string) $body));import requests
import json
url = 'https://api.shlott.com/v1/manage/roles/7'
payload = {
"group_id": 5,
"name": "Head coach",
"visible": true
}
headers = {
'Authorization': 'Bearer {YOUR_AUTH_KEY}',
'Content-Type': 'application/json',
'Accept': 'application/json'
}
response = requests.request('PUT', url, headers=headers, json=payload)
response.json()Example response (200):
{
"message": "Role updated."
}
Example response (403, Not a group admin):
{
"message": "Forbidden."
}
Example response (404):
{
"message": "Role not found."
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Delete Role
requires authentication
The group must keep at least one role. Members keep their (now deleted) role id, as on the website.
Example request:
const url = new URL(
"https://api.shlott.com/v1/manage/roles/7"
);
const params = {
"group_id": "5",
};
Object.keys(params)
.forEach(key => url.searchParams.append(key, params[key]));
const headers = {
"Authorization": "Bearer {YOUR_AUTH_KEY}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "DELETE",
headers,
}).then(response => response.json());$client = new \GuzzleHttp\Client();
$url = 'https://api.shlott.com/v1/manage/roles/7';
$response = $client->delete(
$url,
[
'headers' => [
'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
'Content-Type' => 'application/json',
'Accept' => 'application/json',
],
'query' => [
'group_id' => '5',
],
]
);
$body = $response->getBody();
print_r(json_decode((string) $body));import requests
import json
url = 'https://api.shlott.com/v1/manage/roles/7'
params = {
'group_id': '5',
}
headers = {
'Authorization': 'Bearer {YOUR_AUTH_KEY}',
'Content-Type': 'application/json',
'Accept': 'application/json'
}
response = requests.request('DELETE', url, headers=headers, params=params)
response.json()Example response (200):
{
"message": "Role deleted."
}
Example response (403, Not a group admin):
{
"message": "Forbidden."
}
Example response (404):
{
"message": "Role not found."
}
Example response (422, Last role):
{
"message": "You cannot delete the last role in the group. ..."
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
User
Resend Account Activation Email
Resends the activation email for an account that has a pending request to join a group
but has not yet been activated. To prevent repeated requests from flooding the user's
inbox, resends are limited to once every 10 minutes per pending request. The API
enforces this itself: you do not need to track the cooldown, just handle the 429
response (code 6, with retry_after_seconds) if it occurs. There is also a limit of
5 requests a minute per IP address.
group_id is required alongside email because the activation email is
group-specific — it names the group and is sent from that group's own contact
address, and a user could in principle have more than one pending, unactivated
join request across different groups.
Example request:
const url = new URL(
"https://api.shlott.com/v1/user/send-activation-email"
);
const headers = {
"Content-Type": "application/json",
"Accept": "application/json",
};
let body = {
"email": "[email protected]",
"group_id": 5
};
fetch(url, {
method: "POST",
headers,
body: JSON.stringify(body),
}).then(response => response.json());$client = new \GuzzleHttp\Client();
$url = 'https://api.shlott.com/v1/user/send-activation-email';
$response = $client->post(
$url,
[
'headers' => [
'Content-Type' => 'application/json',
'Accept' => 'application/json',
],
'json' => [
'email' => '[email protected]',
'group_id' => 5,
],
]
);
$body = $response->getBody();
print_r(json_decode((string) $body));import requests
import json
url = 'https://api.shlott.com/v1/user/send-activation-email'
payload = {
"email": "[email protected]",
"group_id": 5
}
headers = {
'Content-Type': 'application/json',
'Accept': 'application/json'
}
response = requests.request('POST', url, headers=headers, json=payload)
response.json()Example response (200, Activation email resent):
{
"code": 0,
"message": "We've resent the activation email — please check your inbox."
}
Example response (404, No account exists for this email):
{
"code": 2,
"message": "No account was found for this email address."
}
Example response (404, Group not found):
{
"code": 3,
"message": "Group not found."
}
Example response (404, No pending join request for this email/group):
{
"code": 4,
"message": "No pending join request was found for this email address and group."
}
Example response (409, Account is already activated):
{
"code": 5,
"message": "This account is already activated — there is nothing to resend."
}
Example response (422, Validation error):
{
"code": 1,
"message": "The email field is required.",
"errors": {
"email": [
"The email field is required."
]
}
}
Example response (429, Resent too recently):
{
"code": 6,
"message": "An activation email was already sent recently. Please wait a few minutes and check your inbox before requesting another.",
"retry_after_seconds": 480
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Get Profile
requires authentication
Returns the authenticated user's personal details, address and emergency contact, plus the list of countries the address can be set to. Group custom fields are not included.
Example request:
const url = new URL(
"https://api.shlott.com/v1/user/profile"
);
const headers = {
"Authorization": "Bearer {YOUR_AUTH_KEY}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());$client = new \GuzzleHttp\Client();
$url = 'https://api.shlott.com/v1/user/profile';
$response = $client->get(
$url,
[
'headers' => [
'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
'Content-Type' => 'application/json',
'Accept' => 'application/json',
],
]
);
$body = $response->getBody();
print_r(json_decode((string) $body));import requests
import json
url = 'https://api.shlott.com/v1/user/profile'
headers = {
'Authorization': 'Bearer {YOUR_AUTH_KEY}',
'Content-Type': 'application/json',
'Accept': 'application/json'
}
response = requests.request('GET', url, headers=headers)
response.json()Example response (200, Profile returned):
{
"profile": {
"first_name": "Jane",
"last_name": "Smith",
"email": "[email protected]",
"phone": "01234 567890",
"mobile": "07700 900000",
"address_line_1": "10 Runway Lane",
"address_line_2": null,
"address_line_3": null,
"city": "Anytown",
"county": "Exampleshire",
"postcode": "EX1 1AA",
"country": "GB",
"next_of_kin": "Bob Smith",
"next_of_kin_relationship": "Spouse",
"next_of_kin_contact": "07700 900001"
},
"countries": [
{
"code": "GB",
"name": "United Kingdom"
},
{
"code": "US",
"name": "United States"
}
]
}
Example response (401, Unauthenticated: no or invalid API key):
{
"message": "Unauthenticated."
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Update Profile
requires authentication
Updates the authenticated user's personal details, address and emergency contact. Every field is overwritten, so send the full set (blank values clear a field).
Example request:
const url = new URL(
"https://api.shlott.com/v1/user/profile"
);
const headers = {
"Authorization": "Bearer {YOUR_AUTH_KEY}",
"Content-Type": "application/json",
"Accept": "application/json",
};
let body = {
"first_name": "Jane",
"last_name": "Smith",
"email": "[email protected]",
"phone": "01234 567890",
"mobile": "07700 900000",
"address_line_1": "10 Runway Lane",
"address_line_2": "architecto",
"address_line_3": "architecto",
"city": "Anytown",
"county": "Exampleshire",
"postcode": "EX1 1AA",
"country": "GB",
"next_of_kin": "Bob Smith",
"next_of_kin_relationship": "Spouse",
"next_of_kin_contact": "07700 900001"
};
fetch(url, {
method: "PUT",
headers,
body: JSON.stringify(body),
}).then(response => response.json());$client = new \GuzzleHttp\Client();
$url = 'https://api.shlott.com/v1/user/profile';
$response = $client->put(
$url,
[
'headers' => [
'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
'Content-Type' => 'application/json',
'Accept' => 'application/json',
],
'json' => [
'first_name' => 'Jane',
'last_name' => 'Smith',
'email' => '[email protected]',
'phone' => '01234 567890',
'mobile' => '07700 900000',
'address_line_1' => '10 Runway Lane',
'address_line_2' => 'architecto',
'address_line_3' => 'architecto',
'city' => 'Anytown',
'county' => 'Exampleshire',
'postcode' => 'EX1 1AA',
'country' => 'GB',
'next_of_kin' => 'Bob Smith',
'next_of_kin_relationship' => 'Spouse',
'next_of_kin_contact' => '07700 900001',
],
]
);
$body = $response->getBody();
print_r(json_decode((string) $body));import requests
import json
url = 'https://api.shlott.com/v1/user/profile'
payload = {
"first_name": "Jane",
"last_name": "Smith",
"email": "[email protected]",
"phone": "01234 567890",
"mobile": "07700 900000",
"address_line_1": "10 Runway Lane",
"address_line_2": "architecto",
"address_line_3": "architecto",
"city": "Anytown",
"county": "Exampleshire",
"postcode": "EX1 1AA",
"country": "GB",
"next_of_kin": "Bob Smith",
"next_of_kin_relationship": "Spouse",
"next_of_kin_contact": "07700 900001"
}
headers = {
'Authorization': 'Bearer {YOUR_AUTH_KEY}',
'Content-Type': 'application/json',
'Accept': 'application/json'
}
response = requests.request('PUT', url, headers=headers, json=payload)
response.json()Example response (200, Profile updated):
{
"profile": {
"first_name": "Jane",
"last_name": "Smith",
"email": "[email protected]",
"phone": "01234 567890",
"mobile": "07700 900000",
"address_line_1": "10 Runway Lane",
"address_line_2": null,
"address_line_3": null,
"city": "Anytown",
"county": "Exampleshire",
"postcode": "EX1 1AA",
"country": "GB",
"next_of_kin": "Bob Smith",
"next_of_kin_relationship": "Spouse",
"next_of_kin_contact": "07700 900001"
}
}
Example response (401, Unauthenticated: no or invalid API key):
{
"message": "Unauthenticated."
}
Example response (422, Validation error):
{
"message": "The email has already been taken.",
"errors": {
"email": [
"The email has already been taken."
]
}
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Change Password
requires authentication
Changes the authenticated user's login password. The current password must be supplied and the new one must be confirmed and be at least 8 characters. Limited to 6 attempts a minute.
Example request:
const url = new URL(
"https://api.shlott.com/v1/user/password"
);
const headers = {
"Authorization": "Bearer {YOUR_AUTH_KEY}",
"Content-Type": "application/json",
"Accept": "application/json",
};
let body = {
"current_password": "oldsecret1",
"password": "newsecret123",
"password_confirmation": "newsecret123"
};
fetch(url, {
method: "PUT",
headers,
body: JSON.stringify(body),
}).then(response => response.json());$client = new \GuzzleHttp\Client();
$url = 'https://api.shlott.com/v1/user/password';
$response = $client->put(
$url,
[
'headers' => [
'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
'Content-Type' => 'application/json',
'Accept' => 'application/json',
],
'json' => [
'current_password' => 'oldsecret1',
'password' => 'newsecret123',
'password_confirmation' => 'newsecret123',
],
]
);
$body = $response->getBody();
print_r(json_decode((string) $body));import requests
import json
url = 'https://api.shlott.com/v1/user/password'
payload = {
"current_password": "oldsecret1",
"password": "newsecret123",
"password_confirmation": "newsecret123"
}
headers = {
'Authorization': 'Bearer {YOUR_AUTH_KEY}',
'Content-Type': 'application/json',
'Accept': 'application/json'
}
response = requests.request('PUT', url, headers=headers, json=payload)
response.json()Example response (200, Password changed):
{
"message": "Your password has been changed."
}
Example response (401, Unauthenticated: no or invalid API key):
{
"message": "Unauthenticated."
}
Example response (422, Wrong current password, or validation error):
{
"message": "The current password is incorrect.",
"errors": {
"current_password": [
"The current password is incorrect."
]
}
}
Example response (429, Too many attempts):
{
"message": "Too Many Attempts."
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Delete Account: What Stands in the Way
requires authentication
The groups the user belongs to (with whether anyone else administers each), their future bookings, and whether the account can be deleted now. A sole administrator of a group must first make someone else an admin or delete that group; future bookings must be cancelled first.
Example request:
const url = new URL(
"https://api.shlott.com/v1/user/delete"
);
const headers = {
"Authorization": "Bearer {YOUR_AUTH_KEY}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());$client = new \GuzzleHttp\Client();
$url = 'https://api.shlott.com/v1/user/delete';
$response = $client->get(
$url,
[
'headers' => [
'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
'Content-Type' => 'application/json',
'Accept' => 'application/json',
],
]
);
$body = $response->getBody();
print_r(json_decode((string) $body));import requests
import json
url = 'https://api.shlott.com/v1/user/delete'
headers = {
'Authorization': 'Bearer {YOUR_AUTH_KEY}',
'Content-Type': 'application/json',
'Accept': 'application/json'
}
response = requests.request('GET', url, headers=headers)
response.json()Example response (200):
{
"groups": [
{
"id": 5,
"name": "Flying Club",
"other_admins": 0,
"sole_admin": true
}
],
"future_bookings": [
{
"group_name": "Flying Club",
"start": "2026-11-01 10:00:00",
"end": "2026-11-01 11:00:00"
}
],
"blocked_by_groups": true,
"blocked_by_bookings": false,
"can_delete": false,
"ad_free_warning": null
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Delete Account
requires authentication
Permanently deletes the account: personal details are wiped, the user is removed from every group, tokens are revoked and any ad-free subscription ends. Needs the account password.
Example request:
const url = new URL(
"https://api.shlott.com/v1/user"
);
const headers = {
"Authorization": "Bearer {YOUR_AUTH_KEY}",
"Content-Type": "application/json",
"Accept": "application/json",
};
let body = {
"password": "secret123",
"confirmed": true
};
fetch(url, {
method: "DELETE",
headers,
body: JSON.stringify(body),
}).then(response => response.json());$client = new \GuzzleHttp\Client();
$url = 'https://api.shlott.com/v1/user';
$response = $client->delete(
$url,
[
'headers' => [
'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
'Content-Type' => 'application/json',
'Accept' => 'application/json',
],
'json' => [
'password' => 'secret123',
'confirmed' => true,
],
]
);
$body = $response->getBody();
print_r(json_decode((string) $body));import requests
import json
url = 'https://api.shlott.com/v1/user'
payload = {
"password": "secret123",
"confirmed": true
}
headers = {
'Authorization': 'Bearer {YOUR_AUTH_KEY}',
'Content-Type': 'application/json',
'Accept': 'application/json'
}
response = requests.request('DELETE', url, headers=headers, json=payload)
response.json()Example response (200):
{
"message": "Your account has been permanently deleted."
}
Example response (422, Blocked or wrong password):
{
"message": "..."
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Get Preferences
requires authentication
Returns the user's Shlott newsletter preference and their per-group notification preferences
for every group the user is an active member of. is_admin marks groups where the
daily upcoming booking list option applies.
Example request:
const url = new URL(
"https://api.shlott.com/v1/user/preferences"
);
const headers = {
"Authorization": "Bearer {YOUR_AUTH_KEY}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());$client = new \GuzzleHttp\Client();
$url = 'https://api.shlott.com/v1/user/preferences';
$response = $client->get(
$url,
[
'headers' => [
'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
'Content-Type' => 'application/json',
'Accept' => 'application/json',
],
]
);
$body = $response->getBody();
print_r(json_decode((string) $body));import requests
import json
url = 'https://api.shlott.com/v1/user/preferences'
headers = {
'Authorization': 'Bearer {YOUR_AUTH_KEY}',
'Content-Type': 'application/json',
'Accept': 'application/json'
}
response = requests.request('GET', url, headers=headers)
response.json()Example response (200, Preferences returned):
{
"receive_shlott_newsletter": true,
"groups": [
{
"group_id": 5,
"group_name": "Anytown Flying Club",
"is_admin": false,
"preferences": {
"hide_details_from_other_members": false,
"allow_members_to_send_email": true,
"allow_members_to_change_booking": true,
"notify_on_members_booking": false,
"notify_on_new_member_joining": false,
"receive_email_sent_to_whole_group": true,
"receive_daily_upcoming_booking_list": false,
"receive_expiry_emails": true
}
}
]
}
Example response (401, Unauthenticated: no or invalid API key):
{
"message": "Unauthenticated."
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Update Preferences
requires authentication
Updates the user's Shlott newsletter preference (receive_shlott_newsletter) and/or the
preferences for one group (group_id plus any of the group preference booleans). Only the
fields sent are changed. The user must be an active member of the group. Returns the
full, updated preferences, as Get Preferences does.
receive_daily_upcoming_booking_list is only offered to group admins; it is ignored for
other members.
Example request:
const url = new URL(
"https://api.shlott.com/v1/user/preferences"
);
const headers = {
"Authorization": "Bearer {YOUR_AUTH_KEY}",
"Content-Type": "application/json",
"Accept": "application/json",
};
let body = {
"receive_shlott_newsletter": true,
"group_id": 5,
"preferences": {
"hide_details_from_other_members": false,
"allow_members_to_send_email": true,
"allow_members_to_change_booking": true,
"notify_on_members_booking": false,
"notify_on_new_member_joining": false,
"receive_email_sent_to_whole_group": true,
"receive_daily_upcoming_booking_list": false,
"receive_expiry_emails": true
}
};
fetch(url, {
method: "PUT",
headers,
body: JSON.stringify(body),
}).then(response => response.json());$client = new \GuzzleHttp\Client();
$url = 'https://api.shlott.com/v1/user/preferences';
$response = $client->put(
$url,
[
'headers' => [
'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
'Content-Type' => 'application/json',
'Accept' => 'application/json',
],
'json' => [
'receive_shlott_newsletter' => true,
'group_id' => 5,
'preferences' => ['hide_details_from_other_members' => false, 'allow_members_to_send_email' => true, 'allow_members_to_change_booking' => true, 'notify_on_members_booking' => false, 'notify_on_new_member_joining' => false, 'receive_email_sent_to_whole_group' => true, 'receive_daily_upcoming_booking_list' => false, 'receive_expiry_emails' => true],
],
]
);
$body = $response->getBody();
print_r(json_decode((string) $body));import requests
import json
url = 'https://api.shlott.com/v1/user/preferences'
payload = {
"receive_shlott_newsletter": true,
"group_id": 5,
"preferences": {
"hide_details_from_other_members": false,
"allow_members_to_send_email": true,
"allow_members_to_change_booking": true,
"notify_on_members_booking": false,
"notify_on_new_member_joining": false,
"receive_email_sent_to_whole_group": true,
"receive_daily_upcoming_booking_list": false,
"receive_expiry_emails": true
}
}
headers = {
'Authorization': 'Bearer {YOUR_AUTH_KEY}',
'Content-Type': 'application/json',
'Accept': 'application/json'
}
response = requests.request('PUT', url, headers=headers, json=payload)
response.json()Example response (200, Preferences updated):
{
"receive_shlott_newsletter": true,
"groups": [
{
"group_id": 5,
"group_name": "Anytown Flying Club",
"is_admin": false,
"preferences": {
"hide_details_from_other_members": false,
"allow_members_to_send_email": true,
"allow_members_to_change_booking": true,
"notify_on_members_booking": false,
"notify_on_new_member_joining": false,
"receive_email_sent_to_whole_group": true,
"receive_daily_upcoming_booking_list": false,
"receive_expiry_emails": true
}
}
]
}
Example response (401, Unauthenticated: no or invalid API key):
{
"message": "Unauthenticated."
}
Example response (403, Not a member of the group):
{
"message": "You are not a member of this group."
}
Example response (422, Validation error):
{
"message": "The preferences.receive_expiry_emails field must be true or false.",
"errors": {
"preferences.receive_expiry_emails": [
"The preferences.receive_expiry_emails field must be true or false."
]
}
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Banner
Get site banner
requires authentication
The site-wide announcement banner, as the user should see it in the given group: banner is
null when there is no banner, it is switched off or expired, or it is targeted at other
members (admins only, standard members and above) or another group type (free or Pro).
version changes whenever the banner is edited. Store the version the user dismisses on the
device and show the banner again when it differs. style is info, success, warning or
danger. html is a safe HTML rendering (basic formatting tags and links only) and text
is the same content as plain text. Render one of them.
Bookings
List Bookings
requires authentication
Returns all non-cancelled bookings that involve the authenticated user across every group they belong to. A booking is included if the user is the primary person booked, an additional member listed on the booking (e.g. an instructor or examiner), or a friend the booking has been shared with.
Results are ordered by booking start time (earliest first). Use the optional
start_date and end_date query parameters to filter to a specific date window —
for example, to fetch only this week's bookings or a future month's schedule.
Each booking includes the resources reserved (e.g. aircraft), any additional members
on the booking, and any members the booking was shared with.
Cancelled bookings are excluded, but bookings that are pending approval or on the
waiting list are included and flagged with pending_approval / waiting_list.
Clients should not treat those as confirmed; the website's My Bookings lists keep
them out of Upcoming and Previous (a booking is confirmed when both flags are false).
Example request:
const url = new URL(
"https://api.shlott.com/v1/bookings"
);
const params = {
"start_date": "2025-06-01",
"end_date": "2025-06-30",
};
Object.keys(params)
.forEach(key => url.searchParams.append(key, params[key]));
const headers = {
"Authorization": "Bearer {YOUR_AUTH_KEY}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());$client = new \GuzzleHttp\Client();
$url = 'https://api.shlott.com/v1/bookings';
$response = $client->get(
$url,
[
'headers' => [
'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
'Content-Type' => 'application/json',
'Accept' => 'application/json',
],
'query' => [
'start_date' => '2025-06-01',
'end_date' => '2025-06-30',
],
]
);
$body = $response->getBody();
print_r(json_decode((string) $body));import requests
import json
url = 'https://api.shlott.com/v1/bookings'
params = {
'start_date': '2025-06-01',
'end_date': '2025-06-30',
}
headers = {
'Authorization': 'Bearer {YOUR_AUTH_KEY}',
'Content-Type': 'application/json',
'Accept': 'application/json'
}
response = requests.request('GET', url, headers=headers, params=params)
response.json()Example response (200, Bookings returned successfully):
[
{
"group_id": 5,
"group_name": "Anytown Flying Club",
"booked_for_member_id": 12,
"booked_for": "Jane Smith",
"booking_start": "2025-06-15 09:00:00",
"booking_end": "2025-06-15 11:00:00",
"calendar_objects": [
{
"id": 3,
"name": "G-ABCD"
}
],
"booked_with": [
{
"id": 7,
"first_name": "John",
"last_name": "Doe",
"role_id": 2,
"role_name": "Instructor"
}
],
"shared_with": [
{
"id": 9,
"first_name": "Alice",
"last_name": "Jones"
}
],
"reason": "Trial Flight",
"notes": "Student's first lesson",
"created_by": "Jane Smith",
"created_at": "2025-05-20T14:32:00.000000Z",
"pending_approval": false,
"waiting_list": false
}
]
Example response (401, Unauthenticated — no or invalid API key):
{
"message": "Unauthenticated."
}
Example response (422, Invalid date format or end_date before start_date):
{
"message": "The end date field must be a date after or equal to start date.",
"errors": {
"end_date": [
"The end date field must be a date after or equal to start date."
]
}
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Get Booking
requires authentication
Returns a single booking by its ID. The authenticated user must be a member of the group that owns the booking. If the group has "hide member details on calendar" enabled, only admins and members directly involved in the booking (created it, booked-for, booked-with, or shared-with) may view its details. Bookings that are pending approval, on the waiting list or cancelled can only be viewed by admins and the members involved in them.
Example request:
const url = new URL(
"https://api.shlott.com/v1/booking/42"
);
const headers = {
"Authorization": "Bearer {YOUR_AUTH_KEY}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());$client = new \GuzzleHttp\Client();
$url = 'https://api.shlott.com/v1/booking/42';
$response = $client->get(
$url,
[
'headers' => [
'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
'Content-Type' => 'application/json',
'Accept' => 'application/json',
],
]
);
$body = $response->getBody();
print_r(json_decode((string) $body));import requests
import json
url = 'https://api.shlott.com/v1/booking/42'
headers = {
'Authorization': 'Bearer {YOUR_AUTH_KEY}',
'Content-Type': 'application/json',
'Accept': 'application/json'
}
response = requests.request('GET', url, headers=headers)
response.json()Example response (200, Booking returned successfully):
{
"group_id": 5,
"group_name": "Anytown Flying Club",
"booked_for_member_id": 12,
"booked_for": "Jane Smith",
"booking_start": "2025-06-15 09:00:00",
"booking_end": "2025-06-15 11:00:00",
"calendar_objects": [
{
"id": 3,
"name": "G-ABCD"
}
],
"booked_with": [
{
"id": 7,
"first_name": "John",
"last_name": "Doe",
"role_id": 2,
"role_name": "Instructor"
}
],
"shared_with": [
{
"id": 9,
"first_name": "Alice",
"last_name": "Jones"
}
],
"reason": "Trial Flight",
"notes": "Student's first lesson",
"created_by": "Jane Smith",
"created_at": "2025-05-20T14:32:00.000000Z"
}
Example response (401, Unauthenticated — no or invalid API key):
{
"message": "Unauthenticated."
}
Example response (403, Not a member of this group, bookings are private and user is not involved, or the booking is pending, waiting-listed or cancelled and user is not involved):
{
"message": "Forbidden."
}
Example response (404, Booking not found):
{
"message": "Not found."
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Get Booking Options
requires authentication
Returns everything needed to build a booking for one group and date: what the user is
allowed to do (book for others, share a booking, make recurring bookings, join the
waiting list), the booking reasons, the categories with the items bookable on that
date, the opening hours and the permitted start/end time steps. Groups without a paid
subscription get the same shape with recurring_allowed false and the group-wide
hours and availability instead of per-day ones.
Example request:
const url = new URL(
"https://api.shlott.com/v1/booking-options"
);
const params = {
"group_id": "5",
"date": "2026-10-13",
};
Object.keys(params)
.forEach(key => url.searchParams.append(key, params[key]));
const headers = {
"Authorization": "Bearer {YOUR_AUTH_KEY}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());$client = new \GuzzleHttp\Client();
$url = 'https://api.shlott.com/v1/booking-options';
$response = $client->get(
$url,
[
'headers' => [
'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
'Content-Type' => 'application/json',
'Accept' => 'application/json',
],
'query' => [
'group_id' => '5',
'date' => '2026-10-13',
],
]
);
$body = $response->getBody();
print_r(json_decode((string) $body));import requests
import json
url = 'https://api.shlott.com/v1/booking-options'
params = {
'group_id': '5',
'date': '2026-10-13',
}
headers = {
'Authorization': 'Bearer {YOUR_AUTH_KEY}',
'Content-Type': 'application/json',
'Accept': 'application/json'
}
response = requests.request('GET', url, headers=headers, params=params)
response.json()Example response (200, Options returned):
{
"pro": true,
"member_id": 12,
"member_name": "Jane Smith",
"permission_level": 2,
"group_now": "2026-10-13 09:30",
"opening_start": "08:00",
"opening_end": "18:00",
"minimum_booking_duration": 30,
"minute_steps": [
0,
30
],
"future_booking_limit_months": 6,
"allow_past_booking": false,
"maximum_bookable_objects": null,
"requires_approval": false,
"recurring_allowed": true,
"can_book_for_others": false,
"can_share": true,
"members": [
{
"id": 7,
"name": "John Doe"
}
],
"allow_waiting_list": true,
"email_options": [
{
"value": 1,
"label": "Do not send an email confirmation"
},
{
"value": 2,
"label": "Email only the users booked"
}
],
"default_email_option": 1,
"reasons": [
{
"id": 3,
"reason": "Trial Flight"
}
],
"categories": [
{
"key": "resource_4",
"type": "resource",
"name": "Aircraft",
"items": [
{
"id": 3,
"name": "G-ABCD",
"available_from": "08:00",
"available_to": "18:00"
}
]
},
{
"key": "role_2",
"type": "role",
"name": "Instructor",
"items": [
{
"id": 7,
"name": "John Doe",
"available_from": "09:00",
"available_to": "17:00"
}
]
}
]
}
Example response (401, Unauthenticated: no or invalid API key):
{
"message": "Unauthenticated."
}
Example response (403, Not a member of the group, on hold, or read-only):
{
"message": "Forbidden."
}
Example response (422, Validation error):
{
"message": "The group id field is required.",
"errors": {
"group_id": [
"The group id field is required."
]
}
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Create Booking
requires authentication
Creates a booking (or a recurring series) for the authenticated member, applying the
group's booking rules: opening hours, item availability, clashes, the waiting list,
approval-only groups and the limits on how far ahead, and how many items, can be booked.
At least one of resources or members is required. Returns 422 with a human-readable
message when the booking is refused (clash, outside opening hours, in the past, etc).
Call Get Booking Options first to find what the user can book and which options apply.
Example request:
const url = new URL(
"https://api.shlott.com/v1/bookings"
);
const headers = {
"Authorization": "Bearer {YOUR_AUTH_KEY}",
"Content-Type": "application/json",
"Accept": "application/json",
};
let body = {
"group_id": 5,
"book_for": 77,
"resources": [
16
],
"members": [
16
],
"shared_with": [
16
],
"start": "2026-10-13 14:00",
"end": "2026-10-13 15:00",
"reason": 3,
"note": "architecto",
"email_to": 16,
"add_to_waiting_list_if_not_available": false,
"recurring": "no",
"frequency_num": 1,
"recurring_end": "architecto"
};
fetch(url, {
method: "POST",
headers,
body: JSON.stringify(body),
}).then(response => response.json());$client = new \GuzzleHttp\Client();
$url = 'https://api.shlott.com/v1/bookings';
$response = $client->post(
$url,
[
'headers' => [
'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
'Content-Type' => 'application/json',
'Accept' => 'application/json',
],
'json' => [
'group_id' => 5,
'book_for' => 77,
'resources' => [16],
'members' => [16],
'shared_with' => [16],
'start' => '2026-10-13 14:00',
'end' => '2026-10-13 15:00',
'reason' => 3,
'note' => 'architecto',
'email_to' => 16,
'add_to_waiting_list_if_not_available' => false,
'recurring' => 'no',
'frequency_num' => 1,
'recurring_end' => 'architecto',
],
]
);
$body = $response->getBody();
print_r(json_decode((string) $body));import requests
import json
url = 'https://api.shlott.com/v1/bookings'
payload = {
"group_id": 5,
"book_for": 77,
"resources": [
16
],
"members": [
16
],
"shared_with": [
16
],
"start": "2026-10-13 14:00",
"end": "2026-10-13 15:00",
"reason": 3,
"note": "architecto",
"email_to": 16,
"add_to_waiting_list_if_not_available": false,
"recurring": "no",
"frequency_num": 1,
"recurring_end": "architecto"
}
headers = {
'Authorization': 'Bearer {YOUR_AUTH_KEY}',
'Content-Type': 'application/json',
'Accept': 'application/json'
}
response = requests.request('POST', url, headers=headers, json=payload)
response.json()Example response (201, Created):
{
"message": "Booking created.",
"status": "success",
"msg": "Booking created.",
"start_id": 120,
"end_id": 120
}
Example response (401, Unauthenticated: no or invalid API key):
{
"message": "Unauthenticated."
}
Example response (403, Not a member of the group, on hold, or read-only):
{
"message": "Forbidden."
}
Example response (422, Refused):
{
"message": "Chris already booked for this time slot.",
"errors": {
"user_booked": [
"Chris already booked for this time slot."
]
}
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Response
Response Fields
message
string
The result, in plain language. Use this. status and msg are the older fields, kept so existing clients keep working.
Update Booking
requires authentication
Changes a booking, applying the same booking rules as Create Booking. The body is the same as Create Booking's, without the recurring fields: a recurring series cannot be changed as a whole. Admins can change any booking in their group, other members only ones they are involved in. In a group where bookings need approval, a non-admin's change sends the booking back for approval and an admin's change approves it. Confirmation and notification emails are sent as for a new booking. Returns 422 with a human-readable message when the change is refused.
Example request:
const url = new URL(
"https://api.shlott.com/v1/booking/42"
);
const headers = {
"Authorization": "Bearer {YOUR_AUTH_KEY}",
"Content-Type": "application/json",
"Accept": "application/json",
};
let body = {
"book_for": 77,
"resources": [
16
],
"members": [
16
],
"shared_with": [
16
],
"start": "2026-10-13 14:00",
"end": "2026-10-13 15:00",
"reason": 3,
"note": "architecto",
"email_to": 16,
"add_to_waiting_list_if_not_available": false
};
fetch(url, {
method: "PUT",
headers,
body: JSON.stringify(body),
}).then(response => response.json());$client = new \GuzzleHttp\Client();
$url = 'https://api.shlott.com/v1/booking/42';
$response = $client->put(
$url,
[
'headers' => [
'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
'Content-Type' => 'application/json',
'Accept' => 'application/json',
],
'json' => [
'book_for' => 77,
'resources' => [16],
'members' => [16],
'shared_with' => [16],
'start' => '2026-10-13 14:00',
'end' => '2026-10-13 15:00',
'reason' => 3,
'note' => 'architecto',
'email_to' => 16,
'add_to_waiting_list_if_not_available' => false,
],
]
);
$body = $response->getBody();
print_r(json_decode((string) $body));import requests
import json
url = 'https://api.shlott.com/v1/booking/42'
payload = {
"book_for": 77,
"resources": [
16
],
"members": [
16
],
"shared_with": [
16
],
"start": "2026-10-13 14:00",
"end": "2026-10-13 15:00",
"reason": 3,
"note": "architecto",
"email_to": 16,
"add_to_waiting_list_if_not_available": false
}
headers = {
'Authorization': 'Bearer {YOUR_AUTH_KEY}',
'Content-Type': 'application/json',
'Accept': 'application/json'
}
response = requests.request('PUT', url, headers=headers, json=payload)
response.json()Example response (200, Updated):
{
"message": "Booking updated.",
"status": "success",
"msg": "Booking updated.",
"booking_id": 42
}
Example response (401, Unauthenticated: no or invalid API key):
{
"message": "Unauthenticated."
}
Example response (403, Not allowed to change it):
{
"message": "You can only change bookings you are involved in."
}
Example response (404, Booking not found):
{
"message": "Not found."
}
Example response (422, Refused):
{
"message": "Chris already booked for this time slot.",
"errors": {
"user_booked": [
"Chris already booked for this time slot."
]
}
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Response
Response Fields
message
string
The result, in plain language. Use this. status and msg are the older fields, kept so existing clients keep working.
Cancel Booking
requires authentication
Cancels a booking. Admins can cancel any booking in their group; other members only
ones they are involved in, and only when the group allows members to cancel. A cancel
reason is required unless the booking is still awaiting approval. Cancelling frees the
slot for the first waiting-list booking that now fits (when the group uses a waiting
list) and sends the cancellation emails. The cancel_reasons and can_cancel fields of
Get Booking show what a user may do.
Example request:
const url = new URL(
"https://api.shlott.com/v1/booking/42/cancel"
);
const headers = {
"Authorization": "Bearer {YOUR_AUTH_KEY}",
"Content-Type": "application/json",
"Accept": "application/json",
};
let body = {
"reason": 2
};
fetch(url, {
method: "POST",
headers,
body: JSON.stringify(body),
}).then(response => response.json());$client = new \GuzzleHttp\Client();
$url = 'https://api.shlott.com/v1/booking/42/cancel';
$response = $client->post(
$url,
[
'headers' => [
'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
'Content-Type' => 'application/json',
'Accept' => 'application/json',
],
'json' => [
'reason' => 2,
],
]
);
$body = $response->getBody();
print_r(json_decode((string) $body));import requests
import json
url = 'https://api.shlott.com/v1/booking/42/cancel'
payload = {
"reason": 2
}
headers = {
'Authorization': 'Bearer {YOUR_AUTH_KEY}',
'Content-Type': 'application/json',
'Accept': 'application/json'
}
response = requests.request('POST', url, headers=headers, json=payload)
response.json()Example response (200, Cancelled):
{
"message": "Booking successfully cancelled.",
"status": "success",
"msg": "Booking successfully cancelled.",
"booked_from_waiting_list": false
}
Example response (401, Unauthenticated: no or invalid API key):
{
"message": "Unauthenticated."
}
Example response (403, Not allowed to cancel):
{
"message": "You can only cancel bookings you are involved in."
}
Example response (404, Booking not found):
{
"message": "Not found."
}
Example response (422, Already cancelled, or no reason):
{
"message": "Please select a reason for cancelling."
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Response
Response Fields
message
string
The result, in plain language. Use this. status and msg are the older fields, kept so existing clients keep working.
Noticeboard
List Notices
requires authentication
Returns the unexpired notices on a group's noticeboard, newest first. can_post says
whether the user may add notices, and each notice's can_edit whether they may edit or
delete it (its author, or a group admin). message is the notice exactly as it was
submitted (use it to pre-fill an edit form), html is a safe HTML rendering of it (basic
formatting tags and links only, with line breaks and bare links converted), and text is
the same content as plain text. Render html or text, never message.
Example request:
const url = new URL(
"https://api.shlott.com/v1/notices"
);
const params = {
"group_id": "5",
};
Object.keys(params)
.forEach(key => url.searchParams.append(key, params[key]));
const headers = {
"Authorization": "Bearer {YOUR_AUTH_KEY}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "GET",
headers,
}).then(response => response.json());$client = new \GuzzleHttp\Client();
$url = 'https://api.shlott.com/v1/notices';
$response = $client->get(
$url,
[
'headers' => [
'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
'Content-Type' => 'application/json',
'Accept' => 'application/json',
],
'query' => [
'group_id' => '5',
],
]
);
$body = $response->getBody();
print_r(json_decode((string) $body));import requests
import json
url = 'https://api.shlott.com/v1/notices'
params = {
'group_id': '5',
}
headers = {
'Authorization': 'Bearer {YOUR_AUTH_KEY}',
'Content-Type': 'application/json',
'Accept': 'application/json'
}
response = requests.request('GET', url, headers=headers, params=params)
response.json()Example response (200, Notices returned):
{
"can_post": true,
"notices": [
{
"id": 31,
"subject": "Club night",
"message": "Friday at 7pm. Map: https://example.com/map",
"html": "Friday at 7pm. Map: <a href=\"https://example.com/map\" target=\"_blank\" rel=\"noopener noreferrer\">https://example.com/map</a>",
"text": "Friday at 7pm. Map: https://example.com/map",
"expire_at": "2026-11-01",
"created_by": "Jane Smith",
"created_at": "2026-10-10T09:15:00.000000Z",
"can_edit": true
}
]
}
Example response (401, Unauthenticated: no or invalid API key):
{
"message": "Unauthenticated."
}
Example response (403, Not a member of the group):
{
"message": "Forbidden."
}
Example response (422, Validation error):
{
"message": "The group id field is required.",
"errors": {
"group_id": [
"The group id field is required."
]
}
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Add Notice
requires authentication
Adds a notice to a group's noticeboard. Group admins can always post; other members can
only post when the group allows members to post notices. Read-only members and members on
hold cannot post. The message may contain basic HTML (formatting tags and links); anything
else is stripped when it is displayed or emailed.
With send_to_all the notice is also emailed to every group member who has opted in to group
emails. Emailing the group is limited to 3 times a day per user, and posting to 10 notices an hour.
Example request:
const url = new URL(
"https://api.shlott.com/v1/notices"
);
const headers = {
"Authorization": "Bearer {YOUR_AUTH_KEY}",
"Content-Type": "application/json",
"Accept": "application/json",
};
let body = {
"group_id": 5,
"subject": "Club night",
"message": "Friday at 7pm.",
"expire_at": "2026-11-01",
"send_to_all": false
};
fetch(url, {
method: "POST",
headers,
body: JSON.stringify(body),
}).then(response => response.json());$client = new \GuzzleHttp\Client();
$url = 'https://api.shlott.com/v1/notices';
$response = $client->post(
$url,
[
'headers' => [
'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
'Content-Type' => 'application/json',
'Accept' => 'application/json',
],
'json' => [
'group_id' => 5,
'subject' => 'Club night',
'message' => 'Friday at 7pm.',
'expire_at' => '2026-11-01',
'send_to_all' => false,
],
]
);
$body = $response->getBody();
print_r(json_decode((string) $body));import requests
import json
url = 'https://api.shlott.com/v1/notices'
payload = {
"group_id": 5,
"subject": "Club night",
"message": "Friday at 7pm.",
"expire_at": "2026-11-01",
"send_to_all": false
}
headers = {
'Authorization': 'Bearer {YOUR_AUTH_KEY}',
'Content-Type': 'application/json',
'Accept': 'application/json'
}
response = requests.request('POST', url, headers=headers, json=payload)
response.json()Example response (201, Notice added):
{
"id": 31
}
Example response (401, Unauthenticated: no or invalid API key):
{
"message": "Unauthenticated."
}
Example response (403, Not a member of the group, or not allowed to post):
{
"message": "Forbidden."
}
Example response (422, Validation error):
{
"message": "The subject field must be at least 3 characters.",
"errors": {
"subject": [
"The subject field must be at least 3 characters."
]
}
}
Example response (429, Too many notices posted, or the group was emailed too often today):
{
"message": "You have emailed the group too many times today. Please try again tomorrow."
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Update Notice
requires authentication
Changes a notice's subject, message and expiry date. Group admins can change any notice in their group; other members only the ones they wrote (and not if they are read-only or on hold). The notice is not re-emailed.
Example request:
const url = new URL(
"https://api.shlott.com/v1/notices/31"
);
const headers = {
"Authorization": "Bearer {YOUR_AUTH_KEY}",
"Content-Type": "application/json",
"Accept": "application/json",
};
let body = {
"subject": "Club night moved",
"message": "Now on Saturday at 7pm.",
"expire_at": "2026-11-01"
};
fetch(url, {
method: "PUT",
headers,
body: JSON.stringify(body),
}).then(response => response.json());$client = new \GuzzleHttp\Client();
$url = 'https://api.shlott.com/v1/notices/31';
$response = $client->put(
$url,
[
'headers' => [
'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
'Content-Type' => 'application/json',
'Accept' => 'application/json',
],
'json' => [
'subject' => 'Club night moved',
'message' => 'Now on Saturday at 7pm.',
'expire_at' => '2026-11-01',
],
]
);
$body = $response->getBody();
print_r(json_decode((string) $body));import requests
import json
url = 'https://api.shlott.com/v1/notices/31'
payload = {
"subject": "Club night moved",
"message": "Now on Saturday at 7pm.",
"expire_at": "2026-11-01"
}
headers = {
'Authorization': 'Bearer {YOUR_AUTH_KEY}',
'Content-Type': 'application/json',
'Accept': 'application/json'
}
response = requests.request('PUT', url, headers=headers, json=payload)
response.json()Example response (200, Notice updated):
{
"id": 31
}
Example response (401, Unauthenticated: no or invalid API key):
{
"message": "Unauthenticated."
}
Example response (403, Not a member of the notice's group, or not allowed to change it):
{
"message": "Forbidden."
}
Example response (404, Notice not found):
{
"message": "Not found."
}
Example response (422, Validation error):
{
"message": "The message field is required.",
"errors": {
"message": [
"The message field is required."
]
}
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Delete Notice
requires authentication
Removes a notice from the noticeboard. Group admins can delete any notice in their group; other members only the ones they wrote (and not if they are read-only or on hold).
Example request:
const url = new URL(
"https://api.shlott.com/v1/notices/31"
);
const headers = {
"Authorization": "Bearer {YOUR_AUTH_KEY}",
"Content-Type": "application/json",
"Accept": "application/json",
};
fetch(url, {
method: "DELETE",
headers,
}).then(response => response.json());$client = new \GuzzleHttp\Client();
$url = 'https://api.shlott.com/v1/notices/31';
$response = $client->delete(
$url,
[
'headers' => [
'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
'Content-Type' => 'application/json',
'Accept' => 'application/json',
],
]
);
$body = $response->getBody();
print_r(json_decode((string) $body));import requests
import json
url = 'https://api.shlott.com/v1/notices/31'
headers = {
'Authorization': 'Bearer {YOUR_AUTH_KEY}',
'Content-Type': 'application/json',
'Accept': 'application/json'
}
response = requests.request('DELETE', url, headers=headers)
response.json()Example response (200, Notice deleted):
{
"deleted": true
}
Example response (401, Unauthenticated: no or invalid API key):
{
"message": "Unauthenticated."
}
Example response (403, Not a member of the notice's group, or not allowed to delete it):
{
"message": "Forbidden."
}
Example response (404, Notice not found):
{
"message": "Not found."
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.
Tickets
Submit Ticket
requires authentication
Raises a support ticket on behalf of the authenticated user in the given group (their
primary group when group_id is omitted). The ticket appears in the group's support
ticket list in Shlott, where it is handled like any other ticket. The user must be an
active member of the group. The optional screen, app_version, platform,
os_version and device_model fields let your application attach diagnostic detail to
the ticket.
Example request:
const url = new URL(
"https://api.shlott.com/v1/tickets"
);
const headers = {
"Authorization": "Bearer {YOUR_AUTH_KEY}",
"Content-Type": "application/json",
"Accept": "application/json",
};
let body = {
"category": "bug",
"message": "The calendar freezes when I rotate my phone.",
"group_id": 5,
"screen": "calendar",
"app_version": "1.0.0",
"platform": "ios",
"os_version": "19.1",
"device_model": "iPhone 16"
};
fetch(url, {
method: "POST",
headers,
body: JSON.stringify(body),
}).then(response => response.json());$client = new \GuzzleHttp\Client();
$url = 'https://api.shlott.com/v1/tickets';
$response = $client->post(
$url,
[
'headers' => [
'Authorization' => 'Bearer {YOUR_AUTH_KEY}',
'Content-Type' => 'application/json',
'Accept' => 'application/json',
],
'json' => [
'category' => 'bug',
'message' => 'The calendar freezes when I rotate my phone.',
'group_id' => 5,
'screen' => 'calendar',
'app_version' => '1.0.0',
'platform' => 'ios',
'os_version' => '19.1',
'device_model' => 'iPhone 16',
],
]
);
$body = $response->getBody();
print_r(json_decode((string) $body));import requests
import json
url = 'https://api.shlott.com/v1/tickets'
payload = {
"category": "bug",
"message": "The calendar freezes when I rotate my phone.",
"group_id": 5,
"screen": "calendar",
"app_version": "1.0.0",
"platform": "ios",
"os_version": "19.1",
"device_model": "iPhone 16"
}
headers = {
'Authorization': 'Bearer {YOUR_AUTH_KEY}',
'Content-Type': 'application/json',
'Accept': 'application/json'
}
response = requests.request('POST', url, headers=headers, json=payload)
response.json()Example response (201, Ticket created):
{
"ticket_ref": "T00123"
}
Example response (401, Unauthenticated: no or invalid API key):
{
"message": "Unauthenticated."
}
Example response (403, Not a member of the group):
{
"message": "You are not a member of this group."
}
Example response (422, Validation error):
{
"message": "The category field must be one of: bug, suggestion, question, other.",
"errors": {
"category": [
"The category field must be one of: bug, suggestion, question, other."
]
}
}
Example response (429, Too many submissions (5 a minute)):
{
"message": "Too Many Attempts."
}
Received response:
Request failed with error:
Tip: Check that you're properly connected to the network.
If you're a maintainer of ths API, verify that your API is running and you've enabled CORS.
You can check the Dev Tools console for debugging information.