Surf Booking.PTOCTO
ManifestoAgentIntelligence LayerTechnologyConstitutionAIMCPPartner API
Partner API

One integration.A thousand channels.

Let your customers book surf lessons directly in your platform. REST · JSON · OCTO — real availability from every school, readable by any marketplace or channel manager. Connect once, reach many.

API live · Bearer / X-Partner-API-Key
surfbooking.eu/developers
/api/partner/v1/availabilities
GET
{
  "spot":     "Carcavelos",
  "date":     "2026-06-22",
  "time":     "10:00",
  "level":    "iniciante",
  "capacity": 6,
  "vacancy":  4
}
Open platform

No closed protocol. We speak OCTO — the open standard for activity bookings. Integrate once, reach everyone.

OCTO (octo.travel) is how activity booking systems, channel managers and marketplaces talk to each other. One integration against SurfBooking, and your offer becomes readable by any OCTO buyer. For those who prefer, we also return the native SB format and variants for existing adapters.

Who connects

Five types of partner. One API.

If you distribute experiences — a hotel, an agency, a DMC, a tourism app or an accommodation platform — the SurfBooking API puts real-time surf lesson availability in your hands.

Hotels

Experience at check-in

Offer surf lessons as an add-on for guests, with real-time booking — no phone calls, no manual coordination.

Travel agencies

Surf packages

Include surf lessons in any travel package, bookable directly via API, with idempotency on duplicate requests.

DMCs

Surf in the itinerary

Add surf to any group itinerary and confirm real-time availability, with live opening counts.

Tourism apps

Listings + live availability

Show schools, spots and live availability in your app. The forecast scope adds surf conditions per spot.

Accommodation

Upsell experiences

Short-term rentals and apartments upsell surf at checkout — confirmed in seconds via API, no manual back-and-forth.

How it works

In 5 seconds, any CTO understands it.

Your platform
Hotel · OTA · Agency · App
SurfBooking API
REST · JSON · OCTO
Schools
Availability
Payments
Bookings

One integration. Every school on the platform. Real openings, real prices, anti-overbooking — confirmed in milliseconds.

Different partners, different access

Each partner connects only to what serves them.

It's not the same API for everyone. Each key is limited (least-privilege) to what that partner needs — never more than that.

Hotels · OTAs · channel managers

Availability + bookings

They read the available lessons and create bookings on behalf of the guest/customer. The v1 path (OCTO). Access for those who distribute lessons.

Equipment brands · apps · AI

Sizing + conditions

They recommend the right board/wetsuit and read the spot conditions. The v2 path, with scope sizing and/or forecast — they never touch bookings.

Partners

Attribution, no API

They bring customers and earn commission via /aff + a 90-day cookie. No key, no technical integration.

Platform keys carry scopes. A sizing key only opens the sizing endpoints — everything else returns 403 insufficient_scope.

What the API does

Three things. Availability, services, bookings.

All in REST + JSON, authenticated by the school's key.

availabilities

Read availability

The real lessons with day, time, spot, level, openings and price. Filter by date and level. Formats sb · fh · bl · octo.

services

List services

The school's lesson types (level, group/private, price range) — in native SB or as OCTO products.

bookings

Create and manage bookings

Create, query and cancel bookings on behalf of the end customer. Idempotent by externalRef, with revalidation of openings and ratios.

Live

One call and you see the API respond.

The descriptor is public. Paste into the terminal:

terminal · API descriptor
# view the API (no key)
curl -s https://www.surfbooking.eu/api/partner/v1

Full, machine-readable contract — designed for AI agents and SDK generators to auto-discover the API: /openapi.json (OpenAPI 3.1) · interactive reference at /docs (Swagger UI).

OCTO
For OTAs & resellers
Products, availability and two-step booking (hold → confirm). Bókun/Rezdy-style plug-and-play.
iCal
Universal, both ways
Import any channel's feed into the SB calendar — and export SB's calendar (with cancellations) back into the channel.
MCP
For AI agents
11 tools — search lessons, live surf score per break, equipment sizing — over the Model Context Protocol.

Two-step booking in two calls — hold the spot while your customer pays, then confirm:

# 1 — hold (expira sozinho; a vaga fica segura)
curl -X POST https://www.surfbooking.eu/api/partner/v1/bookings   -H "X-Partner-API-Key: A_TUA_CHAVE" -H "Content-Type: application/json"   -d '{"lessonSlotId":"…","customerName":"…","customerEmail":"…",
       "status":"ON_HOLD","expirationMinutes":30}'

