Surf Booking.OCTO
ManifestoAgentIntelligence LayerTechnologyConstitutionMCPPartner API
API para Parceiros

Uma integração.Mil canais.

REST · JSON · OCTO. A disponibilidade real das tuas aulas, legível por qualquer marketplace ou channel manager. Liga uma vez, chega a muitos.

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
}
Plataforma aberta

Sem protocolo fechado. Falamos OCTO — o standard aberto das reservas de atividades. Integras uma vez, chegas a toda a gente.

OCTO (octo.travel) é como os sistemas de reservas, channel managers e marketplaces de atividades falam entre si. Uma integração contra a SurfBooking, e a oferta fica legível por qualquer comprador OCTO. Para quem prefere, também devolvemos o formato nativo SB e variantes para adaptadores existentes.

Parceiros diferentes, acessos diferentes

Cada parceiro liga só ao que lhe serve.

Não é a mesma API para todos. Cada chave é limitada (least-privilege) ao que esse parceiro precisa — nunca mais do que isso.

Hotéis · OTAs · channel managers

Disponibilidade + reservas

Lêem as aulas disponíveis e criam reservas em nome do hóspede/cliente. Caminho v1 (OCTO). O acesso de quem distribui aulas.

Marcas de equipamento · apps · IA

Sizing + condições

Recomendam a prancha/fato certos e lêem as condições do spot. Caminho v2, com scope sizing e/ou forecast — nunca tocam em reservas.

Afiliados

Atribuição, sem API

Trazem clientes e recebem comissão via /aff + cookie de 90 dias. Sem chave, sem integração técnica.

As chaves de plataforma carregam scopes. Uma chave de sizing só abre os endpoints de sizing — tudo o resto devolve 403 insufficient_scope.

O que a API faz

Três coisas. Disponibilidade, serviços, reservas.

Tudo em REST + JSON, autenticado por chave da escola.

availabilities

Ler disponibilidade

As aulas reais com dia, hora, spot, nível, vagas e preço. Filtra por data e nível. Formatos sb · fh · bl · octo.

services

Listar serviços

Os tipos de aula da escola (nível, grupo/privada, intervalo de preço) — em nativo SB ou como products OCTO.

bookings

Criar e gerir reservas

Cria, consulta e cancela reservas em nome do cliente final. Idempotente por externalRef, com revalidação de vagas e rácios.

Ao vivo

Uma chamada e vês a API a responder.

O descritor é público. Cola no terminal:

terminal · descritor da API
# ver a API (sem chave)
curl -s https://www.surfbooking.eu/api/partner/v1

Contrato completo, máquina-legível — pensado para agentes de IA e geradores de SDK auto-descobrirem a API: /openapi.json (OpenAPI 3.1) · referência interativa em /docs (Swagger UI).

Com a chave da escola, lês a disponibilidade — aqui em formato OCTO:

terminal · disponibilidade (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"

Limite: 120 pedidos/min por chave. Cada resposta traz X-RateLimit-Limit e X-RateLimit-Remaining; ao exceder recebes 429 com Retry-After: 60.

Autenticação

Uma chave por escola. Tu controlas.

Cada escola gera a sua própria chave no Centro de Controlo — e revoga-a quando quiser. A chave segue num cabeçalho, em qualquer um dos dois formatos:

X-Partner-API-Key

Cabeçalho nativo SB

X-Partner-API-Key: sbpk_xxxx… — o formato canónico da SurfBooking.

Authorization: Bearer

Compatível OCTO

Authorization: Bearer sbpk_xxxx… — para clientes que falam OCTO. A mesma chave.

Gera e revoga chaves em Centro de Controlo → Canais Externos → API Parceiros. Nunca guardamos a chave em claro — só o seu resumo.

Referência

Os endpoints.

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

GET

/

Descritor da API (raiz, sem chave). Devolve name, version, auth, octo e o mapa completo de endpoints. Abrir no browser redireciona para esta página.

GET

/availabilities

Aulas reserváveis. Parâmetros: from, to, level?, format? (sb·fh·bl·octo). Defaults: from=hoje, to=+30 dias.

GET

/availability

Alias OCTO de /availabilities — assume format=octo por defeito (plug-and-play OCTO).

GET

/services

Tipos de aula da escola. format? (sb·octo) — em OCTO devolve products.

GET

/products

Alias OCTO de /services — assume format=octo por defeito. Array de products OCTO.

GET

/supplier

Identidade do fornecedor (descritor OCTO supplier): id, name, endpoint, contact, locales, timeZone.

POST

/bookings

Cria reserva. Corpo: lessonSlotId, customerName, customerEmail, customerPhone?, participants?, externalRef?, notes?.

GET

/bookings/:id

Consulta o estado de uma reserva.

DELETE

/bookings/:id

Cancela uma reserva.

O descritor — GET /api/partner/v1 (sem chave):

resposta · descritor
{
  "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" }
  }
}

Identidade do fornecedor — GET /supplier (OCTO):

resposta · 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": "…" }
}
Exemplos

Pedido e resposta, a sério.

O corpo de uma reserva e o que recebes de volta — tal como a API responde hoje.

Criar uma reserva — POST /bookings:

pedido · 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"
  }'

Resposta — 201 Created:

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

Disponibilidade em OCTO — GET /availabilities?format=octo devolve um array:

resposta · 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/…" }
  }
]

Preços em cents (minor units) — 3500 = 35,00 €, conforme currencyPrecision: 2 do OCTO. As datas vêm em hora local da escola (Europe/Lisbon).

A mesma disponibilidade noutros formatos. Nativo SB — ?format=sb (envelope com _meta):

