{"openapi":"3.1.0","info":{"title":"Carla Travel Partner API","version":"1.5.0","description":"B2B API for agencies that sell Carla Travel day-trips and packages in their own checkout. You charge the tourist; Carla never opens a Stripe session, Payment Link or checkout on this API. GET catalogue and availability return live seats and net euros (never retail prices). POST /bookings conciliates a sale you already made, keyed by your own `partnerRef`. There is no hold between look and POST — if the seat is gone you get 409 MAX_PAX_LLENO. Guides: https://developers.carlatravelservice.com","contact":{"name":"Carla Travel — booking","email":"booking@carlatravelservice.com"}},"servers":[{"url":"https://os.carlatravelservice.com/api/external/v1"}],"tags":[{"name":"daytrips","x-displayName":"Day-trips","description":"One-day excursions (Gibraltar, Ronda & Setenil, Caminito del Rey…). A booking is a titular plus pax counters on one departure (`salida`). Pickup is one of our stops (`puntoRecogidaId`)."},{"name":"packages","x-displayName":"Packages","description":"Multi-day circuits (ANDALUZJA). A booking is rooms with nominative passengers on one departure date (`terminId`). Net prices per room type."},{"name":"webhooks","x-displayName":"Webhooks","description":"Optional outbound notifications to the URL configured in Settings → Webhooki. Signed with HMAC-SHA256; retried with backoff. Never the source of truth — GET the booking."}],"x-tagGroups":[{"name":"Day-trips","tags":["daytrips"]},{"name":"Packages","tags":["packages"]},{"name":"Webhooks","tags":["webhooks"]}],"security":[{"partnerBearer":[]}],"components":{"securitySchemes":{"partnerBearer":{"type":"http","scheme":"bearer","description":"`Authorization: Bearer ct_test_…` (sandbox: bookings flagged esTest, never decrement seats) or `ct_live_…` (production seats; requires a signed agreement). Create keys after signing in at https://developers.carlatravelservice.com (Keys in the sidebar). Hashed at rest; the secret is shown once."}},"schemas":{"PartnerError":{"type":"object","required":["error"],"properties":{"error":{"type":"string","description":"Human-readable message (Spanish)"},"code":{"type":"string","description":"Stable machine code. Absent on 401 and 500.","enum":["MAX_PAX_LLENO","SALIDA_CERRADA","PARADA_INVALIDA","HOTEL_SIN_PARADA","HOTEL_AMBIGUO","RATE_LIMIT","NO_ENCONTRADO","IDIOMA","PAGADA","TERMIN_NO_DISPONIBLE","PARTNER_REF_REQUIRED","SIN_HABITACIONES","SIN_PAX","TITULAR","OCUPACION_INVALIDA"]}}},"ZodIssue":{"type":"object","required":["code","path","message"],"additionalProperties":true,"properties":{"code":{"type":"string"},"path":{"type":"array","items":{"type":["string","integer"]}},"message":{"type":"string"}}},"PartnerValidationError":{"type":"object","required":["error"],"properties":{"error":{"type":"string"},"detalles":{"type":"array","description":"Present on schema validation failures","items":{"$ref":"#/components/schemas/ZodIssue"}}}},"WebhookEnvelope":{"type":"object","required":["id","evento","creado","data"],"description":"Headers: `X-Carla-Event`, `X-Carla-Delivery` (dedupe key — retries reuse it), `X-Carla-Signature: t=<unix>,v1=<hex>` where hex = HMAC-SHA256(secret, `<t>.<raw body>`); reject if |now − t| > 300 s. `User-Agent: CarlaTravel-Webhooks/1.0`. Reply 2xx within 10 s; anything else is retried with backoff 1 min → 24 h, up to 8 attempts.","properties":{"id":{"type":"string","format":"uuid","description":"Delivery id (same value as X-Carla-Delivery)"},"evento":{"type":"string","enum":["booking.created","booking.cancelled","departure.updated"]},"creado":{"type":"string","format":"date-time"},"data":{"type":"object","additionalProperties":true,"description":"booking.created: { id, partnerRef } (day-trip) or { id, partnerRef, kind: \"package\" } · booking.cancelled: { id } · departure.updated: { salidaId, stopSale }"}}},"PartnerExcursionListItem":{"type":"object","required":["slug","nombre","idiomas"],"properties":{"slug":{"type":"string","example":"gibraltar"},"nombre":{"type":"string","example":"Gibraltar"},"idiomas":{"type":"array","items":{"type":"string","enum":["pl","en"]},"description":"Languages with at least one sellable departure"}}},"PartnerExcursionList":{"type":"object","required":["excursions"],"properties":{"excursions":{"type":"array","items":{"$ref":"#/components/schemas/PartnerExcursionListItem"}}}},"PartnerRecogida":{"type":"object","required":["puntoId","nombre","horaEstandar","direccion","estandarizada","hoteles"],"description":"A pickup stop of one operator's offer. Send `puntoId` as `puntoRecogidaId` when booking; it must belong to the offer that operates the chosen salida (else 422 PARADA_INVALIDA).","properties":{"puntoId":{"type":"string","format":"uuid"},"nombre":{"type":"string"},"nombrePl":{"type":["string","null"]},"nombreEn":{"type":["string","null"]},"zona":{"type":["string","null"],"description":"Town / area label"},"direccion":{"type":"string","enum":["malaga","gibraltar","ambas"],"description":"Which direction of the route serves this stop"},"horaEstandar":{"type":"string","pattern":"^\\d{2}:\\d{2}:\\d{2}$","description":"Standard pickup time HH:MM:SS"},"lat":{"type":["number","null"]},"lng":{"type":["number","null"]},"mapaUrl":{"type":["string","null"],"description":"Google Maps link"},"estandarizada":{"type":"boolean","description":"true = shared bus stop; false = hotel-door pickup"},"hoteles":{"type":"array","description":"Hotels that use this stop (helps you map a hotel name to a stop)","items":{"type":"object","required":["id","nombre"],"properties":{"id":{"type":"string","format":"uuid"},"nombre":{"type":"string"}}}}}},"PartnerOferta":{"type":"object","required":["organizadorId","organizador","idiomas","netoAdultoEur","netoNinoEur","netoBebeEur","recogidas"],"description":"One operator's offer for this excursion: net prices per head and its pickup stops. Match `organizadorId` with the `organizadorId` of the salida you book.","properties":{"organizadorId":{"type":"string","format":"uuid"},"organizador":{"type":"string"},"idiomas":{"type":"array","items":{"type":"string","enum":["pl","en"]}},"netoAdultoEur":{"type":"string","pattern":"^\\d+(\\.\\d{2})?$","description":"Euros as a decimal string, e.g. \"42.00\"."},"netoNinoEur":{"type":"string","pattern":"^\\d+(\\.\\d{2})?$","description":"Euros as a decimal string, e.g. \"42.00\"."},"netoBebeEur":{"type":"string","pattern":"^\\d+(\\.\\d{2})?$","description":"Euros as a decimal string, e.g. \"42.00\"."},"recogidas":{"type":"array","items":{"$ref":"#/components/schemas/PartnerRecogida"}}}},"PartnerExcursion":{"type":"object","required":["slug","nombre","ofertas"],"additionalProperties":true,"description":"Excursion detail. Extra descriptive fields may appear; rely on `slug`, `nombre` and `ofertas[]`.","properties":{"slug":{"type":"string"},"nombre":{"type":"string"},"ofertas":{"type":"array","items":{"$ref":"#/components/schemas/PartnerOferta"}}}},"PartnerSalida":{"type":"object","required":["salidaId","fecha","plazasDisponibles","aforoGestionado","idioma","organizadorId"],"description":"One open departure. `plazasDisponibles` is live (max − sold). When `aforoGestionado` is false the operator does not cap seats and `plazasDisponibles` is the sentinel 9007199254740991 — treat it as unlimited. Only PL/EN departures are listed; rows with 0 seats are omitted.","properties":{"salidaId":{"type":"string","format":"uuid"},"fecha":{"type":"string","format":"date","example":"2026-10-03"},"horaSalida":{"type":["string","null"],"description":"HH:MM or null"},"plazasDisponibles":{"type":"integer"},"aforoGestionado":{"type":"boolean"},"idioma":{"type":"string","enum":["pl","en"]},"organizadorId":{"type":"string","format":"uuid"},"organizador":{"type":"string"},"offsetRecogidaMin":{"type":"integer","description":"Minutes this departure shifts every pickup vs catalogue `horaEstandar`. Customer-facing time is horaEstandar + offsetRecogidaMin. 0 = no shift."},"netoAdultoEur":{"type":"string","pattern":"^\\d+(\\.\\d{2})?$","description":"Euros as a decimal string, e.g. \"42.00\"."},"netoNinoEur":{"type":"string","pattern":"^\\d+(\\.\\d{2})?$","description":"Euros as a decimal string, e.g. \"42.00\"."},"netoBebeEur":{"type":"string","pattern":"^\\d+(\\.\\d{2})?$","description":"Euros as a decimal string, e.g. \"42.00\"."}}},"PartnerAvailabilityResponse":{"type":"object","required":["slug","salidas"],"properties":{"slug":{"type":"string"},"salidas":{"type":"array","items":{"$ref":"#/components/schemas/PartnerSalida"}}}},"PartnerBookingCreate":{"type":"object","additionalProperties":false,"required":["excursionSlug","salidaId","titularNombre","titularApellido","clienteEmail","adultos","ninos","bebesConAsiento","bebesSinAsiento","partnerRef"],"description":"Conciliation of a sale you already charged. Unknown keys are rejected. No price field: the OS recomputes the net total. Pickup is exactly one of `puntoRecogidaId`, `hotelRecogidaId` or `hotelRecogidaTexto` (the OS resolves hotel → stop). At least one passenger in total.","properties":{"excursionSlug":{"type":"string","minLength":1},"salidaId":{"type":"string","format":"uuid","description":"From availability"},"titularNombre":{"type":"string","minLength":1},"titularApellido":{"type":"string","minLength":1},"clienteEmail":{"type":"string","format":"email"},"clienteIdioma":{"type":"string","enum":["pl","en"],"default":"pl","description":"Language for the tourist's documents"},"telefonoContacto":{"type":"string","maxLength":40},"adultos":{"type":"integer","minimum":0},"ninos":{"type":"integer","minimum":0,"description":"Children (see the excursion's age bands)"},"bebesConAsiento":{"type":"integer","minimum":0,"description":"Infants occupying a seat"},"bebesSinAsiento":{"type":"integer","minimum":0,"description":"Infants on lap (no seat)"},"puntoRecogidaId":{"type":"string","format":"uuid","description":"`puntoId` of a stop in the operator's offer (from resolve or ofertas[].recogidas)"},"hotelRecogidaId":{"type":"string","format":"uuid","description":"Catalogue hotel id from GET /pickups/resolve. The OS picks the stop."},"hotelRecogidaTexto":{"type":"string","minLength":1,"maxLength":160,"description":"Hotel name. Unique match resolves; 422 HOTEL_AMBIGUO if several hit — call /pickups/resolve."},"partnerRef":{"type":"string","minLength":1,"maxLength":80,"description":"Id of the booking in the partner's system. Unique per agency."},"minibus":{"type":"boolean","description":"Gibraltar only: false = transport without the minibus tour"}}},"PartnerPickupCandidate":{"type":"object","required":["puntoId","paradaNombre","horaRecogida","estandarizada","origen"],"properties":{"hotelId":{"type":["string","null"],"format":"uuid"},"hotelNombre":{"type":["string","null"]},"puntoId":{"type":"string","format":"uuid"},"paradaNombre":{"type":"string"},"paradaNombrePl":{"type":["string","null"]},"paradaNombreEn":{"type":["string","null"]},"horaRecogida":{"type":"string","pattern":"^\\d{2}:\\d{2}:\\d{2}$","description":"Pickup time the tourist should see (includes departure offset)"},"lat":{"type":["number","null"]},"lng":{"type":["number","null"]},"mapaUrl":{"type":["string","null"]},"estandarizada":{"type":"boolean"},"zona":{"type":["string","null"]},"origen":{"type":"string","enum":["hotel","parada","nearest"]},"distanciaKm":{"type":["number","null"]}}},"PartnerPickupResolve":{"type":"object","required":["salidaId","candidatos"],"properties":{"salidaId":{"type":"string","format":"uuid"},"candidatos":{"type":"array","items":{"$ref":"#/components/schemas/PartnerPickupCandidate"}}}},"PartnerBookingCreated":{"type":"object","required":["id","referencia","partnerRef","netoTotalEur","plazas","puntoRecogidaId","horaRecogida","esTest"],"properties":{"id":{"type":"string","format":"uuid"},"referencia":{"type":"string","format":"uuid","description":"Same as id"},"partnerRef":{"type":"string","minLength":1,"maxLength":80,"description":"Id of the booking in the partner's system. Unique per agency."},"netoTotalEur":{"type":"string","pattern":"^\\d+(\\.\\d{2})?$","description":"Euros as a decimal string, e.g. \"42.00\"."},"plazas":{"type":"integer","description":"Seats taken (lap infants excluded)"},"puntoRecogidaId":{"type":"string","format":"uuid"},"horaRecogida":{"type":"string","description":"HH:MM:SS the tourist should see (includes departure offset)"},"paradaNombre":{"type":["string","null"]},"paradaNombrePl":{"type":["string","null"]},"paradaNombreEn":{"type":["string","null"]},"lat":{"type":["number","null"]},"lng":{"type":["number","null"]},"mapaUrl":{"type":["string","null"]},"hotelRecogidaId":{"type":["string","null"],"format":"uuid"},"idioma":{"type":"string"},"esTest":{"type":"boolean","description":"true for ct_test_ keys"}}},"PartnerBooking":{"type":"object","required":["id","referencia","partnerRef","netoTotalEur","adultos","ninos","bebesConAsiento","bebesSinAsiento","puntoRecogidaId","horaRecogida","titularNombre","titularApellido","clienteEmail","telefonoContacto","esTest"],"properties":{"id":{"type":"string","format":"uuid"},"referencia":{"type":"string","format":"uuid"},"partnerRef":{"type":["string","null"],"minLength":1,"maxLength":80,"description":"Id of the booking in the partner's system. Unique per agency."},"netoTotalEur":{"type":"string","pattern":"^\\d+(\\.\\d{2})?$","description":"Euros as a decimal string, e.g. \"42.00\"."},"adultos":{"type":"integer"},"ninos":{"type":"integer"},"bebesConAsiento":{"type":"integer"},"bebesSinAsiento":{"type":"integer"},"puntoRecogidaId":{"type":["string","null"],"format":"uuid"},"horaRecogida":{"type":["string","null"]},"paradaNombre":{"type":["string","null"]},"paradaNombrePl":{"type":["string","null"]},"paradaNombreEn":{"type":["string","null"]},"lat":{"type":["number","null"]},"lng":{"type":["number","null"]},"mapaUrl":{"type":["string","null"]},"hotelRecogidaId":{"type":["string","null"],"format":"uuid"},"titularNombre":{"type":"string"},"titularApellido":{"type":"string"},"clienteEmail":{"type":["string","null"]},"telefonoContacto":{"type":["string","null"]},"esTest":{"type":"boolean"}}},"PartnerBookingAmend":{"type":"object","additionalProperties":false,"minProperties":1,"description":"Same departure only — a date change is cancel + new POST. Pax changes re-check seats (409 MAX_PAX_LLENO) and recompute the net total. Pickup: exactly one of puntoRecogidaId / hotelRecogidaId / hotelRecogidaTexto.","properties":{"titularNombre":{"type":"string","minLength":1},"titularApellido":{"type":"string","minLength":1},"clienteEmail":{"type":"string","format":"email"},"telefonoContacto":{"type":"string","maxLength":40},"adultos":{"type":"integer","minimum":0},"ninos":{"type":"integer","minimum":0},"bebesConAsiento":{"type":"integer","minimum":0},"bebesSinAsiento":{"type":"integer","minimum":0},"puntoRecogidaId":{"type":"string","format":"uuid"},"hotelRecogidaId":{"type":"string","format":"uuid"},"hotelRecogidaTexto":{"type":"string","minLength":1,"maxLength":160}}},"PartnerCancelResult":{"type":"object","required":["cancelled"],"properties":{"cancelled":{"type":"boolean","const":true}}},"PartnerPackageListItem":{"type":"object","required":["slug","nombre"],"properties":{"slug":{"type":"string","example":"andaluzja"},"nombre":{"type":"string"}}},"PartnerPackageList":{"type":"object","required":["packages"],"properties":{"packages":{"type":"array","items":{"$ref":"#/components/schemas/PartnerPackageListItem"}}}},"PartnerPackageTermin":{"type":"object","required":["terminId","fechaInicio","fechaFin","plazasDisponibles","cupoGestionado","netoDblEur","netoSglEur","netoVistaMarEur"],"description":"One departure date. Net prices: `netoDblEur` per person in a double/twin room, `netoSglEur` single supplement, `netoVistaMarEur` sea-view supplement. `plazasDisponibles` is the sentinel 9007199254740991 when `cupoGestionado` is false.","properties":{"terminId":{"type":"string","format":"uuid"},"fechaInicio":{"type":"string","format":"date","example":"2026-10-03"},"fechaFin":{"type":"string","format":"date","example":"2026-10-03"},"plazasDisponibles":{"type":"integer"},"cupoGestionado":{"type":"boolean"},"netoDblEur":{"type":"string","pattern":"^\\d+(\\.\\d{2})?$","description":"Euros as a decimal string, e.g. \"42.00\"."},"netoSglEur":{"type":"string","pattern":"^\\d+(\\.\\d{2})?$","description":"Euros as a decimal string, e.g. \"42.00\"."},"netoVistaMarEur":{"type":"string","pattern":"^\\d+(\\.\\d{2})?$","description":"Euros as a decimal string, e.g. \"42.00\"."}}},"PartnerPackageAvailability":{"type":"object","required":["slug","nombre","termins"],"properties":{"slug":{"type":"string"},"nombre":{"type":"string"},"termins":{"type":"array","items":{"$ref":"#/components/schemas/PartnerPackageTermin"}}}},"PartnerPackagePax":{"type":"object","required":["nombre","apellidos","fechaNacimiento"],"properties":{"nombre":{"type":"string","minLength":1,"maxLength":120},"apellidos":{"type":"string","minLength":1,"maxLength":120},"fechaNacimiento":{"type":"string","format":"date","example":"2026-10-03","description":"YYYY-MM-DD — drives the age band"},"sexo":{"type":"string","maxLength":10},"dni":{"type":"string","maxLength":60,"description":"ID / passport number"},"docCaducidad":{"type":"string","format":"date"},"esTitular":{"type":"boolean","default":false,"description":"Exactly one across all rooms"}}},"PartnerPackageRoom":{"type":"object","required":["tipoHabitacion","pax"],"properties":{"tipoHabitacion":{"type":"string","enum":["dbl_twin","single","single_premium_vista_mar"]},"preferenciaCama":{"type":["string","null"],"enum":["dbl","twin",null]},"vistaMar":{"type":"boolean","default":false},"pax":{"type":"array","minItems":1,"maxItems":6,"items":{"$ref":"#/components/schemas/PartnerPackagePax"}}}},"PartnerPackageBookingCreate":{"type":"object","additionalProperties":false,"required":["paqueteSlug","terminId","partnerRef","email","habitaciones"],"description":"Conciliation of a package sale you already charged. 1–4 rooms, nominative passengers, exactly one `esTitular`. No price field: the OS computes the net total.","properties":{"paqueteSlug":{"type":"string","minLength":1},"terminId":{"type":"string","format":"uuid","description":"From package availability"},"partnerRef":{"type":"string","minLength":1,"maxLength":80,"description":"Id of the booking in the partner's system. Unique per agency."},"email":{"type":"string","format":"email"},"telefono":{"type":"string","maxLength":40},"habitaciones":{"type":"array","minItems":1,"maxItems":4,"items":{"$ref":"#/components/schemas/PartnerPackageRoom"}}}},"PartnerPackageBookingCreated":{"type":"object","required":["id","referencia","partnerRef","netoTotalEur","esTest"],"properties":{"id":{"type":"string","format":"uuid"},"referencia":{"type":"string","description":"Human reference, e.g. ANDALUZJA-260122-0007"},"partnerRef":{"type":"string","minLength":1,"maxLength":80,"description":"Id of the booking in the partner's system. Unique per agency."},"netoTotalEur":{"type":"string","pattern":"^\\d+(\\.\\d{2})?$","description":"Euros as a decimal string, e.g. \"42.00\"."},"esTest":{"type":"boolean"}}},"PartnerPackageBooking":{"type":"object","required":["id","referencia","partnerRef","netoTotalEur","esTest","estado"],"properties":{"id":{"type":"string","format":"uuid"},"referencia":{"type":"string"},"partnerRef":{"type":["string","null"],"minLength":1,"maxLength":80,"description":"Id of the booking in the partner's system. Unique per agency."},"netoTotalEur":{"type":"string","pattern":"^\\d+(\\.\\d{2})?$","description":"Euros as a decimal string, e.g. \"42.00\"."},"esTest":{"type":"boolean"},"estado":{"type":"string","description":"Reservation state in the OS (e.g. firme, cancelada)"}}},"PartnerPackageBookingAmend":{"type":"object","additionalProperties":false,"minProperties":1,"properties":{"email":{"type":"string","format":"email"}},"description":"Only the contact email can change after conciliation. Rooms/pax: cancel + new POST."}}},"webhooks":{"booking.created":{"post":{"tags":["webhooks"],"summary":"booking.created","description":"A POST created a new booking (a partnerRef replay does not fire). `data.kind` is \"package\" for package bookings, absent for day-trips.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEnvelope"},"examples":{"daytrip":{"value":{"id":"88888888-8888-4888-8888-888888888888","evento":"booking.created","creado":"2026-10-01T10:00:00.000Z","data":{"id":"44444444-4444-4444-8444-444444444444","partnerRef":"WT-1001"}}},"package":{"value":{"id":"88888888-8888-4888-8888-888888888888","evento":"booking.created","creado":"2026-10-01T10:00:00.000Z","data":{"id":"66666666-6666-4666-8666-666666666666","partnerRef":"WT-2001","kind":"package"}}}}}}},"responses":{"2xx":{"description":"Acknowledged; anything else is retried"}}}},"booking.cancelled":{"post":{"tags":["webhooks"],"summary":"booking.cancelled","description":"A booking was cancelled through the API.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEnvelope"},"examples":{"default":{"value":{"id":"88888888-8888-4888-8888-888888888888","evento":"booking.cancelled","creado":"2026-10-01T10:05:00.000Z","data":{"id":"44444444-4444-4444-8444-444444444444"}}}}}}},"responses":{"2xx":{"description":"Acknowledged; anything else is retried"}}}},"departure.updated":{"post":{"tags":["webhooks"],"summary":"departure.updated","description":"Stop-sale changed on a departure where your agency holds seats. Re-check availability before selling more.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEnvelope"},"examples":{"default":{"value":{"id":"88888888-8888-4888-8888-888888888888","evento":"departure.updated","creado":"2026-10-01T12:00:00.000Z","data":{"salidaId":"11111111-1111-4111-8111-111111111111","stopSale":true}}}}}}},"responses":{"2xx":{"description":"Acknowledged; anything else is retried"}}}}},"paths":{"/excursions":{"get":{"operationId":"listExcursions","tags":["daytrips"],"summary":"List excursions","description":"All published day-trips with at least one Polish or English departure. Use `slug` for the other calls.","security":[{"partnerBearer":[]}],"responses":{"200":{"description":"Catalogue","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PartnerExcursionList"},"examples":{"default":{"value":{"excursions":[{"slug":"gibraltar","nombre":"Gibraltar","idiomas":["en","pl"]},{"slug":"ronda-setenil","nombre":"Ronda i Setenil","idiomas":["pl"]}]}}}}}},"401":{"description":"Missing or invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PartnerError"},"examples":{"default":{"value":{"error":"No autorizado"}}}}}},"429":{"description":"Over 60 requests per minute for this key","headers":{"Retry-After":{"description":"Seconds to wait before retrying","schema":{"type":"integer","example":60}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PartnerError"},"examples":{"default":{"value":{"error":"Too many requests","code":"RATE_LIMIT"}}}}}},"500":{"description":"Unexpected error — safe to retry GETs; POST /bookings is safe to retry thanks to partnerRef","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PartnerError"},"examples":{"default":{"value":{"error":"Error interno"}}}}}}}}},"/excursions/{slug}":{"get":{"operationId":"getExcursion","tags":["daytrips"],"summary":"Excursion detail and pickup stops","description":"Operators' offers for this excursion with **net** prices per head and the pickup stops you may send as `puntoRecogidaId`. Retail prices are never returned to partner keys.","security":[{"partnerBearer":[]}],"parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string","example":"gibraltar"}}],"responses":{"200":{"description":"Excursion","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PartnerExcursion"},"examples":{"default":{"value":{"slug":"gibraltar","nombre":"Gibraltar","ofertas":[{"organizadorId":"33333333-3333-4333-8333-333333333333","organizador":"Carla Travel","idiomas":["pl","en"],"netoAdultoEur":"42.00","netoNinoEur":"34.00","netoBebeEur":"0.00","recogidas":[{"puntoId":"22222222-2222-4222-8222-222222222222","nombre":"Hotel Sol Príncipe (parada bus)","nombrePl":"Hotel Sol Príncipe (przystanek)","nombreEn":"Hotel Sol Príncipe (bus stop)","zona":"Torremolinos","direccion":"ambas","horaEstandar":"07:15:00","lat":36.6172,"lng":-4.5063,"mapaUrl":"https://maps.app.goo.gl/example","estandarizada":true,"hoteles":[{"id":"77777777-7777-4777-8777-777777777777","nombre":"Sol Príncipe"}]}]}]}}}}}},"401":{"description":"Missing or invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PartnerError"},"examples":{"default":{"value":{"error":"No autorizado"}}}}}},"404":{"description":"Unknown slug","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PartnerError"},"examples":{"default":{"value":{"error":"Excursión no encontrada"}}}}}},"429":{"description":"Over 60 requests per minute for this key","headers":{"Retry-After":{"description":"Seconds to wait before retrying","schema":{"type":"integer","example":60}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PartnerError"},"examples":{"default":{"value":{"error":"Too many requests","code":"RATE_LIMIT"}}}}}},"500":{"description":"Unexpected error — safe to retry GETs; POST /bookings is safe to retry thanks to partnerRef","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PartnerError"},"examples":{"default":{"value":{"error":"Error interno"}}}}}}}}},"/excursions/{slug}/availability":{"get":{"operationId":"getExcursionAvailability","tags":["daytrips"],"summary":"Live availability by date range","description":"Open departures between `from` and `to` (inclusive) with live seats and net prices. **No hold**: check right before you charge and POST right after; a later POST can still return 409 MAX_PAX_LLENO.","security":[{"partnerBearer":[]}],"parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string","example":"gibraltar"}},{"name":"from","in":"query","required":true,"schema":{"type":"string","format":"date","example":"2026-10-03"}},{"name":"to","in":"query","required":true,"schema":{"type":"string","format":"date","example":"2026-10-31"}}],"responses":{"200":{"description":"Live availability","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PartnerAvailabilityResponse"},"examples":{"default":{"value":{"slug":"gibraltar","salidas":[{"salidaId":"11111111-1111-4111-8111-111111111111","fecha":"2026-10-03","horaSalida":"08:00","plazasDisponibles":12,"aforoGestionado":true,"idioma":"pl","organizadorId":"33333333-3333-4333-8333-333333333333","organizador":"Carla Travel","offsetRecogidaMin":0,"netoAdultoEur":"42.00","netoNinoEur":"34.00","netoBebeEur":"0.00"}]}}}}}},"400":{"description":"Invalid or missing from/to","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PartnerError"},"examples":{"default":{"value":{"error":"Parámetros from/to inválidos (YYYY-MM-DD)"}}}}}},"401":{"description":"Missing or invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PartnerError"},"examples":{"default":{"value":{"error":"No autorizado"}}}}}},"429":{"description":"Over 60 requests per minute for this key","headers":{"Retry-After":{"description":"Seconds to wait before retrying","schema":{"type":"integer","example":60}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PartnerError"},"examples":{"default":{"value":{"error":"Too many requests","code":"RATE_LIMIT"}}}}}},"500":{"description":"Unexpected error — safe to retry GETs; POST /bookings is safe to retry thanks to partnerRef","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PartnerError"},"examples":{"default":{"value":{"error":"Error interno"}}}}}}}}},"/pickups/resolve":{"get":{"operationId":"resolvePickup","tags":["daytrips"],"summary":"Resolve a hotel or GPS pin to our pickup stops","description":"Typeahead for checkout: send `salidaId` plus `q` (hotel or stop name, min 2 chars) or `lat`+`lng`. Returns up to 3 candidates with the stop id, tourist-facing time, map and (for GPS) distance. Use `puntoId` or `hotelId` on POST /bookings. The stop always belongs to the operator of that departure.","security":[{"partnerBearer":[]}],"parameters":[{"name":"salidaId","in":"query","required":true,"schema":{"type":"string","format":"uuid"},"description":"Departure from availability"},{"name":"q","in":"query","schema":{"type":"string","minLength":2,"maxLength":160,"example":"riu"},"description":"Hotel or stop name. XOR with lat+lng."},{"name":"lat","in":"query","schema":{"type":"number","example":36.62}},{"name":"lng","in":"query","schema":{"type":"number","example":-4.5}}],"responses":{"200":{"description":"Candidates (may be empty)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PartnerPickupResolve"},"examples":{"default":{"value":{"salidaId":"11111111-1111-4111-8111-111111111111","candidatos":[{"hotelId":"77777777-7777-4777-8777-777777777777","hotelNombre":"Sol Príncipe","puntoId":"22222222-2222-4222-8222-222222222222","paradaNombre":"Hotel Sol Príncipe (parada bus)","paradaNombrePl":"Hotel Sol Príncipe (przystanek)","paradaNombreEn":"Hotel Sol Príncipe (bus stop)","horaRecogida":"07:15:00","lat":36.6172,"lng":-4.5063,"mapaUrl":"https://maps.app.goo.gl/example","estandarizada":true,"zona":"Torremolinos","origen":"hotel","distanciaKm":null}]}}}}}},"400":{"description":"Missing q or lat+lng, or invalid salidaId","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PartnerError"},"examples":{"default":{"value":{"error":"Parámetros inválidos"}}}}}},"401":{"description":"Missing or invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PartnerError"},"examples":{"default":{"value":{"error":"No autorizado"}}}}}},"404":{"description":"Unknown or unpublished departure","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PartnerError"},"examples":{"default":{"value":{"error":"Salida no encontrada","code":"NO_ENCONTRADO"}}}}}},"429":{"description":"Over 60 requests per minute for this key","headers":{"Retry-After":{"description":"Seconds to wait before retrying","schema":{"type":"integer","example":60}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PartnerError"},"examples":{"default":{"value":{"error":"Too many requests","code":"RATE_LIMIT"}}}}}},"500":{"description":"Unexpected error — safe to retry GETs; POST /bookings is safe to retry thanks to partnerRef","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PartnerError"},"examples":{"default":{"value":{"error":"Error interno"}}}}}}}}},"/bookings":{"post":{"operationId":"createBooking","tags":["daytrips"],"summary":"Conciliate a day-trip booking","description":"Call after the tourist paid **you**. Takes the seats on the departure and creates the same reservation the agency sees in the portal. `partnerRef` (your booking id) is required and unique per agency: sending the same value again returns **200** with the original booking and takes no second seat. With a `ct_test_` key the row is flagged `esTest` and no seat is taken.","security":[{"partnerBearer":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PartnerBookingCreate"},"examples":{"default":{"value":{"excursionSlug":"gibraltar","salidaId":"11111111-1111-4111-8111-111111111111","titularNombre":"Jan","titularApellido":"Kowalski","clienteEmail":"jan@example.pl","clienteIdioma":"pl","telefonoContacto":"+48 600 000 000","adultos":2,"ninos":0,"bebesConAsiento":0,"bebesSinAsiento":0,"puntoRecogidaId":"22222222-2222-4222-8222-222222222222","partnerRef":"WT-1001"}}}}}},"responses":{"200":{"description":"Same partnerRef already conciliated — original booking, no extra seat","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PartnerBookingCreated"},"examples":{"default":{"value":{"id":"44444444-4444-4444-8444-444444444444","referencia":"44444444-4444-4444-8444-444444444444","partnerRef":"WT-1001","netoTotalEur":"84.00","plazas":2,"idioma":"pl","esTest":true,"puntoRecogidaId":"22222222-2222-4222-8222-222222222222","horaRecogida":"07:15:00","paradaNombre":"Hotel Sol Príncipe (parada bus)","paradaNombrePl":"Hotel Sol Príncipe (przystanek)","paradaNombreEn":"Hotel Sol Príncipe (bus stop)","lat":36.6172,"lng":-4.5063,"mapaUrl":"https://maps.app.goo.gl/example","hotelRecogidaId":"77777777-7777-4777-8777-777777777777"}}}}}},"201":{"description":"Created — seats taken","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PartnerBookingCreated"},"examples":{"default":{"value":{"id":"44444444-4444-4444-8444-444444444444","referencia":"44444444-4444-4444-8444-444444444444","partnerRef":"WT-1001","netoTotalEur":"84.00","plazas":2,"idioma":"pl","esTest":true,"puntoRecogidaId":"22222222-2222-4222-8222-222222222222","horaRecogida":"07:15:00","paradaNombre":"Hotel Sol Príncipe (parada bus)","paradaNombrePl":"Hotel Sol Príncipe (przystanek)","paradaNombreEn":"Hotel Sol Príncipe (bus stop)","lat":36.6172,"lng":-4.5063,"mapaUrl":"https://maps.app.goo.gl/example","hotelRecogidaId":"77777777-7777-4777-8777-777777777777"}}}}}},"400":{"description":"Invalid JSON or payload (Zod issues in `detalles`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PartnerValidationError"},"examples":{"missingPartnerRef":{"summary":"partnerRef missing","value":{"error":"Payload inválido","detalles":[{"code":"custom","path":["partnerRef"],"message":"partnerRef es obligatorio"}]}},"invalidJson":{"summary":"Body is not JSON","value":{"error":"JSON inválido"}}}}}},"401":{"description":"Missing or invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PartnerError"},"examples":{"default":{"value":{"error":"No autorizado"}}}}}},"404":{"description":"Unknown salida or offer","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PartnerError"},"examples":{"default":{"value":{"error":"Salida no encontrada","code":"NO_ENCONTRADO"}}}}}},"409":{"description":"No seats left (no hold after GET) or departure not open","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PartnerError"},"examples":{"full":{"value":{"error":"Sin plazas disponibles","code":"MAX_PAX_LLENO"}},"closed":{"value":{"error":"Salida no disponible","code":"SALIDA_CERRADA"}}}}}},"422":{"description":"Stop/hotel not valid for that departure, or departure not PL/EN","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PartnerError"},"examples":{"stop":{"value":{"error":"Parada no válida para esta salida","code":"PARADA_INVALIDA"}},"hotel":{"value":{"error":"Ese hotel no tiene parada en esta salida","code":"HOTEL_SIN_PARADA"}},"ambiguous":{"value":{"error":"Varios hoteles coinciden; usa /pickups/resolve o hotelRecogidaId","code":"HOTEL_AMBIGUO"}},"language":{"value":{"error":"Solo PL o EN","code":"IDIOMA"}}}}}},"429":{"description":"Over 60 requests per minute for this key","headers":{"Retry-After":{"description":"Seconds to wait before retrying","schema":{"type":"integer","example":60}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PartnerError"},"examples":{"default":{"value":{"error":"Too many requests","code":"RATE_LIMIT"}}}}}},"500":{"description":"Unexpected error — safe to retry GETs; POST /bookings is safe to retry thanks to partnerRef","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PartnerError"},"examples":{"default":{"value":{"error":"Error interno"}}}}}}}}},"/bookings/{id}":{"get":{"operationId":"getBooking","tags":["daytrips"],"summary":"Get a day-trip booking","description":"Your agency's bookings only. Another agency's id — or an unknown one — is a 404, never a 403.","security":[{"partnerBearer":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Booking","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PartnerBooking"},"examples":{"default":{"value":{"id":"44444444-4444-4444-8444-444444444444","referencia":"44444444-4444-4444-8444-444444444444","partnerRef":"WT-1001","netoTotalEur":"84.00","adultos":2,"ninos":0,"bebesConAsiento":0,"bebesSinAsiento":0,"titularNombre":"Jan","titularApellido":"Kowalski","clienteEmail":"jan@example.pl","telefonoContacto":"+48 600 000 000","esTest":true,"puntoRecogidaId":"22222222-2222-4222-8222-222222222222","horaRecogida":"07:15:00","paradaNombre":"Hotel Sol Príncipe (parada bus)","paradaNombrePl":"Hotel Sol Príncipe (przystanek)","paradaNombreEn":"Hotel Sol Príncipe (bus stop)","lat":36.6172,"lng":-4.5063,"mapaUrl":"https://maps.app.goo.gl/example","hotelRecogidaId":"77777777-7777-4777-8777-777777777777"}}}}}},"401":{"description":"Missing or invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PartnerError"},"examples":{"default":{"value":{"error":"No autorizado"}}}}}},"404":{"description":"Unknown id or another agency's booking","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PartnerError"},"examples":{"default":{"value":{"error":"Reserva no encontrada"}}}}}},"429":{"description":"Over 60 requests per minute for this key","headers":{"Retry-After":{"description":"Seconds to wait before retrying","schema":{"type":"integer","example":60}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PartnerError"},"examples":{"default":{"value":{"error":"Too many requests","code":"RATE_LIMIT"}}}}}},"500":{"description":"Unexpected error — safe to retry GETs; POST /bookings is safe to retry thanks to partnerRef","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PartnerError"},"examples":{"default":{"value":{"error":"Error interno"}}}}}}}},"patch":{"operationId":"amendBooking","tags":["daytrips"],"summary":"Amend pax, pickup or contact","description":"Same departure only. Pax changes re-check seats and recompute the net total; the pickup must belong to the same operator's offer. To change the date: cancel and POST again.","security":[{"partnerBearer":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PartnerBookingAmend"},"examples":{"pax":{"value":{"adultos":3}},"pickup":{"value":{"puntoRecogidaId":"22222222-2222-4222-8222-222222222222","telefonoContacto":"+48 600 000 000"}}}}}},"responses":{"200":{"description":"Amended","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PartnerBooking"},"examples":{"default":{"value":{"id":"44444444-4444-4444-8444-444444444444","referencia":"44444444-4444-4444-8444-444444444444","partnerRef":"WT-1001","netoTotalEur":"126.00","adultos":3,"ninos":0,"bebesConAsiento":0,"bebesSinAsiento":0,"titularNombre":"Jan","titularApellido":"Kowalski","clienteEmail":"jan@example.pl","telefonoContacto":"+48 600 000 000","esTest":true,"puntoRecogidaId":"22222222-2222-4222-8222-222222222222","horaRecogida":"07:15:00","paradaNombre":"Hotel Sol Príncipe (parada bus)","paradaNombrePl":"Hotel Sol Príncipe (przystanek)","paradaNombreEn":"Hotel Sol Príncipe (bus stop)","lat":36.6172,"lng":-4.5063,"mapaUrl":"https://maps.app.goo.gl/example","hotelRecogidaId":"77777777-7777-4777-8777-777777777777"}}}}}},"400":{"description":"Invalid JSON or payload (Zod issues in `detalles`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PartnerValidationError"},"examples":{"missingPartnerRef":{"summary":"partnerRef missing","value":{"error":"Payload inválido","detalles":[{"code":"custom","path":["partnerRef"],"message":"partnerRef es obligatorio"}]}},"invalidJson":{"summary":"Body is not JSON","value":{"error":"JSON inválido"}}}}}},"401":{"description":"Missing or invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PartnerError"},"examples":{"default":{"value":{"error":"No autorizado"}}}}}},"404":{"description":"Unknown id or another agency's booking","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PartnerError"},"examples":{"default":{"value":{"error":"Reserva no encontrada","code":"NO_ENCONTRADO"}}}}}},"409":{"description":"No seats for the extra pax, or departure no longer open","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PartnerError"},"examples":{"full":{"value":{"error":"Sin plazas disponibles","code":"MAX_PAX_LLENO"}},"closed":{"value":{"error":"Salida no disponible","code":"SALIDA_CERRADA"}}}}}},"422":{"description":"Stop not valid for that departure","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PartnerError"},"examples":{"default":{"value":{"error":"Parada no válida","code":"PARADA_INVALIDA"}}}}}},"429":{"description":"Over 60 requests per minute for this key","headers":{"Retry-After":{"description":"Seconds to wait before retrying","schema":{"type":"integer","example":60}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PartnerError"},"examples":{"default":{"value":{"error":"Too many requests","code":"RATE_LIMIT"}}}}}},"500":{"description":"Unexpected error — safe to retry GETs; POST /bookings is safe to retry thanks to partnerRef","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PartnerError"},"examples":{"default":{"value":{"error":"Error interno"}}}}}}}}},"/bookings/{id}/cancel":{"post":{"operationId":"cancelBooking","tags":["daytrips"],"summary":"Cancel a day-trip booking","description":"Deletes the booking and frees its seats (live keys). A later GET returns 404. Refunding the tourist is on your side.","security":[{"partnerBearer":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Cancelled","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PartnerCancelResult"},"examples":{"default":{"value":{"cancelled":true}}}}}},"401":{"description":"Missing or invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PartnerError"},"examples":{"default":{"value":{"error":"No autorizado"}}}}}},"404":{"description":"Unknown id or another agency's booking","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PartnerError"},"examples":{"default":{"value":{"error":"Reserva no encontrada","code":"NO_ENCONTRADO"}}}}}},"429":{"description":"Over 60 requests per minute for this key","headers":{"Retry-After":{"description":"Seconds to wait before retrying","schema":{"type":"integer","example":60}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PartnerError"},"examples":{"default":{"value":{"error":"Too many requests","code":"RATE_LIMIT"}}}}}},"500":{"description":"Unexpected error — safe to retry GETs; POST /bookings is safe to retry thanks to partnerRef","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PartnerError"},"examples":{"default":{"value":{"error":"Error interno"}}}}}}}}},"/packages":{"get":{"operationId":"listPackages","tags":["packages"],"summary":"List packages","description":"Multi-day circuits currently sold to partners.","security":[{"partnerBearer":[]}],"responses":{"200":{"description":"Catalogue","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PartnerPackageList"},"examples":{"default":{"value":{"packages":[{"slug":"andaluzja","nombre":"Andaluzja — magia południa Hiszpanii"}]}}}}}},"401":{"description":"Missing or invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PartnerError"},"examples":{"default":{"value":{"error":"No autorizado"}}}}}},"429":{"description":"Over 60 requests per minute for this key","headers":{"Retry-After":{"description":"Seconds to wait before retrying","schema":{"type":"integer","example":60}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PartnerError"},"examples":{"default":{"value":{"error":"Too many requests","code":"RATE_LIMIT"}}}}}},"500":{"description":"Unexpected error — safe to retry GETs; POST /bookings is safe to retry thanks to partnerRef","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PartnerError"},"examples":{"default":{"value":{"error":"Error interno"}}}}}}}}},"/packages/{slug}/availability":{"get":{"operationId":"getPackageAvailability","tags":["packages"],"summary":"Departure dates with net prices","description":"Every sellable departure date (`termin`) with live places and net prices per room type. No hold: a later POST can return 409 TERMIN_NO_DISPONIBLE or MAX_PAX_LLENO.","security":[{"partnerBearer":[]}],"parameters":[{"name":"slug","in":"path","required":true,"schema":{"type":"string","example":"andaluzja"}}],"responses":{"200":{"description":"Availability","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PartnerPackageAvailability"},"examples":{"default":{"value":{"slug":"andaluzja","nombre":"Andaluzja — magia południa Hiszpanii","termins":[{"terminId":"55555555-5555-4555-8555-555555555555","fechaInicio":"2027-01-22","fechaFin":"2027-01-29","plazasDisponibles":18,"cupoGestionado":true,"netoDblEur":"484.00","netoSglEur":"192.00","netoVistaMarEur":"96.00"}]}}}}}},"401":{"description":"Missing or invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PartnerError"},"examples":{"default":{"value":{"error":"No autorizado"}}}}}},"404":{"description":"Unknown slug","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PartnerError"},"examples":{"default":{"value":{"error":"Paquete no encontrado"}}}}}},"429":{"description":"Over 60 requests per minute for this key","headers":{"Retry-After":{"description":"Seconds to wait before retrying","schema":{"type":"integer","example":60}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PartnerError"},"examples":{"default":{"value":{"error":"Too many requests","code":"RATE_LIMIT"}}}}}},"500":{"description":"Unexpected error — safe to retry GETs; POST /bookings is safe to retry thanks to partnerRef","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PartnerError"},"examples":{"default":{"value":{"error":"Error interno"}}}}}}}}},"/package-bookings":{"post":{"operationId":"createPackageBooking","tags":["packages"],"summary":"Conciliate a package booking","description":"Call after the tourist paid **you**. Takes the places on the departure and creates a firm reservation with rooms and nominative passengers. `partnerRef` required and unique per agency: a replay returns **200** with the original reservation. `ct_test_` keys never take places.","security":[{"partnerBearer":[]}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PartnerPackageBookingCreate"},"examples":{"default":{"value":{"paqueteSlug":"andaluzja","terminId":"55555555-5555-4555-8555-555555555555","partnerRef":"WT-2001","email":"anna@example.pl","telefono":"+48 600 000 001","habitaciones":[{"tipoHabitacion":"dbl_twin","preferenciaCama":"dbl","vistaMar":false,"pax":[{"nombre":"Anna","apellidos":"Nowak","fechaNacimiento":"1985-04-12","esTitular":true},{"nombre":"Piotr","apellidos":"Nowak","fechaNacimiento":"1983-09-30","esTitular":false}]}]}}}}}},"responses":{"200":{"description":"Same partnerRef already conciliated — original reservation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PartnerPackageBookingCreated"},"examples":{"default":{"value":{"id":"66666666-6666-4666-8666-666666666666","referencia":"ANDALUZJA-260122-0007","partnerRef":"WT-2001","netoTotalEur":"968.00","esTest":true}}}}}},"201":{"description":"Created — places taken","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PartnerPackageBookingCreated"},"examples":{"default":{"value":{"id":"66666666-6666-4666-8666-666666666666","referencia":"ANDALUZJA-260122-0007","partnerRef":"WT-2001","netoTotalEur":"968.00","esTest":true}}}}}},"400":{"description":"Invalid payload, or business rule on rooms/pax","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PartnerError"},"examples":{"titular":{"value":{"error":"Exactamente un titular","code":"TITULAR"}},"rooms":{"value":{"error":"Sin habitaciones","code":"SIN_HABITACIONES"}},"occupancy":{"value":{"error":"Ocupación no válida","code":"OCUPACION_INVALIDA"}}}}}},"401":{"description":"Missing or invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PartnerError"},"examples":{"default":{"value":{"error":"No autorizado"}}}}}},"404":{"description":"Unknown package or departure","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PartnerError"},"examples":{"default":{"value":{"error":"Termin no encontrado","code":"NO_ENCONTRADO"}}}}}},"409":{"description":"Departure not sellable or no places left","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PartnerError"},"examples":{"closed":{"value":{"error":"Termin no disponible","code":"TERMIN_NO_DISPONIBLE"}},"full":{"value":{"error":"Sin plazas disponibles","code":"MAX_PAX_LLENO"}}}}}},"429":{"description":"Over 60 requests per minute for this key","headers":{"Retry-After":{"description":"Seconds to wait before retrying","schema":{"type":"integer","example":60}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PartnerError"},"examples":{"default":{"value":{"error":"Too many requests","code":"RATE_LIMIT"}}}}}},"500":{"description":"Unexpected error — safe to retry GETs; POST /bookings is safe to retry thanks to partnerRef","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PartnerError"},"examples":{"default":{"value":{"error":"Error interno"}}}}}}}}},"/package-bookings/{id}":{"get":{"operationId":"getPackageBooking","tags":["packages"],"summary":"Get a package booking","description":"Your agency's reservations only; unknown or foreign id → 404.","security":[{"partnerBearer":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Reservation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PartnerPackageBooking"},"examples":{"default":{"value":{"id":"66666666-6666-4666-8666-666666666666","referencia":"ANDALUZJA-260122-0007","partnerRef":"WT-2001","netoTotalEur":"968.00","esTest":true,"estado":"firme"}}}}}},"401":{"description":"Missing or invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PartnerError"},"examples":{"default":{"value":{"error":"No autorizado"}}}}}},"404":{"description":"Unknown id or another agency's reservation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PartnerError"},"examples":{"default":{"value":{"error":"Reserva no encontrada"}}}}}},"429":{"description":"Over 60 requests per minute for this key","headers":{"Retry-After":{"description":"Seconds to wait before retrying","schema":{"type":"integer","example":60}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PartnerError"},"examples":{"default":{"value":{"error":"Too many requests","code":"RATE_LIMIT"}}}}}},"500":{"description":"Unexpected error — safe to retry GETs; POST /bookings is safe to retry thanks to partnerRef","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PartnerError"},"examples":{"default":{"value":{"error":"Error interno"}}}}}}}},"patch":{"operationId":"amendPackageBooking","tags":["packages"],"summary":"Change the contact email","description":"Rooms and passengers are frozen after conciliation — cancel and POST again to change them.","security":[{"partnerBearer":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PartnerPackageBookingAmend"},"examples":{"default":{"value":{"email":"anna.nowak@example.pl"}}}}}},"responses":{"200":{"description":"Amended","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PartnerPackageBooking"},"examples":{"default":{"value":{"id":"66666666-6666-4666-8666-666666666666","referencia":"ANDALUZJA-260122-0007","partnerRef":"WT-2001","netoTotalEur":"968.00","esTest":true,"estado":"firme"}}}}}},"400":{"description":"Invalid JSON or payload (Zod issues in `detalles`)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PartnerValidationError"},"examples":{"missingPartnerRef":{"summary":"partnerRef missing","value":{"error":"Payload inválido","detalles":[{"code":"custom","path":["partnerRef"],"message":"partnerRef es obligatorio"}]}},"invalidJson":{"summary":"Body is not JSON","value":{"error":"JSON inválido"}}}}}},"401":{"description":"Missing or invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PartnerError"},"examples":{"default":{"value":{"error":"No autorizado"}}}}}},"404":{"description":"Unknown id or another agency's reservation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PartnerError"},"examples":{"default":{"value":{"error":"Reserva no encontrada","code":"NO_ENCONTRADO"}}}}}},"409":{"description":"Already paid — changes go through booking@","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PartnerError"},"examples":{"default":{"value":{"error":"Reserva ya pagada","code":"PAGADA"}}}}}},"429":{"description":"Over 60 requests per minute for this key","headers":{"Retry-After":{"description":"Seconds to wait before retrying","schema":{"type":"integer","example":60}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PartnerError"},"examples":{"default":{"value":{"error":"Too many requests","code":"RATE_LIMIT"}}}}}},"500":{"description":"Unexpected error — safe to retry GETs; POST /bookings is safe to retry thanks to partnerRef","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PartnerError"},"examples":{"default":{"value":{"error":"Error interno"}}}}}}}}},"/package-bookings/{id}/cancel":{"post":{"operationId":"cancelPackageBooking","tags":["packages"],"summary":"Cancel a package booking","description":"Releases the places (live keys). Refunding the tourist is on your side.","security":[{"partnerBearer":[]}],"parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Cancelled","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PartnerCancelResult"},"examples":{"default":{"value":{"cancelled":true}}}}}},"401":{"description":"Missing or invalid API key","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PartnerError"},"examples":{"default":{"value":{"error":"No autorizado"}}}}}},"404":{"description":"Unknown id or another agency's reservation","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PartnerError"},"examples":{"default":{"value":{"error":"Reserva no encontrada","code":"NO_ENCONTRADO"}}}}}},"409":{"description":"Already paid — cancellation goes through booking@","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PartnerError"},"examples":{"default":{"value":{"error":"Reserva ya pagada","code":"PAGADA"}}}}}},"429":{"description":"Over 60 requests per minute for this key","headers":{"Retry-After":{"description":"Seconds to wait before retrying","schema":{"type":"integer","example":60}}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PartnerError"},"examples":{"default":{"value":{"error":"Too many requests","code":"RATE_LIMIT"}}}}}},"500":{"description":"Unexpected error — safe to retry GETs; POST /bookings is safe to retry thanks to partnerRef","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PartnerError"},"examples":{"default":{"value":{"error":"Error interno"}}}}}}}}}}}