Intégration IA / AI integration
Guide API pour assistants IA (anglais) / API integration guide for AI assistants (English).
Guide original en anglais à transmettre à votre assistant IA. Original guide in English to share with your AI assistant.
Télécharger le Markdown / Download Markdown
Use this file when an LLM must generate code that integrates a club website with the Okkre public API.
Core Contract
Base URL:
/api/v1/clubs/{clubId}
Authentication:
Authorization: Bearer <api_key>
Every request needs both values: {clubId} in the URL identifies the club and the API key authorizes the request. The key must belong to that club.
Scopes:
read: read courts, availability, API-created reservations.book: allreadpermissions plus create/cancel reservations.
Never expose a book key in browser code. Prefer server-side calls.
Endpoints
| Method | Path | Scope | Purpose |
|---|---|---|---|
GET | /courts | read | List active courts. |
GET | /availability?from=YYYY-MM-DD&to=YYYY-MM-DD&sport=padel&court_id=... | read | List available slots using the club's configured duration and price. |
POST | /reservations | book | Create an idempotent reservation. |
GET | /reservations/{externalId} | read | Read one API-created reservation. |
DELETE | /reservations/{externalId} | book | Cancel one API-created reservation. |
POST | /reservations/{externalId}/cancel | book | Cancel one API-created reservation. |
Data Shapes
Court response:
{
"courts": [
{
"id": "uuid",
"name": "Terrain 1",
"sport": "padel",
"environment": "indoor",
"display_order": 1
}
]
}
Availability response:
{
"availability": [
{
"court": "Terrain 1",
"date": "2026-09-12",
"start_time": "18:00",
"end_time": "20:00",
"price_cents": 2400
}
]
}
Create reservation request:
{
"external_id": "site-order-123",
"court": "Terrain 1",
"date": "2026-09-12",
"start_time": "18:00",
"end_time": "19:30",
"person": {
"first_name": "Ada",
"last_name": "Lovelace",
"email": "ada@example.com"
},
"notes": "Optional"
}
Create/read/cancel reservation response:
{
"reservation": {
"id": "uuid",
"external_id": "site-order-123",
"court_id": "uuid",
"starts_at": "2026-09-12T16:00:00.000Z",
"ends_at": "2026-09-12T17:30:00.000Z",
"price_cents": 2400,
"status": "confirmed",
"cancellation_reason": null
},
"idempotent_replay": false
}
GET /reservations/{externalId} returns the same reservation object without idempotent_replay.
Error shape:
{
"error": {
"code": "slot_unavailable",
"message": "Ce terrain est deja occupe sur ce creneau."
}
}
Validation Rules
from,to,date:YYYY-MM-DD.- Availability range: 31 days maximum.
- Times:
HH:MM, 24-hour format. - Reservation start must be on the hour or half-hour.
- Reservation duration must be positive and a multiple of 30 minutes.
court: readable court name is preferred; UUID also works.person.emailorperson.phoneis required.person.email: basic email format.person.phone: 6 to 30 characters.person.first_nameandperson.last_name: required, max 80 characters each.notes: optional, truncated to 500 characters.- Cancellation
reason: optional, truncated to 300 characters.
Integration Algorithm
- On page load, call
GET /courtsor directly callGET /availability. - Show availability slots to the user. Slots are returned in local club date and time fields, with
price_centsfor the complete configured slot. Send the returned start and end times unchanged when booking. - When the user confirms, call
POST /reservationsfrom the backend with a stableexternal_id. - If response is
201or200withidempotent_replay: true, treat the reservation as created. - If response is
409anderror.codeisslot_unavailable, refresh availability and ask the user to choose another slot. - To cancel, call either
DELETE /reservations/{externalId}orPOST /reservations/{externalId}/cancel.
TypeScript Helper
type OkkreReservationInput = {
external_id: string;
court: string;
date: string;
start_time: string;
end_time: string;
person: {
first_name: string;
last_name: string;
email?: string;
phone?: string;
};
notes?: string;
};
async function okkreFetch<T>(
baseUrl: string,
apiKey: string,
path: string,
init: RequestInit = {},
): Promise<T> {
const response = await fetch(`${baseUrl}${path}`, {
...init,
headers: {
Authorization: `Bearer ${apiKey}`,
"Content-Type": "application/json",
...init.headers,
},
});
const data = await response.json().catch(() => null);
if (!response.ok) {
const code = data?.error?.code ?? "unknown_error";
const message = data?.error?.message ?? `Okkre API error ${response.status}`;
throw new Error(`${code}: ${message}`);
}
return data as T;
}
export async function createOkkreReservation(
baseUrl: string,
apiKey: string,
input: OkkreReservationInput,
) {
return okkreFetch(baseUrl, apiKey, "/reservations", {
method: "POST",
body: JSON.stringify(input),
});
}
Common Error Handling
missing_api_key,invalid_authorization_header,api_key_not_found,api_key_revoked: ask the admin to check the API key.api_key_wrong_club: check the{clubId}in the URL.insufficient_scope: use abookkey for create/cancel.validation_error: fix request fields.invalid_period: fix availability date range.reservation_not_found: unknown or non-API reservation.slot_unavailable: refresh availability and retry with another slot.