# 2 — confirm (idempotente; hold expirado devolve 410 e liberta a vaga)
curl -X POST https://www.surfbooking.eu/api/partner/v1/bookings/{id}/confirm   -H "X-Partner-API-Key: A_TUA_CHAVE"

With the school's key, you read the availability — here in OCTO format:

terminal · availability (OCTO)
curl -s "https://www.surfbooking.eu/api/partner/v1/availabilities?format=octo&from=2026-07-01&to=2026-07-07" \
  -H "Authorization: Bearer sbpk_xxxxxxxxxxxxxxxxxxxxxxxx"

Limit: 120 requests/min per key. Each response carries X-RateLimit-Limit and X-RateLimit-Remaining; when you exceed it you get 429 with Retry-After: 60.

Authentication

One key per school. You're in control.

Each school generates its own key in SurfBooking OS — and revokes it whenever it wants. The key goes in a header, in either of two formats:

X-Partner-API-Key

Native SB header

X-Partner-API-Key: sbpk_xxxx… — SurfBooking's canonical format.

Authorization: Bearer

OCTO-compatible

Authorization: Bearer sbpk_xxxx… — for clients that speak OCTO. The same key.

Generate and revoke keys in SurfBooking OS → External Channels → Partner API. We never store the key in plaintext — only its digest.

Reference

The endpoints.

Base: https://www.surfbooking.eu/api/partner/v1

GET

/

API descriptor (root, no key). Returns name, version, auth, octo and the full map of endpoints. Opening it in a browser redirects to this page.

GET

/availabilities

Bookable lessons. Parameters: from, to, level?, format? (sb·fh·bl·octo). Defaults: from=today, to=+30 days.

GET

/availability

OCTO alias of /availabilities — assumes format=octo by default (plug-and-play OCTO).

GET

/services

The school's lesson types. format? (sb·octo) — in OCTO it returns products.

GET

/products

OCTO alias of /services — assumes format=octo by default. Array of OCTO products.

GET

/supplier

Supplier identity (OCTO supplier descriptor): id, name, endpoint, contact, locales, timeZone.

POST

/bookings

Creates a booking. Body: lessonSlotId, customerName, customerEmail, customerPhone?, participants?, externalRef?, notes?.

GET

/bookings/:id

Queries the state of a booking.

DELETE

/bookings/:id

Cancels a booking.

The descriptor — GET /api/partner/v1 (no key):

response · descriptor
{
  "name": "SurfBooking Partner API v1",
  "version": "1.0.0",
  "auth": "X-Partner-API-Key header, ou Authorization: Bearer  (compatível OCTO).",
  "octo": {
    "supported": true,
    "formatParam": "octo",
    "supplierEndpoint": "/api/partner/v1/supplier"
  },
  "endpoints": {
    "availabilities": { "method": "GET", "path": "/api/partner/v1/availabilities" },
    "services":       { "method": "GET", "path": "/api/partner/v1/services" },
    "supplier":       { "method": "GET", "path": "/api/partner/v1/supplier" },
    "createBooking":  { "method": "POST", "path": "/api/partner/v1/bookings" },
    "getBooking":     { "method": "GET", "path": "/api/partner/v1/bookings/:id" },
    "cancelBooking":  { "method": "DELETE", "path": "/api/partner/v1/bookings/:id" }
  }
}

Supplier identity — GET /supplier (OCTO):

response · supplier (OCTO)
{
  "id": "school-uuid-…",
  "name": "Nome da Escola",
  "endpoint": "https://www.surfbooking.eu/api/partner/v1",
  "contact": { "website": "https://www.surfbooking.eu", "email": null, "telephone": null, "address": null },
  "locales": ["pt", "en"],
  "timeZone": "Europe/Lisbon",
  "_sb": { "description": "…", "logoUrl": "…" }
}
Examples

Request and response, for real.

The body of a booking and what you get back — exactly as the API responds today.

Create a booking — POST /bookings:

request · POST /bookings
curl -s -X POST https://www.surfbooking.eu/api/partner/v1/bookings \
  -H "Authorization: Bearer sbpk_xxxxxxxxxxxxxxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{
    "lessonSlotId": "a1b2c3d4-…",
    "customerName": "Ana Silva",
    "customerEmail": "[email protected]",
    "customerPhone": "+351912345678",
    "participants": 2,
    "externalRef": "BOKUN-7782",
    "notes": "Primeira aula"
  }'

Response — 201 Created:

response · 201
{
  "ok": true,
  "bookingId": "f1e2d3c4-…",
  "createdAt": "2026-06-21T11:42:00.000Z",
  "lessonSlotId": "a1b2c3d4-…",
  "schoolId": "…",
  "status": "confirmed",
  "participants": 2,
  "totalAmountCents": 7000,
  "currency": "EUR"
}

Availability in OCTO — GET /availabilities?format=octo returns an array:

response · availabilities (OCTO)
[
  {
    "id": "a1b2c3d4-…",
    "localDateTimeStart": "2026-07-02T10:00:00",
    "localDateTimeEnd": "2026-07-02T12:00:00",
    "available": true,
    "status": "AVAILABLE",
    "vacancies": 4,
    "capacity": 6,
    "maxUnits": 4,
    "pricing": {
      "original": 3500, "retail": 3500, "net": 3500,
      "currency": "EUR", "currencyPrecision": 2
    },
    "_sb": { "level": "beginner", "spotName": "…", "bookingUrl": "https://www.surfbooking.eu/aulas/…" }
  }
]

Prices in cents (minor units) — 3500 = €35.00, per OCTO's currencyPrecision: 2. Dates come in the school's local time (Europe/Lisbon).

The same availability in other formats. Native SB — ?format=sb (envelope with _meta):

response · availabilities (SB)
{
  "availabilities": [
    {
      "id": "a1b2c3d4-…",
      "date": "2026-07-02",
      "startTime": "10:00",
      "endTime": "12:00",
      "level": "beginner",
      "spotsTotal": 6,
      "spotsRemaining": 4,
      "pricePerPerson": 3500,
      "currency": "EUR",
      "instructorName": "…",
      "spotName": "…",
      "schoolId": "…",
      "schoolName": "…",
      "bookingUrl": "https://www.surfbooking.eu/aulas/a1b2c3d4-…"
    }
  ],
  "_meta": { "format": "sb", "total": 1, "from": "2026-07-01", "to": "2026-07-07" }
}

FareHarbor — ?format=fh (availabilities envelope):

response · availabilities (FH)
{
  "availabilities": [
    {
      "pk": "a1b2c3d4-…",
      "start_at": "2026-07-02T10:00:00",
      "end_at": "2026-07-02T12:00:00",
      "capacity": 6,
      "num_remaining": 4,
      "is_available": true,
      "customer_type_rates": [
        { "pk": "a1b2c3d4-…_default", "total": { "amount": "35.00", "currency": "EUR" } }
      ],
      "_sb_meta": { "level": "beginner", "bookingUrl": "https://www.surfbooking.eu/aulas/a1b2c3d4-…" }
    }
  ]
}

Booking Layer — ?format=bl (data envelope):

response · availabilities (BL)
{
  "data": [
    {
      "id": "a1b2c3d4-…",
      "date": "2026-07-02",
      "startTime": "10:00",
      "endTime": "12:00",
      "serviceName": "Surf beginner",
      "capacity": 6,
      "spotsLeft": 4,
      "pricePerPerson": 3500,
      "currency": "EUR",
      "_sb_meta": { "level": "beginner", "bookingUrl": "https://www.surfbooking.eu/aulas/a1b2c3d4-…" }
    }
  ]
}

In octo the response is a plain array (no envelope). In sb/fh it comes in availabilities; in bl, in data.

Errors

Status codes.

Each error carries a JSON body { "error": "…" } — machine-readable.

401

missing_api_key · invalid_api_key

Key missing, too short, invalid or revoked. Includes a hint when the header is missing.

400

missing_fields · invalid_email · invalid_date_range

Incomplete body, malformed email, or invalid date range (max 180 days, from ≤ to).

404

slot_not_found · booking_not_found · booking_not_found_or_already_cancelled

The lesson or booking doesn't exist at this school — or, on DELETE, it had already been cancelled.

409

insufficient_spots · slot_expired · slot_not_purchasable · ratio_exceeded · external_ref_exists

State conflict: no openings, lesson in the past, closed to sale, instructor ratio exceeded, or repeated externalRef (idempotency — returns the bookingId that already exists).

429

rate_limit_exceeded

Above 120 requests/min per key. Wait for Retry-After (60s). Each response carries X-RateLimit-Remaining.

Missing or invalid key — 401:

response · 401
# no key header
{
  "error": "missing_api_key",
  "hint": "Set X-Partner-API-Key header (or Authorization: Bearer)"
}

# nonexistent or revoked key
{ "error": "invalid_api_key" }

Not enough openings (anti-overbooking) — 409:

