{"openapi":"3.1.0","info":{"title":"SurfBooking Partner API","version":"1.0.0","summary":"Read availability, create bookings, and pull the surf-intelligence layer (sizing + conditions).","description":"Two surfaces:\n\n- **v1 (OCTO-compatible)** — list lesson availability, list products/services, identify the supplier, and create/cancel bookings on SurfBooking. OCTO aliases (`/availability`, `/products`, `/supplier`) default to OCTO response shape.\n- **v2 (Surf-intelligence)** — read-only equipment sizing and per-spot conditions. Scoped per key.\n\nAuth: every request needs a partner API key in the `X-Partner-API-Key` header (or `Authorization: Bearer`). Rate limit: 120 req/min/key on v1, 240 on v2 → `429` with `Retry-After`. Idempotency: send `externalRef` on a booking — a retry with the same `externalRef` returns `409 external_ref_exists` carrying the original `bookingId` (the booking is created once). Call `GET /api/partner/v2/meta` to see your key's scopes (`sizing`, `forecast`) + the live endpoint list.","contact":{"name":"SurfBooking Partners","url":"https://www.surfbooking.eu/developers"}},"servers":[{"url":"https://www.surfbooking.eu","description":"Production"}],"security":[{"PartnerApiKey":[]},{"BearerKey":[]}],"tags":[{"name":"v1 · OCTO","description":"Availability, products, supplier, bookings (OCTO-compatible)."},{"name":"v2 · Intelligence","description":"Equipment sizing + per-spot conditions (read-only)."}],"paths":{"/api/partner/v1/supplier":{"get":{"tags":["v1 · OCTO"],"operationId":"getSupplier","summary":"Supplier identity (OCTO /supplier)","description":"Identity of the supplier (the school that owns the API key).","parameters":[{"name":"X-Partner-API-Key","in":"header","required":true,"schema":{"type":"string","example":"sbpk_xxxxxxxxxxxxxxxxxxxxxxxx"},"description":"Partner API key issued by SurfBooking. May also be sent as `Authorization: Bearer <key>`."}],"responses":{"200":{"description":"Supplier object"},"401":{"description":"Invalid key","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string","description":"Short machine code for the failure.","examples":["missing_api_key","invalid_api_key","insufficient_scope","rate_limit_exceeded","missing_fields","invalid_email","slot_not_found","slot_not_purchasable","slot_expired","insufficient_spots","ratio_exceeded","external_ref_exists","booking_not_found"]}},"additionalProperties":true}}}}}}},"/api/partner/v1/availabilities":{"get":{"tags":["v1 · OCTO"],"operationId":"listAvailabilities","summary":"List bookable lessons","description":"Upcoming bookable lesson slots in a date range. Alias `/api/partner/v1/availability` (OCTO) defaults to OCTO response shape.","parameters":[{"name":"X-Partner-API-Key","in":"header","required":true,"schema":{"type":"string","example":"sbpk_xxxxxxxxxxxxxxxxxxxxxxxx"},"description":"Partner API key issued by SurfBooking. May also be sent as `Authorization: Bearer <key>`."},{"name":"from","in":"query","required":false,"schema":{"type":"string","format":"date","example":"2026-07-01"},"description":"Earliest date (YYYY-MM-DD)."},{"name":"to","in":"query","required":false,"schema":{"type":"string","format":"date","example":"2026-07-14"},"description":"Latest date (YYYY-MM-DD)."},{"name":"format","in":"query","required":false,"schema":{"type":"string","enum":["octo","native","fh","bl","sb"]},"description":"Response shape. Defaults to `octo` on the `/availability` alias, `native` otherwise."}],"responses":{"200":{"description":"Array of availability objects"},"401":{"description":"Invalid key","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string","description":"Short machine code for the failure.","examples":["missing_api_key","invalid_api_key","insufficient_scope","rate_limit_exceeded","missing_fields","invalid_email","slot_not_found","slot_not_purchasable","slot_expired","insufficient_spots","ratio_exceeded","external_ref_exists","booking_not_found"]}},"additionalProperties":true}}}},"429":{"description":"Rate limited","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string","description":"Short machine code for the failure.","examples":["missing_api_key","invalid_api_key","insufficient_scope","rate_limit_exceeded","missing_fields","invalid_email","slot_not_found","slot_not_purchasable","slot_expired","insufficient_spots","ratio_exceeded","external_ref_exists","booking_not_found"]}},"additionalProperties":true}}}}}}},"/api/partner/v1/services":{"get":{"tags":["v1 · OCTO"],"operationId":"listServices","summary":"List lesson types / levels (OCTO /products)","description":"Lesson types and levels offered. Alias `/api/partner/v1/products` (OCTO) returns a plain products array.","parameters":[{"name":"X-Partner-API-Key","in":"header","required":true,"schema":{"type":"string","example":"sbpk_xxxxxxxxxxxxxxxxxxxxxxxx"},"description":"Partner API key issued by SurfBooking. May also be sent as `Authorization: Bearer <key>`."},{"name":"format","in":"query","required":false,"schema":{"type":"string","enum":["octo","native","fh","bl","sb"]}}],"responses":{"200":{"description":"Array of services/products"},"401":{"description":"Invalid key","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string","description":"Short machine code for the failure.","examples":["missing_api_key","invalid_api_key","insufficient_scope","rate_limit_exceeded","missing_fields","invalid_email","slot_not_found","slot_not_purchasable","slot_expired","insufficient_spots","ratio_exceeded","external_ref_exists","booking_not_found"]}},"additionalProperties":true}}}}}}},"/api/partner/v1/bookings":{"post":{"tags":["v1 · OCTO"],"operationId":"createBooking","summary":"Create a booking","description":"Create a booking on a SurfBooking lesson slot on behalf of the end customer. Overbooking-safe (row lock). Pass `externalRef` for idempotency on your side.","parameters":[{"name":"X-Partner-API-Key","in":"header","required":true,"schema":{"type":"string","example":"sbpk_xxxxxxxxxxxxxxxxxxxxxxxx"},"description":"Partner API key issued by SurfBooking. May also be sent as `Authorization: Bearer <key>`."}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["lessonSlotId","customerName","customerEmail"],"properties":{"lessonSlotId":{"type":"string","description":"ID of the slot from /availabilities."},"customerName":{"type":"string"},"customerEmail":{"type":"string","format":"email"},"customerPhone":{"type":"string","description":"Optional."},"participants":{"type":"integer","minimum":1,"default":1},"externalRef":{"type":"string","description":"Your booking reference (dedupe / reconciliation)."},"notes":{"type":"string","description":"Optional free text."}}}}}},"responses":{"201":{"description":"Booking created — returns { ok, bookingId, ... }"},"400":{"description":"missing_fields / invalid_email","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string","description":"Short machine code for the failure.","examples":["missing_api_key","invalid_api_key","insufficient_scope","rate_limit_exceeded","missing_fields","invalid_email","slot_not_found","slot_not_purchasable","slot_expired","insufficient_spots","ratio_exceeded","external_ref_exists","booking_not_found"]}},"additionalProperties":true}}}},"401":{"description":"missing_api_key / invalid_api_key","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string","description":"Short machine code for the failure.","examples":["missing_api_key","invalid_api_key","insufficient_scope","rate_limit_exceeded","missing_fields","invalid_email","slot_not_found","slot_not_purchasable","slot_expired","insufficient_spots","ratio_exceeded","external_ref_exists","booking_not_found"]}},"additionalProperties":true}}}},"404":{"description":"slot_not_found","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string","description":"Short machine code for the failure.","examples":["missing_api_key","invalid_api_key","insufficient_scope","rate_limit_exceeded","missing_fields","invalid_email","slot_not_found","slot_not_purchasable","slot_expired","insufficient_spots","ratio_exceeded","external_ref_exists","booking_not_found"]}},"additionalProperties":true}}}},"409":{"description":"slot_not_purchasable / slot_expired / insufficient_spots / ratio_exceeded / external_ref_exists (idempotent retry — body carries the original bookingId)","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string","description":"Short machine code for the failure.","examples":["missing_api_key","invalid_api_key","insufficient_scope","rate_limit_exceeded","missing_fields","invalid_email","slot_not_found","slot_not_purchasable","slot_expired","insufficient_spots","ratio_exceeded","external_ref_exists","booking_not_found"]}},"additionalProperties":true}}}},"429":{"description":"rate_limit_exceeded (Retry-After header)","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string","description":"Short machine code for the failure.","examples":["missing_api_key","invalid_api_key","insufficient_scope","rate_limit_exceeded","missing_fields","invalid_email","slot_not_found","slot_not_purchasable","slot_expired","insufficient_spots","ratio_exceeded","external_ref_exists","booking_not_found"]}},"additionalProperties":true}}}}}}},"/api/partner/v1/bookings/{id}":{"get":{"tags":["v1 · OCTO"],"operationId":"getBooking","summary":"Booking status","parameters":[{"name":"X-Partner-API-Key","in":"header","required":true,"schema":{"type":"string","example":"sbpk_xxxxxxxxxxxxxxxxxxxxxxxx"},"description":"Partner API key issued by SurfBooking. May also be sent as `Authorization: Bearer <key>`."},{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Booking object"},"404":{"description":"booking_not_found","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string","description":"Short machine code for the failure.","examples":["missing_api_key","invalid_api_key","insufficient_scope","rate_limit_exceeded","missing_fields","invalid_email","slot_not_found","slot_not_purchasable","slot_expired","insufficient_spots","ratio_exceeded","external_ref_exists","booking_not_found"]}},"additionalProperties":true}}}}}},"delete":{"tags":["v1 · OCTO"],"operationId":"cancelBooking","summary":"Cancel a booking","parameters":[{"name":"X-Partner-API-Key","in":"header","required":true,"schema":{"type":"string","example":"sbpk_xxxxxxxxxxxxxxxxxxxxxxxx"},"description":"Partner API key issued by SurfBooking. May also be sent as `Authorization: Bearer <key>`."},{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"Cancelled"},"404":{"description":"booking_not_found","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string","description":"Short machine code for the failure.","examples":["missing_api_key","invalid_api_key","insufficient_scope","rate_limit_exceeded","missing_fields","invalid_email","slot_not_found","slot_not_purchasable","slot_expired","insufficient_spots","ratio_exceeded","external_ref_exists","booking_not_found"]}},"additionalProperties":true}}}}}}},"/api/partner/v2/meta":{"get":{"tags":["v2 · Intelligence"],"operationId":"v2Meta","summary":"v2 endpoint catalogue","parameters":[{"name":"X-Partner-API-Key","in":"header","required":true,"schema":{"type":"string","example":"sbpk_xxxxxxxxxxxxxxxxxxxxxxxx"},"description":"Partner API key issued by SurfBooking. May also be sent as `Authorization: Bearer <key>`."}],"responses":{"200":{"description":"Meta + endpoint list"}}}},"/api/partner/v2/sizing/board":{"get":{"tags":["v2 · Intelligence"],"operationId":"sizeBoard","summary":"Recommend a board size","parameters":[{"name":"X-Partner-API-Key","in":"header","required":true,"schema":{"type":"string","example":"sbpk_xxxxxxxxxxxxxxxxxxxxxxxx"},"description":"Partner API key issued by SurfBooking. May also be sent as `Authorization: Bearer <key>`."},{"name":"level","in":"query","required":true,"schema":{"type":"string","enum":["beginner","intermediate","advanced","kids"]}},{"name":"weight","in":"query","required":true,"schema":{"type":"number"},"description":"Body weight in kg."}],"responses":{"200":{"description":"Board recommendation"},"403":{"description":"insufficient_scope","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string","description":"Short machine code for the failure.","examples":["missing_api_key","invalid_api_key","insufficient_scope","rate_limit_exceeded","missing_fields","invalid_email","slot_not_found","slot_not_purchasable","slot_expired","insufficient_spots","ratio_exceeded","external_ref_exists","booking_not_found"]}},"additionalProperties":true}}}}}}},"/api/partner/v2/sizing/board/conditions":{"get":{"tags":["v2 · Intelligence"],"operationId":"sizeBoardConditions","summary":"Board size tuned to conditions","parameters":[{"name":"X-Partner-API-Key","in":"header","required":true,"schema":{"type":"string","example":"sbpk_xxxxxxxxxxxxxxxxxxxxxxxx"},"description":"Partner API key issued by SurfBooking. May also be sent as `Authorization: Bearer <key>`."},{"name":"level","in":"query","required":true,"schema":{"type":"string","enum":["beginner","intermediate","advanced","kids"]}},{"name":"weight","in":"query","required":true,"schema":{"type":"number"}},{"name":"swellPeriodS","in":"query","required":false,"schema":{"type":"number"}},{"name":"swellDirectionDeg","in":"query","required":false,"schema":{"type":"number"}},{"name":"bestSwellDirDeg","in":"query","required":false,"schema":{"type":"number"}}],"responses":{"200":{"description":"Board recommendation tuned to conditions"}}}},"/api/partner/v2/sizing/wetsuit":{"get":{"tags":["v2 · Intelligence"],"operationId":"sizeWetsuit","summary":"Recommend a wetsuit size","parameters":[{"name":"X-Partner-API-Key","in":"header","required":true,"schema":{"type":"string","example":"sbpk_xxxxxxxxxxxxxxxxxxxxxxxx"},"description":"Partner API key issued by SurfBooking. May also be sent as `Authorization: Bearer <key>`."},{"name":"weight","in":"query","required":true,"schema":{"type":"number"}},{"name":"height","in":"query","required":false,"schema":{"type":"number"},"description":"cm — improves fit."}],"responses":{"200":{"description":"Wetsuit size"}}}},"/api/partner/v2/sizing/leash":{"get":{"tags":["v2 · Intelligence"],"operationId":"sizeLeash","summary":"Recommend a leash size","parameters":[{"name":"X-Partner-API-Key","in":"header","required":true,"schema":{"type":"string","example":"sbpk_xxxxxxxxxxxxxxxxxxxxxxxx"},"description":"Partner API key issued by SurfBooking. May also be sent as `Authorization: Bearer <key>`."},{"name":"boardSize","in":"query","required":true,"schema":{"type":"string","example":"6'6"}},{"name":"category","in":"query","required":false,"schema":{"type":"string","enum":["board","soft_board"]}}],"responses":{"200":{"description":"Leash size"}}}},"/api/partner/v2/sizing/boots":{"get":{"tags":["v2 · Intelligence"],"operationId":"sizeBoots","summary":"Recommend a boot size","parameters":[{"name":"X-Partner-API-Key","in":"header","required":true,"schema":{"type":"string","example":"sbpk_xxxxxxxxxxxxxxxxxxxxxxxx"},"description":"Partner API key issued by SurfBooking. May also be sent as `Authorization: Bearer <key>`."},{"name":"shoeSizeEu","in":"query","required":true,"schema":{"type":"integer","example":42}}],"responses":{"200":{"description":"Boot size (XS–XL bucket)"}}}},"/api/partner/v2/conditions/{spotId}":{"get":{"tags":["v2 · Intelligence"],"operationId":"spotConditions","summary":"Per-spot conditions","description":"Sanitised forecast/conditions for a spot (never exposes the underlying forecast model or sources).","parameters":[{"name":"X-Partner-API-Key","in":"header","required":true,"schema":{"type":"string","example":"sbpk_xxxxxxxxxxxxxxxxxxxxxxxx"},"description":"Partner API key issued by SurfBooking. May also be sent as `Authorization: Bearer <key>`."},{"name":"spotId","in":"path","required":true,"schema":{"type":"string"},"description":"Spot id, e.g. from the public catalogue."}],"responses":{"200":{"description":"Conditions payload"},"403":{"description":"insufficient_scope","content":{"application/json":{"schema":{"type":"object","required":["error"],"properties":{"error":{"type":"string","description":"Short machine code for the failure.","examples":["missing_api_key","invalid_api_key","insufficient_scope","rate_limit_exceeded","missing_fields","invalid_email","slot_not_found","slot_not_purchasable","slot_expired","insufficient_spots","ratio_exceeded","external_ref_exists","booking_not_found"]}},"additionalProperties":true}}}}}}}},"components":{"securitySchemes":{"PartnerApiKey":{"type":"apiKey","in":"header","name":"X-Partner-API-Key","description":"Partner API key (sbpk_… for v1, sbpd_… for v2)."},"BearerKey":{"type":"http","scheme":"bearer","description":"Same key, sent as a Bearer token."}},"schemas":{"Error":{"type":"object","required":["error"],"properties":{"error":{"type":"string","description":"Short machine code for the failure.","examples":["missing_api_key","invalid_api_key","insufficient_scope","rate_limit_exceeded","missing_fields","invalid_email","slot_not_found","slot_not_purchasable","slot_expired","insufficient_spots","ratio_exceeded","external_ref_exists","booking_not_found"]}},"additionalProperties":true}}}}