Oakline provides a Public Sessions API allowing tenants to display their session catalogue on external websites or applications. This guide assumes technical knowledge of HTTP APIs.
The API is read-only and exposes:
[!IMPORTANT] The API does not support booking creation. The checkout must take place on your Oakline portal. All
booking_urlvalues in the response link directly to the correct booking page.
API access is disabled by default. To enable it:
All API requests require an API key passed as a request header:
X-API-Key: oak_your_key_here
Requests without a valid key return 401 Unauthorized. Requests to a tenant with API access disabled return 403 Forbidden.
https://oakline.app/api/public/tenants/{tenant_slug}
{tenant_slug} — your subdomain identifier (e.g. forestfamilies for forestfamilies.oakline.app).Both versioned and unversioned paths are supported:
/api/public/tenants/{slug}/sessions ← current
/api/v1/public/tenants/{slug}/sessions ← v1 alias (identical)
Returns upcoming session occurrences ordered by your custom sort order, then chronologically.
GET /sessions
| Name | Type | Required | Description |
|---|---|---|---|
from |
string | No | Start date YYYY-MM-DD. Defaults to today. |
to |
string | No | End date YYYY-MM-DD. |
limit |
int | No | Max results. Default: 500. Max: 1000. |
cursor |
string | No | Pagination cursor from previous response. |
GET /api/public/tenants/forestfamilies/sessions?from=2026-04-01&limit=20
X-API-Key: oak_your_key_here
{
"data": [
{
"id": 101,
"template_id": 12,
"title": "Summer Camp",
"description": "A full week of outdoor activities...",
"description_short": "A full week of outdoor activities...",
"start_at": "2026-06-15T09:00:00",
"end_at": "2026-06-15T15:00:00",
"location_name": "Forest Park, Oak Lane",
"price": {
"amount": 25.00,
"currency": "GBP"
},
"capacity": {
"total": 20,
"remaining": 5,
"is_full": false
},
"age_range": {
"min": 3,
"max": 11
},
"image_url": "https://...",
"sort_order": 1,
"booking_url": "https://forestfamilies.oakline.app/booking?session_id=101"
}
],
"pagination": {
"limit": 20,
"has_more": false,
"next_cursor": null
},
"tenant": {
"theme_color": "#2C5F2D",
"secondary_color": "#6c757d",
"vat_number": "GB123456789"
}
}
is_full: true are still returned — show them as "Sold Out" rather than hiding them.tenant object provides your brand colours for styling the widget to match your portal.cancelled are excluded automatically.Returns full details for one occurrence.
GET /sessions/{id}
{id} — the occurrence ID from a list response.{
"data": {
"id": 101,
"title": "Summer Camp",
"description": "Full description text...",
"image_url": "https://...",
"location": {
"name": "Forest Park, Oak Lane",
"map_url": "https://maps.google.com/?q=..."
},
"schedule": {
"date": "2026-06-15",
"start_time": "09:00:00",
"end_time": "15:00:00"
},
"price": {
"amount": 25.00,
"currency": "GBP"
},
"capacity": {
"total": 20,
"remaining": 5,
"status": "available"
},
"policies": {
"cancellation_deadline_hours": 48
}
}
}
Pagination uses an opaque cursor token:
pagination.has_more in the response.true, pass pagination.next_cursor as the cursor query parameter in your next request.has_more is false.GET /sessions?limit=20&cursor=MjAyNi0wNi0xNXwxMDE=
| Limit | Window |
|---|---|
| 60 requests | per 60 seconds, per IP |
Exceeded requests return 429 Too Many Requests. Implement exponential backoff in your client.
Responses include standard HTTP cache headers:
Cache-Control: public, max-age=60 — responses are valid for 60 seconds.ETag — compare against If-None-Match to receive 304 Not Modified and avoid re-parsing unchanged data.All errors follow a consistent shape:
{ "error": "Description of the problem" }
| Status | Meaning |
|---|---|
401 |
Missing or invalid API key |
403 |
API not enabled for this tenant |
404 |
Tenant or session not found |
429 |
Rate limit exceeded |
500 |
Internal server error |
is_full — display sold-out sessions with a disabled state rather than hiding them. This gives parents a better picture of your programme.booking_url for all "Book Now" links — do not construct booking URLs manually.