resposta · 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 (envelope availabilities):

resposta · 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 (envelope data):

resposta · 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-…" }
    }
  ]
}

Em octo a resposta é um array simples (sem envelope). Em sb/fh vem em availabilities; em bl, em data.

Erros

Códigos de estado.

Cada erro traz um corpo JSON { "error": "…" } — legível por máquina.

401

missing_api_key · invalid_api_key

Chave ausente, demasiado curta, inválida ou revogada. Inclui hint quando falta o cabeçalho.

400

missing_fields · invalid_email · invalid_date_range

Corpo incompleto, email malformado, ou intervalo de datas inválido (máx 180 dias, from ≤ to).

404

slot_not_found · booking_not_found · booking_not_found_or_already_cancelled

A aula ou a reserva não existe nesta escola — ou, no DELETE, já tinha sido cancelada.

409

insufficient_spots · slot_expired · slot_not_purchasable · ratio_exceeded · external_ref_exists

Conflito de estado: sem vagas, aula no passado, fechada à venda, rácio de instrutor excedido, ou externalRef repetido (idempotência — devolve o bookingId que já existe).

429

rate_limit_exceeded

Acima de 120 pedidos/min por chave. Espera o Retry-After (60s). Cada resposta traz X-RateLimit-Remaining.

Chave em falta ou inválida — 401:

resposta · 401
# sem cabeçalho de chave
{
  "error": "missing_api_key",
  "hint": "Set X-Partner-API-Key header (or Authorization: Bearer)"
}

# chave inexistente ou revogada
{ "error": "invalid_api_key" }

Sem vagas suficientes (anti-overbooking) — 409:

resposta · 409
# a aula já não tem lugares para o nº de participantes
{
  "error": "insufficient_spots",
  "remaining": 1
}

# externalRef repetido → idempotência: devolve o bookingId existente
{
  "error": "external_ref_exists",
  "bookingId": "f1e2d3c4-…"
}

A revalidação de vagas e rácios corre dentro de uma transação com SELECT … FOR UPDATE — esgotado é esgotado, nunca há dupla-marcação.

Sizing + condições · v2

Recomenda o equipamento certo. Sem tocar em reservas.

O caminho v2 para marcas de equipamento, apps e IA. Chave de plataforma com scope sizing e/ou forecast — determinístico, sem dados pessoais, nunca toca em reservas. Base: https://www.surfbooking.eu/api/partner/v2.

GET

/sizing/board

Prancha recomendada por level + weight. Scope sizing.

GET

/sizing/board/conditions

Prancha afinada ao dia: level, weight, swellPeriodS, swellDirectionDeg, bestSwellDirDeg. Scope sizing.

GET

/sizing/wetsuit

Tamanho de fato por weight + height. Scope sizing.

GET

/sizing/leash

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

GET

/conditions/:spotId

Condições do spot — a mesma shape pública, sanitizada (nunca expõe modelo nem fontes). Scope forecast.

Recomendação de prancha — 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"
resposta · sizing
{ "recommendation": { "category": "soft_board", "size": "8'0" } }

As chaves de plataforma (sbpd_…) são emitidas pela SurfBooking, não pela escola. Scope errado → 403 insufficient_scope. Limite 240 pedidos/min por chave, com Retry-After: 60 ao exceder.

Widget embedável

Sizing no teu site. Uma linha.

Uma página iframe self-contained, "Powered by SurfBooking". Mostra as condições do pico, recomenda a prancha pelo peso e nível, e leva o cliente a reservar uma aula — com o teu link de afiliado embutido. PII-free, sem cookies de seguimento.

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 = a tua chave de plataforma (scope sizing, e forecast se quiseres as condições no topo). spot e slug são opcionais — o slug liga as reservas geradas ao teu /aff (cookie de 90 dias).

Versões

v1 — estável.

A base é /api/partner/v1. Mudanças que quebram compatibilidade entram numa versão nova (/v2) — a v1 não tem depreciação prevista. Podemos acrescentar campos sem aviso, por isso lê de forma tolerante: ignora o que ainda não conheces.

Para quem

Channel managers, marketplaces, hotéis.

Qualquer parceiro que queira vender ou mostrar aulas de surf reais. Já desenhado para falar com adaptadores existentes (FareHarbor, Booking Layer) e com qualquer comprador OCTO — Bókun e os marketplaces que ele alimenta.

Hotéis & resorts

Oferece aulas reais aos hóspedes sem gerir nada — a reserva e o pagamento ficam na SurfBooking.

Channel managers

Sincroniza a disponibilidade em tempo real, sem dupla-marcação. OCTO de fábrica.

Marketplaces

Lista a oferta das escolas e encaminha a reserva — com vagas e rácios sempre revalidados do nosso lado.

A tua segurança

A escola manda. Sempre.

CHAVE

Por escola, revogável

Cada chave vê só a oferta da própria escola. Revoga num clique e o acesso morre na hora.

VAGAS

Nunca há overbooking

Cada reserva revalida vagas e rácios de instrutor dentro de uma transação. Esgotado é esgotado.

PAGAMENTO

Confirmado na SurfBooking

O fluxo de dinheiro fica do nosso lado — o parceiro encaminha, a SurfBooking confirma.

ALUNOS

Dados protegidos

Só sai o necessário para a reserva. Os contactos dos teus alunos não viram lista de ninguém.

Pronto a ligar

Gera uma chave e começa.

A API está viva. Gera a chave da tua escola no Centro de Controlo e faz a primeira chamada hoje.

Abrir o Centro de Controlo Ver o descritor