{"openapi":"3.1.0","info":{"title":"Corner28 direct booking API","version":"1.0.0","description":"One entire holiday apartment in Salamanca, Madrid. All amounts are integer EUR cents. Public quotes never hold dates. Checkout requires the guest's approval of the current total, deposit and terms; card payment takes place on Stripe. Do not collect card details. This API does not imply integration or endorsement by any AI provider.","contact":{"name":"Corner28","email":"venture@cruzalonso.com"}},"servers":[{"url":"https://www.corner28.es"}],"paths":{"/api/agent/property":{"get":{"operationId":"getProperty","description":"Read official property facts, contacts, known limitations and current booking capabilities.","parameters":[{"name":"locale","in":"query","schema":{"type":"string","enum":["es","en"],"default":"en"}}],"responses":{"200":{"description":"Public property details only.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Property"}}}}}}},"/api/agent/quote":{"post":{"operationId":"quoteStay","description":"Check dates and current prices. No personal data, payment, or inventory hold. Treat a 503 as unknown availability, never as available. When policy.requiresHostConfirmation is true, provide the booking link and ask the guest to contact the host before paying.","security":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Stay"}}}},"responses":{"200":{"description":"Success. Check status; a quote or checkout URL does not confirm a booking.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Quote"}}}},"400":{"description":"See the machine-readable error and message.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"See the machine-readable error and message.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Dates unavailable, changed price/policy, expired checkout, or host confirmation required.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"413":{"description":"See the machine-readable error and message.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"415":{"description":"See the machine-readable error and message.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"See the machine-readable error and message.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Availability, pricing or payment provider cannot be verified. Retry safely.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/agent/checkout":{"post":{"operationId":"createCheckout","description":"After explicit guest approval of dates, guests, price and published terms, create or retrieve a short-lived Stripe payment link. This holds dates but does not charge or confirm. If checkoutEnabled is false, use the bookingUrl for a guest handoff. After a timeout, reuse the same key and identical request.","security":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CheckoutRequest"}}}},"responses":{"200":{"description":"Success. Check status; a quote or checkout URL does not confirm a booking.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Checkout"}}}},"400":{"description":"See the machine-readable error and message.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"See the machine-readable error and message.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Dates unavailable, changed price/policy, expired checkout, or host confirmation required.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"413":{"description":"See the machine-readable error and message.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"415":{"description":"See the machine-readable error and message.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"See the machine-readable error and message.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Availability, pricing or payment provider cannot be verified. Retry safely.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/agent/status":{"post":{"operationId":"getCheckoutStatus","description":"Read status using the secret idempotency key in the body, never a URL. No guest personal details are returned. Only confirmed means a verified paid, confirmed reservation.","security":[],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/StatusRequest"}}}},"responses":{"200":{"description":"Success. Check status; a quote or checkout URL does not confirm a booking.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Status"}}}},"400":{"description":"See the machine-readable error and message.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"See the machine-readable error and message.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"409":{"description":"Dates unavailable, changed price/policy, expired checkout, or host confirmation required.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"413":{"description":"See the machine-readable error and message.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"415":{"description":"See the machine-readable error and message.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"See the machine-readable error and message.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"503":{"description":"Availability, pricing or payment provider cannot be verified. Retry safely.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}},"components":{"schemas":{"Error":{"type":"object","required":["error","message"],"properties":{"error":{"type":"string"},"message":{"type":"string"},"requiredNights":{"type":"integer"},"retryAfterSeconds":{"type":"integer"}}},"Stay":{"type":"object","additionalProperties":false,"required":["checkIn","checkOut","guests"],"properties":{"checkIn":{"type":"string","format":"date","description":"YYYY-MM-DD, Europe/Madrid. Must not be in the past."},"checkOut":{"type":"string","format":"date","description":"Exclusive checkout date. 1–90 nights, within 730 days."},"guests":{"type":"integer","minimum":1,"maximum":4},"locale":{"type":"string","enum":["es","en"],"default":"es"}}},"CheckoutRequest":{"type":"object","additionalProperties":false,"required":["checkIn","checkOut","guests","guestName","guestEmail","expectedTotalCents","expectedDepositCents","termsAccepted","policyVersion","idempotencyKey"],"properties":{"checkIn":{"type":"string","format":"date","description":"YYYY-MM-DD, Europe/Madrid. Must not be in the past."},"checkOut":{"type":"string","format":"date","description":"Exclusive checkout date. 1–90 nights, within 730 days."},"guests":{"type":"integer","minimum":1,"maximum":4},"locale":{"type":"string","enum":["es","en"],"default":"es"},"guestName":{"type":"string","minLength":2,"maxLength":120},"guestEmail":{"type":"string","format":"email","maxLength":254},"guestPhone":{"type":"string","maxLength":40},"expectedTotalCents":{"type":"integer","minimum":1,"description":"The guest-approved pricing.totalCents from the quote."},"expectedDepositCents":{"type":"integer","minimum":1,"description":"The guest-approved pricing.depositCents."},"termsAccepted":{"type":"boolean","const":true},"policyVersion":{"type":"string","description":"policy.version from the approved quote."},"idempotencyKey":{"type":"string","format":"uuid","description":"Generate a random UUID once per booking. Keep it secret as it authorizes status lookup. Reuse exactly the same key and body after timeouts. Never reuse for a different booking."}}},"StatusRequest":{"type":"object","additionalProperties":false,"required":["idempotencyKey"],"properties":{"idempotencyKey":{"type":"string","format":"uuid","description":"Generate a random UUID once per booking. Keep it secret as it authorizes status lookup. Reuse exactly the same key and body after timeouts. Never reuse for a different booking."}}},"Property":{"type":"object","required":["propertyId","name","maxGuests","capabilities","policy","urls"],"properties":{"propertyId":{"type":"string"},"name":{"type":"string"},"maxGuests":{"type":"integer"},"arrivalDeparturePolicy":{"type":"object","description":"Early check-in and early check-out require a prior request and confirmation from Corner28. Do not promise a time automatically.","properties":{"earlyCheckIn":{"type":"string","enum":["on_request"]},"earlyCheckOut":{"type":"string","enum":["on_request"]},"advanceRequestRequired":{"type":"boolean","const":true},"hostConfirmationRequired":{"type":"boolean","const":true}}},"capabilities":{"type":"object","properties":{"quote":{"type":"boolean"},"bookingHandoff":{"type":"boolean"},"checkout":{"type":"boolean"},"paymentRequiresGuestAuthorization":{"type":"boolean"}}},"policy":{"$ref":"#/components/schemas/Policy"},"urls":{"type":"object","additionalProperties":{"type":"string","format":"uri"}}}},"Policy":{"type":"object","required":["version","cancellation","requiresHostConfirmation"],"properties":{"version":{"type":"string"},"cancellation":{"type":["string","null"]},"requiresHostConfirmation":{"type":"boolean"},"url":{"type":"string","format":"uri"}}},"Quote":{"type":"object","required":["status","available","checkIn","checkOut","guests","currency","pricing","policy","bookingUrl","checkoutEnabled"],"properties":{"status":{"type":"string","const":"quote"},"available":{"type":"boolean","const":true},"checkIn":{"type":"string","format":"date","description":"YYYY-MM-DD, Europe/Madrid. Must not be in the past."},"checkOut":{"type":"string","format":"date","description":"Exclusive checkout date. 1–90 nights, within 730 days."},"guests":{"type":"integer","minimum":1,"maximum":4},"locale":{"type":"string","enum":["es","en"],"default":"es"},"currency":{"type":"string","const":"EUR"},"quotedAt":{"type":"string","format":"date-time"},"recheckAfterSeconds":{"type":"integer"},"pricing":{"type":"object","required":["unit","totalCents","depositCents","remainingCents","cleaningFeeCents"],"properties":{"unit":{"type":"string","const":"cents"},"accommodationCents":{"type":"integer"},"cleaningFeeCents":{"type":"integer"},"totalCents":{"type":"integer"},"depositCents":{"type":"integer"},"remainingCents":{"type":"integer"},"remainingDue":{"type":"string","const":"on_arrival"},"nightly":{"type":"array","items":{"type":"object","properties":{"date":{"type":"string","format":"date"},"amountCents":{"type":"integer"}}}}}},"policy":{"$ref":"#/components/schemas/Policy"},"bookingUrl":{"type":"string","format":"uri"},"checkoutEnabled":{"type":"boolean"}}},"Checkout":{"type":"object","required":["reservationId","status","checkoutUrl"],"properties":{"reservationId":{"type":"string","format":"uuid"},"status":{"type":"string","enum":["awaiting_payment","payment_received","confirmed"]},"checkoutUrl":{"type":["string","null"],"format":"uri"},"expiresAt":{"type":"string","format":"date-time"},"currency":{"type":"string","const":"EUR"},"totalCents":{"type":"integer"},"depositCents":{"type":"integer"},"remainingCents":{"type":"integer"}}},"Status":{"type":"object","required":["reservationId","status","checkIn","checkOut","totalCents","depositCents"],"properties":{"reservationId":{"type":"string","format":"uuid"},"status":{"type":"string","enum":["awaiting_payment","confirmed","cancelled"]},"checkIn":{"type":"string","format":"date","description":"YYYY-MM-DD, Europe/Madrid. Must not be in the past."},"checkOut":{"type":"string","format":"date","description":"Exclusive checkout date. 1–90 nights, within 730 days."},"currency":{"type":"string","const":"EUR"},"totalCents":{"type":"integer"},"depositCents":{"type":"integer"}}}}}}