response · 409
# the lesson no longer has room for the number of participants
{
  "error": "insufficient_spots",
  "remaining": 1
}

# repeated externalRef → idempotency: returns the existing bookingId
{
  "error": "external_ref_exists",
  "bookingId": "f1e2d3c4-…"
}

Revalidation of openings and ratios runs inside a transaction with SELECT … FOR UPDATE — sold out is sold out, there's never double-booking.

Sizing + conditions · v2

Recommend the right gear. Without touching bookings.

The v2 path for equipment brands, apps and AI. Platform key with scope sizing, forecast and/or brand:read — deterministic, no personal data, never touches bookings. Discover your scopes at /api/partner/v2/meta. Base: https://www.surfbooking.eu/api/partner/v2.

GET

/sizing/board

Recommended board by level + weight. Scope sizing.

GET

/sizing/board/conditions

Board tuned to the day: level, weight, swellPeriodS, swellDirectionDeg, bestSwellDirDeg. Scope sizing.

GET

/sizing/wetsuit

Wetsuit size by weight + height. Scope sizing.

GET

/sizing/leash

Leash by boardSize + category (board·soft_board). Scope sizing.

GET

/sizing/boots

Boots by shoeSizeEu (e.g.: 42). Scope sizing.

GET

/spots

Lists the spots you can query — use an id from here in /conditions/:spotId. Scope forecast.

GET

/conditions/:spotId

Spot conditions — the same public shape, sanitized (never exposes model or sources). Scope forecast.

GET

/intelligence/surf-now

The best spots in the country right now, ranked by the live score — limit + level. Scope forecast.

GET

/brand/equipment-stats

For equipment brands: how many times each size of your brand was used in lessons, by month (YYYY-MM). Only your data. Scope brand:read.

GET

/brand/spot-activity

Which spots your brand's equipment was most used at, last months (1–12). Scope brand:read.

Board recommendation — GET /sizing/board:

terminal · sizing (v2)
curl -s "https://www.surfbooking.eu/api/partner/v2/sizing/board?level=beginner&weight=75" \
  -H "X-Partner-API-Key: sbpd_xxxxxxxxxxxxxxxxxxxxxxxx"
response · sizing
{ "recommendation": { "category": "soft_board", "size": "8'0" } }

Platform keys (sbpd_…) are issued by SurfBooking, not by the school. Wrong scope → 403 insufficient_scope. Limit 240 requests/min per key, with Retry-After: 60 when exceeded.

Embeddable widget

Sizing on your site. One line.

A self-contained iframe page, "Powered by SurfBooking". It shows the spot conditions, recommends the board by weight and level, and takes the customer to book a lesson — with your premium partner link built in. PII-free, no tracking cookies.

Live example — this is exactly what your visitors see:
html · embed
<iframe
  src="https://www.surfbooking.eu/w/surf?key=sbpd_xxxx&spot=SPOT_ID&slug=O_TEU_SLUG"
  width="360" height="520" frameborder="0"></iframe>

key = your platform key (scope sizing, and forecast if you want the conditions at the top). spot and slug are optional — the slug links the bookings generated to your /aff (90-day cookie).

Versions

v1 — stable.

The base is /api/partner/v1. Breaking changes go into a new version (/v2) — v1 has no deprecation planned. We may add fields without notice, so read tolerantly: ignore what you don't yet know.

Who it's for

Channel managers, marketplaces, hotels.

Any partner that wants to sell or show real surf lessons. Designed on the open OCTO standard, which many booking systems and channel managers already speak.

Hotels & resorts

Offer real lessons to guests without managing anything — the booking and payment stay on SurfBooking.

Channel managers

Sync availability in real time, no double-booking. OCTO out of the box.

Marketplaces

List the schools' offer and route the booking — with openings and ratios always revalidated on our side.

Your safety

The school is in charge. Always.

KEY

Per school, revocable

Each key sees only its own school's offer. Revoke in one click and access dies instantly.

OPENINGS

Never any overbooking

Each booking revalidates openings and instructor ratios inside a transaction. Sold out is sold out.

PAYMENT

Confirmed on SurfBooking

The money flow stays on our side — the partner routes, SurfBooking confirms.

STUDENTS

Protected data

Only what's needed for the booking goes out. Your students' contacts don't become anyone's list.

Ready to connect

Get a key and start.

The API is live. If you run a surf school, generate your own key in SurfBooking OS. If you're an external partner — an equipment brand, app, AI or hotel — request a platform key (issued by SurfBooking).

I'm a school · Open SurfBooking OS I'm an external partner · Request access View the descriptor