API reference

Schemas

The 83 objects the requests and responses of the API are built from. Every type named on an operation page resolves to an entry here.

All schemas

A field whose own shape is another named object stops at that name and links here rather than nesting further — that is what keeps a table readable instead of unrolling a whole booking into one column of dotted paths.

AccountDto

FieldTypeRequiredDescriptionExample
partnerAccountPartnerDtoRequired
partner.idstringRequiredOur identifier for the partner. Stable.1e4861d9-af96-4821-b264-c1670dbd4b2b
partner.slugstringRequiredShort name of the partner, as assigned in the portal.whitelabeltravel
partner.namestringOptionalDisplay name of the partner.Whitelabel Travel
keyAccountKeyDtoRequired
key.prefixstringRequiredThe visible part of the key this request came in with. It is shown in the portal as well — so you can tell which of several keys is currently talking.vcb_test_A8xWTLai
key.environmentstringRequiredEnvironment of this key. The gateway compares it against the kind of key the deployment is configured to accept and refuses a mismatch with wrong_environment — but today a single deployment accepts both kinds, so that refusal does not fire yet. It starts to refuse once the two hosts are separate deployments; until then, matching the key prefix against the base URL is on the caller (the SDK derives the host from the prefix by itself).One of: LIVE TESTTEST
key.sandboxbooleanRequiredWhether this call lands on the sandbox: no suppliers, no costs, no search quota.true
key.settlementstringRequiredHow THIS key settles — who takes the traveller's money for anything bought with it. partner: you are merchant of record. Prices you see are net, your account is charged, your traveller pays you. vacabee: we are merchant of record. Prices you see are end-customer prices, your traveller pays us, and you earn a commission. The mode follows the key, not the request: a key does one of the two and never both. If you run both modes you hold two keys — which is also what makes it unambiguous which price a search result carries. Sending settlement in a booking body is allowed and is checked against this value; it cannot change it.One of: partner vacabeepartner
settlementModesstring[]RequiredSettlement modes your ACCOUNT is set up for — the menu, not the choice. What this particular key does is key.settlement, and it is exactly one of these. A mode listed here that is not your key's is reachable by issuing a key for it, not by asking for it in a request body: a body field that decides who pays would be a permission you write for yourself, and the price it applies to was already formed during the search.["partner","vacabee"]
scopesstring[]RequiredWhat this key may do. A call outside this list is refused with insufficient_scope and names the missing scope.One of: account:read hotels:search hotels:book flights:search flights:book transfers:search transfers:book esim:read esim:order ledger:read webhooks:manage customers:manage["account:read","hotels:search","hotels:book"]
limitsAccountLimitsDtoRequired
limits.requestsPerMinutenumberRequiredRequests per minute across all classes, unless limited more tightly.600
limits.rateLimitClassesmap<string, number>RequiredUpper bound per class and minute. A search counts differently from a read, because it costs money at the supplier.{"READ":1200,"DEFAULT":600,"SEARCH":120,"BOOKING":60}
limits.freeSearchesPerDaynumberRequiredBillable searches per day that cost nothing. Only calls that actually reach a supplier are counted.1000
limits.searchHardCapPerDaynumber | nullRequiredHard upper bound on searches per day, or null for none. Do NOT confuse it with the free quota: beyond that one you are billed, here you are refused.null
limits.maxWebsocketConnectionsnumberRequiredConcurrent WebSocket connections for the streaming channel.20
limits.maxWebsocketSubscriptionsnumberRequiredConcurrent subscriptions across all WebSocket connections.200
ipAllowliststring[]RequiredIP addresses and networks this key may be used from. Empty means no restriction.[]

AccountKeyDto

FieldTypeRequiredDescriptionExample
prefixstringRequiredThe visible part of the key this request came in with. It is shown in the portal as well — so you can tell which of several keys is currently talking.vcb_test_A8xWTLai
environmentstringRequiredEnvironment of this key. The gateway compares it against the kind of key the deployment is configured to accept and refuses a mismatch with wrong_environment — but today a single deployment accepts both kinds, so that refusal does not fire yet. It starts to refuse once the two hosts are separate deployments; until then, matching the key prefix against the base URL is on the caller (the SDK derives the host from the prefix by itself).One of: LIVE TESTTEST
sandboxbooleanRequiredWhether this call lands on the sandbox: no suppliers, no costs, no search quota.true
settlementstringRequiredHow THIS key settles — who takes the traveller's money for anything bought with it. partner: you are merchant of record. Prices you see are net, your account is charged, your traveller pays you. vacabee: we are merchant of record. Prices you see are end-customer prices, your traveller pays us, and you earn a commission. The mode follows the key, not the request: a key does one of the two and never both. If you run both modes you hold two keys — which is also what makes it unambiguous which price a search result carries. Sending settlement in a booking body is allowed and is checked against this value; it cannot change it.One of: partner vacabeepartner

AccountLimitsDto

FieldTypeRequiredDescriptionExample
requestsPerMinutenumberRequiredRequests per minute across all classes, unless limited more tightly.600
rateLimitClassesmap<string, number>RequiredUpper bound per class and minute. A search counts differently from a read, because it costs money at the supplier.{"READ":1200,"DEFAULT":600,"SEARCH":120,"BOOKING":60}
freeSearchesPerDaynumberRequiredBillable searches per day that cost nothing. Only calls that actually reach a supplier are counted.1000
searchHardCapPerDaynumber | nullRequiredHard upper bound on searches per day, or null for none. Do NOT confuse it with the free quota: beyond that one you are billed, here you are refused.null
maxWebsocketConnectionsnumberRequiredConcurrent WebSocket connections for the streaming channel.20
maxWebsocketSubscriptionsnumberRequiredConcurrent subscriptions across all WebSocket connections.200

AccountPartnerDto

FieldTypeRequiredDescriptionExample
idstringRequiredOur identifier for the partner. Stable.1e4861d9-af96-4821-b264-c1670dbd4b2b
slugstringRequiredShort name of the partner, as assigned in the portal.whitelabeltravel
namestringOptionalDisplay name of the partner.Whitelabel Travel

ApiErrorDetailDto

FieldTypeRequiredDescriptionExample
typestringRequiredFor branching. Stable and machine-readable — a caller may switch on this. New types can be added at any time, so an unknown type must be treated like a generic error.One of: authentication_required invalid_api_key api_key_revoked api_key_expired insufficient_scope ip_not_allowed wrong_environment invalid_request not_found method_not_allowed unsupported_media_type idempotency_key_required idempotency_key_reuse idempotency_key_in_progress offer_expired price_changed insufficient_funds search_quota_exceeded account_not_active not_implemented settlement_not_allowed settlement_not_available rate_limit_exceeded upstream_unavailable upstream_error internal_errorinsufficient_scope
messagestringRequiredFor humans: log lines and support requests. The wording can change at any time — NEVER compare against it.The key lacks the `hotels:book` scope.
requestIdstringRequiredIdentifier of this exact request, also in the X-Request-Id header. Quote it in every support request: it is how we find the request in our logs.a4f1c358-6b2e-5acb-8ef5-0c3d0bcc35f5
docUrlstringOptionalThe documentation page that explains this error. Points at the topic, not at one page per error type.https://developers.vacabee.com/docs/authentication
missingScopestringOptionalOn insufficient_scope: the scope that is missing. Saves a caller from having to read it out of the message.hotels:book
retryAfternumberOptionalOn rate_limit_exceeded: seconds until the next permitted attempt. Also present in the Retry-After header.12

ApiErrorDto

FieldTypeRequiredDescriptionExample
errorApiErrorDetailDtoRequired
error.typestringRequiredFor branching. Stable and machine-readable — a caller may switch on this. New types can be added at any time, so an unknown type must be treated like a generic error.One of: authentication_required invalid_api_key api_key_revoked api_key_expired insufficient_scope ip_not_allowed wrong_environment invalid_request not_found method_not_allowed unsupported_media_type idempotency_key_required idempotency_key_reuse idempotency_key_in_progress offer_expired price_changed insufficient_funds search_quota_exceeded account_not_active not_implemented settlement_not_allowed settlement_not_available rate_limit_exceeded upstream_unavailable upstream_error internal_errorinsufficient_scope
error.messagestringRequiredFor humans: log lines and support requests. The wording can change at any time — NEVER compare against it.The key lacks the `hotels:book` scope.
error.requestIdstringRequiredIdentifier of this exact request, also in the X-Request-Id header. Quote it in every support request: it is how we find the request in our logs.a4f1c358-6b2e-5acb-8ef5-0c3d0bcc35f5
error.docUrlstringOptionalThe documentation page that explains this error. Points at the topic, not at one page per error type.https://developers.vacabee.com/docs/authentication
error.missingScopestringOptionalOn insufficient_scope: the scope that is missing. Saves a caller from having to read it out of the message.hotels:book
error.retryAfternumberOptionalOn rate_limit_exceeded: seconds until the next permitted attempt. Also present in the Retry-After header.12

CancelBookingDto

FieldTypeRequiredDescription
reasonstringOptional

ConfirmFlightChangeDto

FieldTypeRequiredDescriptionExample
changeIdstringRequiredchg_9f2c…

ContactDto

FieldTypeRequiredDescriptionExample
emailstringRequiredada@example.com
phonestringOptional+4915112345678

CreatedWebhookEndpointDto

FieldTypeRequiredDescriptionExample
endpointWebhookEndpointDtoRequired
endpoint.idstringRequiredep_01J8…
endpoint.urlstringRequiredhttps://api.example.com/hooks/vacabee
endpoint.eventsstring[]RequiredOne of: booking.confirmed booking.failed booking.cancelled booking.modified checkout.created checkout.expired payment.succeeded payment.failed payment.refunded esim.provisioned esim.usage_threshold ledger.low_balance search_quota.threshold settlement.invoice_issued settlement.payment_failed apikey.revoked
endpoint.secretPrefixstringRequiredOnly the beginning of the signing secret, so that you can tell two endpoints apart. The full value appears exactly once: on creation and on rotation.whsec_LL2z9E
endpoint.statusstringRequiredOne of: ACTIVE DISABLED
endpoint.descriptionstring | nullRequired
endpoint.lastDeliveryAtstring | nullRequired2026-09-05T09:12:44.000Z
endpoint.createdAtstringRequired2026-08-30T11:02:00.000Z
endpoint.canDeliverbooleanRequiredFalse for endpoints predating delivery: for those we only hold a hash of the secret, and you cannot sign with a hash. Such endpoints receive nothing until the secret has been rotated once.
signingSecretstringRequiredTHE ONLY TIME you get to see this value. It is the only thing that tells a genuine delivery apart from any other POST to your public endpoint. Lost it? Then rotate — do not ask us for it.whsec_LL2z9ESy…
signingSecretShownOncebooleanRequiredtrue
previousSecretValidUntilstring | nullOptionalOn rotation only: up to this point we ALSO sign with the previous secret, so you can switch over without losing an event.

CreateEsimOrderDto

FieldTypeRequiredDescriptionExample
planIdstringRequiredplan_01J8…
quantitynumberOptional1
externalReferencestringRequiredYOUR reference for this order. It is the idempotency across the whole operation: the same value twice yields ONE order, not two — even when the first response never reached you.order-2026-0915-abc
customerEsimCustomerDtoRequired
customer.firstNamestringRequiredAda
customer.lastNamestringRequiredLovelace
customer.emailstringRequiredYOUR customer's address. We send him nothing — it goes to the supplier, who issues the eSIM against it.ada@example.com
customer.phonestringOptional+4915112345678
settlementstringOptionalOptional. Who takes the traveller’s money for this order. The mode follows your API KEY — sending it here is a check, not a choice: a value that contradicts your key is refused rather than obeyed.One of: partner vacabee

CreateFlightOrderDto

FieldTypeRequiredDescriptionExample
offerIdstringRequiredoff_8f2c…
externalReferencestringRequiredorder-2026-0915-abc
passengersTravellerDto[]RequiredOne entry per traveller, as the airline needs them.
passengers[].firstNamestringRequiredAda
passengers[].lastNamestringRequiredLovelace
passengers[].dateOfBirthstringOptionalISO date. Most airlines require it; without it the order can be rejected.1990-05-17
passengers[].genderstringOptionalAs the airline records it. Pass through what your customer stated.f
passengers[].titlestringOptionalSalutation as the airline records it — mr, ms, mrs, miss, dr. Optional here and required by most carriers: leaving it out answers "Each passenger must include 'title'" from the airline.mr
contactContactDtoRequired
contact.emailstringRequiredada@example.com
contact.phonestringOptional+4915112345678
maxTotalCentsnumberOptionalUpper limit. Airlines REPRICE an offer before booking — that is the rule, not the exception. If the new price is above it, NOTHING is booked and nothing is charged; you get 409 price_changed with both amounts and decide yourself. Without this value we book at the new price.45000
settlementstringOptionalOptional. Who takes the traveller’s money for this order. The mode follows your API KEY — sending it here is a check, not a choice.One of: partner vacabee

CreateHotelBookingDto

FieldTypeRequiredDescriptionExample
rateIdstringRequiredFrom a search result. Valid for 20 minutes — after that 410 offer_expired, and you search again. The window is how long the offer is valid at the provider, not a limit we set.rate_8f2c…
externalReferencestringRequiredYOUR reference. Idempotency across the whole operation: the same value twice yields ONE booking — even when the first response never reached you.booking-2026-0915-abc
guestsGuestDto[]RequiredOne entry per guest — as many as the rate is for. A rate for two adults and a child needs three names; naming fewer is refused rather than padded, because an invented name ends up on an invoice and at a hotel desk. Pass age for a child. With several rooms the guests are placed in the order given.
guests[].firstNamestringRequiredAda
guests[].lastNamestringRequiredLovelace
guests[].agenumberOptionalA child's age. Suppliers price children by age, so leave it off for adults.8
contactContactDtoRequired
contact.emailstringRequiredada@example.com
contact.phonestringOptional+4915112345678
specialRequestsstringOptional
settlementstringOptionalOptional. Who takes the traveller’s money for this booking. The mode follows your API KEY — sending it here is a check, not a choice: a value that contradicts your key is refused rather than obeyed.One of: partner vacabee

CreateTransferOrderDto

FieldTypeRequiredDescriptionExample
quoteIdstringRequiredFrom a search result. Valid for about 20 minutes; after that the answer is 410 offer_expired and you search again.qte_9dJ2…
externalReferencestringRequiredYOUR reference for this order, and the idempotency key. Repeating a request with the same value returns the same order instead of buying a second transfer — which is what makes a retry after a timeout safe.
holderTransferHolderDtoRequired
holder.firstNamestringRequired
holder.lastNamestringRequired
holder.emailstringRequired
holder.phonestringRequiredE.164. The driver calls this number when the pickup goes wrong.+351912345678
remarkstringOptionalFree text for the driver, e.g. "two large suitcases, child seat needed".
languagestringOptionalen
settlementstringOptionalOptional. Who takes the traveller’s money for this order. The mode follows your API KEY — sending it here is a check, not a choice: a value that contradicts your key is refused rather than obeyed.One of: partner vacabee
flightNumberstringOptionalThe traveller flight this transfer meets. Required by the supplier when either end is an airport (IATA), so the driver can follow the arrival. Max 7 characters.IB3100

CreateWebhookEndpointDto

FieldTypeRequiredDescriptionExample
urlstringRequiredhttps, and the host must resolve publicly. A target inside a private network would make us query internal services from within our own cluster and deliver the answer to you.https://api.example.com/hooks/vacabee
eventsstring[]RequiredAn unknown event is refused rather than accepted: a subscription to something nobody publishes looks like coverage and is silence.One of: booking.confirmed booking.failed booking.cancelled booking.modified checkout.created checkout.expired payment.succeeded payment.failed payment.refunded esim.provisioned esim.usage_threshold ledger.low_balance search_quota.threshold settlement.invoice_issued settlement.payment_failed apikey.revoked["booking.confirmed","payment.refunded"]
descriptionstringOptionalProduction consumer

DependencyStatusDto

FieldTypeRequiredDescriptionExample
statusstringRequiredOne of: ok degraded errorok
responseTimenumberOptionalResponse time of the check, in milliseconds.7

EsimCheckoutDto

FieldTypeRequiredDescriptionExample
urlstring | nullRequiredSend your traveller here. The link is issued ONCE and is never stored, by us or by anyone: repeat the same externalReference and you get the order back with url: null and the deadline of the link you already have. Keep it when you receive it.https://mrbee.ai/pay/8Kx…
expiresAtstringRequiredWhen the link stops being payable. Running out is the ORDINARY outcome of an unpaid order, not a fault: nothing is reserved for your traveller, the price simply expires. You then get esim.failed with reason checkout_expired and can order again at the price of the day.2026-09-10T12:15:00.000Z
amountCentsnumberRequiredWhat YOUR TRAVELLER pays, in minor units — the end-customer price. Not a net price: in this mode you buy nothing from us.4200
currencystringRequiredusd

EsimCountryDto

FieldTypeRequiredDescriptionExample
codestringRequiredISO 3166-1 alpha-2.DE
namestringRequiredGermany
flagstring | nullRequiredFlag emoji, when we have one.🇩🇪

EsimCustomerDto

FieldTypeRequiredDescriptionExample
firstNamestringRequiredAda
lastNamestringRequiredLovelace
emailstringRequiredYOUR customer's address. We send him nothing — it goes to the supplier, who issues the eSIM against it.ada@example.com
phonestringOptional+4915112345678

EsimOrderDto

FieldTypeRequiredDescriptionExample
purchaseIdstringRequiredpur_01J8…
netAmountCentsnumberRequiredWhat YOU owe us for this order — your purchase price. What you charge your own customer is your business and we do not see it. 0 when this order settles as vacabee: there your traveller pays us directly and you owe us nothing at all. What HE pays is in checkout.amountCents.1080
currencystringRequiredusd
statusstringRequiredACTIVE means: the eSIM is provisioned at the supplier. AWAITING_PAYMENT appears only in vacabee settlement and means exactly what it says — the order is written down, NOTHING has been bought, and nothing will be until your traveller has paid through checkout.url.One of: ACTIVE PENDING AWAITING_PAYMENT
provisioningStatusstringRequiredprovisioned
iccidstring | nullRequired89310…
activationCodestring | nullRequiredLPA:1$smdp.example$ABC
lpaCodestring | nullRequired
smdpAddressstring | nullRequired
qrCodeUrlstring | nullRequiredhttps://…/qr.png
settlementstringRequiredHow this order settles — echoed back so you never have to infer it. It follows your API KEY; see /docs/settlement.One of: partner vacabee
checkoutobject | nullRequiredWhere your traveller pays. null in partner settlement, where the order is already paid for out of your account.
checkout.urlstring | nullRequiredSend your traveller here. The link is issued ONCE and is never stored, by us or by anyone: repeat the same externalReference and you get the order back with url: null and the deadline of the link you already have. Keep it when you receive it.https://mrbee.ai/pay/8Kx…
checkout.expiresAtstringRequiredWhen the link stops being payable. Running out is the ORDINARY outcome of an unpaid order, not a fault: nothing is reserved for your traveller, the price simply expires. You then get esim.failed with reason checkout_expired and can order again at the price of the day.2026-09-10T12:15:00.000Z
checkout.amountCentsnumberRequiredWhat YOUR TRAVELLER pays, in minor units — the end-customer price. Not a net price: in this mode you buy nothing from us.4200
checkout.currencystringRequiredusd

EsimOrderPageDto

FieldTypeRequiredDescriptionExample
itemsEsimOrderDto[]Required
items[].purchaseIdstringRequiredpur_01J8…
items[].netAmountCentsnumberRequiredWhat YOU owe us for this order — your purchase price. What you charge your own customer is your business and we do not see it. 0 when this order settles as vacabee: there your traveller pays us directly and you owe us nothing at all. What HE pays is in checkout.amountCents.1080
items[].currencystringRequiredusd
items[].statusstringRequiredACTIVE means: the eSIM is provisioned at the supplier. AWAITING_PAYMENT appears only in vacabee settlement and means exactly what it says — the order is written down, NOTHING has been bought, and nothing will be until your traveller has paid through checkout.url.One of: ACTIVE PENDING AWAITING_PAYMENT
items[].provisioningStatusstringRequiredprovisioned
items[].iccidstring | nullRequired89310…
items[].activationCodestring | nullRequiredLPA:1$smdp.example$ABC
items[].lpaCodestring | nullRequired
items[].smdpAddressstring | nullRequired
items[].qrCodeUrlstring | nullRequiredhttps://…/qr.png
items[].settlementstringRequiredHow this order settles — echoed back so you never have to infer it. It follows your API KEY; see /docs/settlement.One of: partner vacabee
items[].checkoutobject | nullRequiredWhere your traveller pays. null in partner settlement, where the order is already paid for out of your account.
items[].checkout.urlstring | nullRequiredSend your traveller here. The link is issued ONCE and is never stored, by us or by anyone: repeat the same externalReference and you get the order back with url: null and the deadline of the link you already have. Keep it when you receive it.https://mrbee.ai/pay/8Kx…
items[].checkout.expiresAtstringRequiredWhen the link stops being payable. Running out is the ORDINARY outcome of an unpaid order, not a fault: nothing is reserved for your traveller, the price simply expires. You then get esim.failed with reason checkout_expired and can order again at the price of the day.2026-09-10T12:15:00.000Z
items[].checkout.amountCentsnumberRequiredWhat YOUR TRAVELLER pays, in minor units — the end-customer price. Not a net price: in this mode you buy nothing from us.4200
items[].checkout.currencystringRequiredusd
nextCursorstring | nullRequiredPass as cursor to get the next page. null means this was the last one.null

EsimPlanDto

FieldTypeRequiredDescriptionExample
idstringRequiredplan_7f3a2b
providerPlanIdstringRequiredThe provider's own identifier.plan_7f3a2b
namestringRequiredEurope 10 GB / 30 days
planTypestringRequiredOne of: data unlimited voice_smsdata
dataAmountMbnumberRequiredIncluded data in megabytes.10240
validityDaysnumberRequired30
voiceSecondsnumber | nullRequirednull
smsCountnumber | nullRequirednull
isGlobalbooleanRequiredfalse
countriesEsimCountryDto[]Required
countries[].codestringRequiredISO 3166-1 alpha-2.DE
countries[].namestringRequiredGermany
countries[].flagstring | nullRequiredFlag emoji, when we have one.🇩🇪
regionsEsimRegionDto[]Required
regions[].idstringRequiredreg_eu
regions[].codestringRequiredEU
regions[].namestringRequiredEurope
retailCentsnumberRequiredYOUR price in minor units — the net price after your margin, not the consumer price. This is what we charge your account when you order.4200
currencystringRequiredusd

EsimPlanPageDto

FieldTypeRequiredDescriptionExample
itemsEsimPlanDto[]Required
items[].idstringRequiredplan_7f3a2b
items[].providerPlanIdstringRequiredThe provider's own identifier.plan_7f3a2b
items[].namestringRequiredEurope 10 GB / 30 days
items[].planTypestringRequiredOne of: data unlimited voice_smsdata
items[].dataAmountMbnumberRequiredIncluded data in megabytes.10240
items[].validityDaysnumberRequired30
items[].voiceSecondsnumber | nullRequirednull
items[].smsCountnumber | nullRequirednull
items[].isGlobalbooleanRequiredfalse
items[].countriesEsimCountryDto[]Required
items[].countries[].codestringRequiredISO 3166-1 alpha-2.DE
items[].countries[].namestringRequiredGermany
items[].countries[].flagstring | nullRequiredFlag emoji, when we have one.🇩🇪
items[].regionsEsimRegionDto[]Required
items[].regions[].idstringRequiredreg_eu
items[].regions[].codestringRequiredEU
items[].regions[].namestringRequiredEurope
items[].retailCentsnumberRequiredYOUR price in minor units — the net price after your margin, not the consumer price. This is what we charge your account when you order.4200
items[].currencystringRequiredusd
totalnumberRequired42
pagenumberRequired1
pageSizenumberRequired25
totalPagesnumberRequired2

EsimRegionDto

FieldTypeRequiredDescriptionExample
idstringRequiredreg_eu
codestringRequiredEU
namestringRequiredEurope

EsimUsageDto

FieldTypeRequiredDescriptionExample
dataUsedMbnumber | nullRequired1024
dataRemainingMbnumber | nullRequired9216
dataTotalMbnumber | nullRequired10240
statusstring | nullRequiredNull means: the supplier was briefly unreachable and you are seeing the last known state.
expiresAtstring | nullRequired
expiredbooleanRequired

FlightCancellationDto

FieldTypeRequiredDescriptionExample
orderIdstring | nullRequiredord_5a11
statusstringRequiredCANCELLED
refundedCentsnumberRequiredWhat was credited. 0 on a non-refundable fare.19260
currencystringRequiredUSD
refundCreditedbooleanRequiredWhether the credit reached your account. FALSE means the flight IS cancelled and we owe you — we can see it and so can you; do not treat it as "nothing happened".true
messagestring | nullRequiredPresent only when refundCredited is false.

FlightCancellationQuoteDto

FieldTypeRequiredDescriptionExample
penaltyCentsnumberRequiredThe airline penalty. Stays with us; it is the difference between what you paid and what comes back.2500
refundCentsnumberRequiredWhat is credited to your account if you cancel. Your NET figure — the consumer markup was never in your price and is not in your refund.19260
currencystringRequiredUSD
freeCancellationbooleanRequiredTrue when the whole amount comes back.false
cancellablebooleanRequiredFalse when the airline will not take this order back at all — then cancelling is refused rather than credited at zero.true

FlightChangeDto

FieldTypeRequiredDescriptionExample
orderIdstringRequiredord_5a11
statusstringRequiredCHANGED
differenceCentsnumberRequiredSigned, as in the option you took.4200
currencystringRequiredUSD
settledbooleanRequiredWhether the money moved. FALSE means the TICKET IS REISSUED and the difference has not settled — a debt in one direction or the other, never a reason to retry.true
messagestring | nullRequired

FlightChangeOptionDto

FieldTypeRequiredDescriptionExample
changeIdstringRequiredPass this to the change endpoint. Nothing else identifies it.chg_9f2c…
differenceCentsnumberRequiredSIGNED. Positive means you pay the difference; NEGATIVE means the new itinerary is cheaper and the difference is credited to your account. There is no separate direction field — read the sign.4200
currencystringRequiredUSD
newTotalCentsnumber | nullRequired46200
expiresAtstring | nullRequired
slicesFlightSliceDto[]Required
slices[].originstringRequiredLHR
slices[].destinationstringRequiredJFK
slices[].departurestringRequired2026-11-15T08:00:00Z
slices[].arrivalstringRequired2026-11-15T16:30:00Z
slices[].durationMinutesnumberRequired510
slices[].stopsnumberRequired0

FlightChangeOptionsDto

FieldTypeRequiredDescriptionExample
orderIdstringRequiredord_5a11
optionsFlightChangeOptionDto[]Required
options[].changeIdstringRequiredPass this to the change endpoint. Nothing else identifies it.chg_9f2c…
options[].differenceCentsnumberRequiredSIGNED. Positive means you pay the difference; NEGATIVE means the new itinerary is cheaper and the difference is credited to your account. There is no separate direction field — read the sign.4200
options[].currencystringRequiredUSD
options[].newTotalCentsnumber | nullRequired46200
options[].expiresAtstring | nullRequired
options[].slicesFlightSliceDto[]Required
options[].slices[].originstringRequiredLHR
options[].slices[].destinationstringRequiredJFK
options[].slices[].departurestringRequired2026-11-15T08:00:00Z
options[].slices[].arrivalstringRequired2026-11-15T16:30:00Z
options[].slices[].durationMinutesnumberRequired510
options[].slices[].stopsnumberRequired0

FlightChangeOptionsRequestDto

FieldTypeRequiredDescriptionExample
removeSliceIdstringRequiredThe slice of the existing order you want to replace.sli_0000A
addobjectRequiredThe replacement leg: {origin, destination, departureDate}, same vocabulary as a search.{"origin":"BER","destination":"LIS","departureDate":"2026-10-04"}

FlightOfferDto

FieldTypeRequiredDescriptionExample
offerIdstringRequiredStable: the same flight carries the same id across two searches, so you can tell it is the same offer. Pass it to POST /v1/flights/orders.off_2c7d
totalCentsnumber | nullRequiredYOUR net price.42000
currencystring | nullRequiredEUR
slicesFlightSliceDto[]Required
slices[].originstringRequiredLHR
slices[].destinationstringRequiredJFK
slices[].departurestringRequired2026-11-15T08:00:00Z
slices[].arrivalstringRequired2026-11-15T16:30:00Z
slices[].durationMinutesnumberRequired510
slices[].stopsnumberRequired0
airlineobject | nullRequired{"iataCode":"BA","name":"British Airways"}
cabinClassstring | nullRequiredeconomy
conditionsobject | nullRequiredChange and refund rules, VERBATIM. A legal consequence, not a display string.{"changeBeforeDeparture":{"allowed":true,"penaltyCents":5000},"refundBeforeDeparture":{"allowed":false,"penaltyCents":null}}
baggageobject | nullRequired{"carryOn":1,"checked":0}

FlightOrderDto

FieldTypeRequiredDescriptionExample
orderIdstring | nullRequiredord_5a11
externalReferencestring | nullRequiredThe externalReference you sent when creating this booking.my-order-4711
statusstring | nullRequiredCONFIRMED
pnrstring | nullRequiredThe airline record locator.X7QK2P
totalAmountCentsnumber | nullRequired42000
currencystring | nullRequiredEUR
slicesFlightSliceDto[]Required
slices[].originstringRequiredLHR
slices[].destinationstringRequiredJFK
slices[].departurestringRequired2026-11-15T08:00:00Z
slices[].arrivalstringRequired2026-11-15T16:30:00Z
slices[].durationMinutesnumberRequired510
slices[].stopsnumberRequired0
passengersobject[]Required
conditionsobject | nullRequiredVerbatim, as in the offer.

FlightOrderPageDto

FieldTypeRequiredDescriptionExample
itemsFlightOrderSummaryDto[]Required
items[].orderIdstring | nullRequiredord_5a11
items[].statusstring | nullRequiredCONFIRMED
items[].pnrstring | nullRequiredX7QK2P
items[].totalAmountCentsnumber | nullRequired42000
items[].currencystring | nullRequiredEUR
items[].createdAtstring | nullRequired
nextCursorstring | nullRequiredPass as cursor to get the next page. null means this was the last one. The value is opaque — read it, do not build it.null

FlightOrderSummaryDto

FieldTypeRequiredDescriptionExample
orderIdstring | nullRequiredord_5a11
statusstring | nullRequiredCONFIRMED
pnrstring | nullRequiredX7QK2P
totalAmountCentsnumber | nullRequired42000
currencystring | nullRequiredEUR
createdAtstring | nullRequired

FlightSearchDto

FieldTypeRequiredDescriptionExample
offersFlightOfferDto[]Required
offers[].offerIdstringRequiredStable: the same flight carries the same id across two searches, so you can tell it is the same offer. Pass it to POST /v1/flights/orders.off_2c7d
offers[].totalCentsnumber | nullRequiredYOUR net price.42000
offers[].currencystring | nullRequiredEUR
offers[].slicesFlightSliceDto[]Required
offers[].slices[].originstringRequiredLHR
offers[].slices[].destinationstringRequiredJFK
offers[].slices[].departurestringRequired2026-11-15T08:00:00Z
offers[].slices[].arrivalstringRequired2026-11-15T16:30:00Z
offers[].slices[].durationMinutesnumberRequired510
offers[].slices[].stopsnumberRequired0
offers[].airlineobject | nullRequired{"iataCode":"BA","name":"British Airways"}
offers[].cabinClassstring | nullRequiredeconomy
offers[].conditionsobject | nullRequiredChange and refund rules, VERBATIM. A legal consequence, not a display string.{"changeBeforeDeparture":{"allowed":true,"penaltyCents":5000},"refundBeforeDeparture":{"allowed":false,"penaltyCents":null}}
offers[].baggageobject | nullRequired{"carryOn":1,"checked":0}

FlightSearchRequestDto

FieldTypeRequiredDescriptionExample
originstringRequiredIATA code of the departure airport.LHR
destinationstringRequiredJFK
departureDatestringRequiredISO date.2026-11-15
returnDatestringOptionalOmit for a one-way.2026-11-22
adultsnumberOptional1
childrennumberOptional0
infantsnumberOptionalInfants without their own seat.0
cabinClassstringOptionalOne of: economy premium_economy business first
maxConnectionsnumberOptional0 means direct flights only.1
airlinesstringOptionalComma-separated IATA airline codes.BA,LH
sortstringOptionalprice

FlightSliceDto

FieldTypeRequiredDescriptionExample
originstringRequiredLHR
destinationstringRequiredJFK
departurestringRequired2026-11-15T08:00:00Z
arrivalstringRequired2026-11-15T16:30:00Z
durationMinutesnumberRequired510
stopsnumberRequired0

FullHealthDto

FieldTypeRequiredDescriptionExample
statusstringRequiredOne of: ok degraded errorok
servicestringRequiredvacabee-partner-api
timestampstringRequired2026-09-05T04:12:33.019Z
databaseDependencyStatusDtoRequired
database.statusstringRequiredOne of: ok degraded errorok
database.responseTimenumberOptionalResponse time of the check, in milliseconds.7
redisDependencyStatusDtoRequired
redis.statusstringRequiredOne of: ok degraded errorok
redis.responseTimenumberOptionalResponse time of the check, in milliseconds.7
upstreamsmap<string, UpstreamStatusDto>RequiredState per downstream service, keyed by the service name.{"profile":{"status":"ok","service":"vacabee-profile","responseTime":21}}

GuestDto

FieldTypeRequiredDescriptionExample
firstNamestringRequiredAda
lastNamestringRequiredLovelace
agenumberOptionalA child's age. Suppliers price children by age, so leave it off for adults.8

HotelBookingDto

FieldTypeRequiredDescriptionExample
bookingIdstring | nullRequiredbkg_31f0
externalReferencestring | nullRequiredThe externalReference you sent when creating this booking.my-order-4711
statusstring | nullRequiredCONFIRMED
hotelNamestring | nullRequiredHotel Lisboa Centro
checkInstring | nullRequired2026-11-02
checkOutstring | nullRequired2026-11-06
totalAmountCentsnumber | nullRequired12400
currencystring | nullRequiredEUR
confirmationNumberstring | nullRequiredconf_88ab
cancellationPolicystring | nullRequiredVerbatim, as in the rate.
guestsobject[]Required

HotelBookingPageDto

FieldTypeRequiredDescriptionExample
itemsHotelBookingDto[]Required
items[].bookingIdstring | nullRequiredbkg_31f0
items[].externalReferencestring | nullRequiredThe externalReference you sent when creating this booking.my-order-4711
items[].statusstring | nullRequiredCONFIRMED
items[].hotelNamestring | nullRequiredHotel Lisboa Centro
items[].checkInstring | nullRequired2026-11-02
items[].checkOutstring | nullRequired2026-11-06
items[].totalAmountCentsnumber | nullRequired12400
items[].currencystring | nullRequiredEUR
items[].confirmationNumberstring | nullRequiredconf_88ab
items[].cancellationPolicystring | nullRequiredVerbatim, as in the rate.
items[].guestsobject[]Required
nextCursorstring | nullRequirednull

HotelCancellationPolicyDto

FieldTypeRequiredDescriptionExample
fromstring | nullRequiredFrom when this window applies.2026-11-01T18:00:00
untilstring | nullRequiredUntil when.2026-11-02T14:00:00
chargeCentsnumber | nullRequiredWhat cancelling in this window costs, in minor units — at YOUR net basis.11340

HotelDestinationDto

FieldTypeRequiredDescriptionExample
idstringRequireddst_lisbon
namestringRequiredLisbon
typestringRequiredOne of: city region poi hotelcity
countrystringRequiredISO 3166-1 alpha-2.PT

HotelHitDto

FieldTypeRequiredDescriptionExample
hotelIdstring | nullRequiredhtl_4b21
namestring | nullRequiredAltis Avenida Hotel
starRatingnumber | nullRequiredStar category, 0–5. Never a review score.5
addressstring | nullRequiredOne line, as the supplier states it. We do not split it into city and country — that would be guessing, and a guessed address is worse than an undivided one.Rua 1º De Dezembro, 120, Lisbon
latitudenumber | nullRequired38.72
longitudenumber | nullRequired-9.14
imagesstring[]Required
amenitiesstring[]Required["Free Wi-Fi","Parking"]
priceFromobject | nullRequiredAn indicative "from" price. NOT bookable — call GET /v1/hotels/{hotelId}/rates for rates you can book.
priceFrom.totalCentsnumber | nullRequiredFor the whole stay.49600
priceFrom.perNightCentsnumber | nullRequired12400
priceFrom.currencystring | nullRequiredEUR
rateStatusstring | nullRequiredavailable — there are bookable rates, go fetch them. checking — the supplier's price query is still running. none — nothing to be had for these dates.One of: available checking noneavailable
reviewScorenumber | nullRequiredGuest review score, 0–10. Absent means no reviews — never substituted.8.4
reviewCountnumber | nullRequiredHow many reviews the score is made of. A score without it is not a verdict.214

HotelPriceFromDto

FieldTypeRequiredDescriptionExample
totalCentsnumber | nullRequiredFor the whole stay.49600
perNightCentsnumber | nullRequired12400
currencystring | nullRequiredEUR

HotelRateDetailDto

FieldTypeRequiredDescriptionExample
rateIdstringRequiredPass to POST /v1/hotels/bookings. Expires — a stale rate answers 410 offer_expired rather than booking at a price that no longer holds.rate_9f2c1a
roomNamestring | nullRequiredDouble Room
boardTypestring | nullRequiredbreakfast
totalCentsnumber | nullRequiredYOUR net price for the whole stay.12400
currencystring | nullRequiredEUR
refundableboolean | nullRequirednull means we do not know — not "no". "Non-refundable" is a statement with money behind it, and guessing it is the wrong way to be helpful.true
freeCancellationUntilstring | nullRequiredFree cancellation up to this moment, when the supplier states one. Local time at the property, as they state it.2026-11-01T18:00:00
cancellationPoliciesHotelCancellationPolicyDto[]RequiredEvery window the supplier states, in full. We do NOT condense them into one sentence: money hangs on them, and a summary would be our reading rather than the supplier's.
cancellationPolicies[].fromstring | nullRequiredFrom when this window applies.2026-11-01T18:00:00
cancellationPolicies[].untilstring | nullRequiredUntil when.2026-11-02T14:00:00
cancellationPolicies[].chargeCentsnumber | nullRequiredWhat cancelling in this window costs, in minor units — at YOUR net basis.11340

HotelRatesDto

FieldTypeRequiredDescriptionExample
hotelIdstringRequiredhtl_4b21
ratesHotelRateDetailDto[]Required
rates[].rateIdstringRequiredPass to POST /v1/hotels/bookings. Expires — a stale rate answers 410 offer_expired rather than booking at a price that no longer holds.rate_9f2c1a
rates[].roomNamestring | nullRequiredDouble Room
rates[].boardTypestring | nullRequiredbreakfast
rates[].totalCentsnumber | nullRequiredYOUR net price for the whole stay.12400
rates[].currencystring | nullRequiredEUR
rates[].refundableboolean | nullRequirednull means we do not know — not "no". "Non-refundable" is a statement with money behind it, and guessing it is the wrong way to be helpful.true
rates[].freeCancellationUntilstring | nullRequiredFree cancellation up to this moment, when the supplier states one. Local time at the property, as they state it.2026-11-01T18:00:00
rates[].cancellationPoliciesHotelCancellationPolicyDto[]RequiredEvery window the supplier states, in full. We do NOT condense them into one sentence: money hangs on them, and a summary would be our reading rather than the supplier's.
rates[].cancellationPolicies[].fromstring | nullRequiredFrom when this window applies.2026-11-01T18:00:00
rates[].cancellationPolicies[].untilstring | nullRequiredUntil when.2026-11-02T14:00:00
rates[].cancellationPolicies[].chargeCentsnumber | nullRequiredWhat cancelling in this window costs, in minor units — at YOUR net basis.11340
priceNoticeobject | nullRequiredPresent ONLY when the price here differs materially from the one the search advertised. Show both numbers rather than quietly using the higher one.

HotelSearchDto

FieldTypeRequiredDescriptionExample
searchIdstring | nullRequiredPass to GET/PATCH /v1/hotels/searches/{id} to refine or page.ses_7a1f
statusstringRequiredcomplete means every supplier answered. A partial result still carries hotels — show them rather than waiting.complete
hotelsHotelHitDto[]Required
hotels[].hotelIdstring | nullRequiredhtl_4b21
hotels[].namestring | nullRequiredAltis Avenida Hotel
hotels[].starRatingnumber | nullRequiredStar category, 0–5. Never a review score.5
hotels[].addressstring | nullRequiredOne line, as the supplier states it. We do not split it into city and country — that would be guessing, and a guessed address is worse than an undivided one.Rua 1º De Dezembro, 120, Lisbon
hotels[].latitudenumber | nullRequired38.72
hotels[].longitudenumber | nullRequired-9.14
hotels[].imagesstring[]Required
hotels[].amenitiesstring[]Required["Free Wi-Fi","Parking"]
hotels[].priceFromobject | nullRequiredAn indicative "from" price. NOT bookable — call GET /v1/hotels/{hotelId}/rates for rates you can book.
hotels[].priceFrom.totalCentsnumber | nullRequiredFor the whole stay.49600
hotels[].priceFrom.perNightCentsnumber | nullRequired12400
hotels[].priceFrom.currencystring | nullRequiredEUR
hotels[].rateStatusstring | nullRequiredavailable — there are bookable rates, go fetch them. checking — the supplier's price query is still running. none — nothing to be had for these dates.One of: available checking noneavailable
hotels[].reviewScorenumber | nullRequiredGuest review score, 0–10. Absent means no reviews — never substituted.8.4
hotels[].reviewCountnumber | nullRequiredHow many reviews the score is made of. A score without it is not a verdict.214

HotelSearchPatchDto

FieldTypeRequiredDescriptionExample
destinationstringOptionalWhere to search. Either this, nearPoi, or a latitude/longitude pair — without one of the three there is nothing to anchor the search on.Lisbon
nearPoistringOptionalA landmark to search around. Useful for small places a city name would miss.Torre de Belém
areaHintstringOptionalA district within the destination.Alfama
latitudenumberOptional38.7223
longitudenumberOptional-9.1393
radiusKmnumberOptionalRadius around the coordinates, in kilometres.5
checkInstringOptionalISO date. Without dates you get availability without prices.2026-11-02
checkOutstringOptional2026-11-06
adultsnumberOptional2
childrennumberOptional0
childrenAgesstring[]OptionalOne age per child. Required by most suppliers to price a room correctly.[7,11]
roomsnumberOptional1
boardstringOptionalOne of: any room_only breakfast half_board full_board all_inclusive
budgetMaxPerNightnumberOptionalUpper bound per night, in minor units.20000
currencystringOptionalEUR
starMinnumberOptionalMinimum star rating.4
amenitiesstring[]Optional["wifi","parking"]
sortstringOptionalprice_asc
languagestringOptionalen
limitnumberOptionalAt most 100.20

HotelSearchRequestDto

FieldTypeRequiredDescriptionExample
destinationstringOptionalWhere to search. Either this, nearPoi, or a latitude/longitude pair — without one of the three there is nothing to anchor the search on.Lisbon
nearPoistringOptionalA landmark to search around. Useful for small places a city name would miss.Torre de Belém
areaHintstringOptionalA district within the destination.Alfama
latitudenumberOptional38.7223
longitudenumberOptional-9.1393
radiusKmnumberOptionalRadius around the coordinates, in kilometres.5
checkInstringOptionalISO date. Without dates you get availability without prices.2026-11-02
checkOutstringOptional2026-11-06
adultsnumberOptional2
childrennumberOptional0
childrenAgesstring[]OptionalOne age per child. Required by most suppliers to price a room correctly.[7,11]
roomsnumberOptional1
boardstringOptionalOne of: any room_only breakfast half_board full_board all_inclusive
budgetMaxPerNightnumberOptionalUpper bound per night, in minor units.20000
currencystringOptionalEUR
starMinnumberOptionalMinimum star rating.4
amenitiesstring[]Optional["wifi","parking"]
sortstringOptionalprice_asc
languagestringOptionalen
limitnumberOptionalAt most 100.20

LedgerBalanceDto

FieldTypeRequiredDescriptionExample
accountIdstringRequiredacc_4f21
accountRefstringRequiredQuote this in a bank transfer so we can match the payment to your account.VCB-ACC-9K2M
currencystringRequiredUSD
balanceCentsnumberRequiredThe sum of every entry on your account. Holds are stored as negative entries, so money already reserved for a booking in flight is ALREADY deducted here.500000
creditLimitCentsnumberRequiredAgreed overdraft, if you have one.0
heldCentsnumberRequiredHow much of the balance is reserved for bookings that have started but not finished. Shown so you can explain the number — NOT to be subtracted again: it is already out of balanceCents.12400
availableCentsnumberRequiredbalance + creditLimit. THE number to check before booking. Holds are not subtracted a second time — they are already inside balanceCents.500000
statusstringRequiredAnything but ACTIVE and bookings answer 402 regardless of the balance.One of: ACTIVE SUSPENDED CLOSEDACTIVE

LedgerEntryDto

FieldTypeRequiredDescriptionExample
idstringRequiredled_8a31
kindstringRequiredOne of: DEPOSIT HOLD RELEASE CHARGE REFUND ADJUSTMENTCHARGE
amountCentsnumberRequiredSigned, in minor units. Negative takes money out. The balance is their sum.-12400
currencystringRequiredUSD
sourceReferencestringRequiredYour reference for the thing that caused this. Use it to reconcile.hotel:e2e-1788707669
descriptionstring | nullRequiredHotel htl_4b21 2026-11-02
createdAtstringRequired2026-09-06T14:31:09.272Z

LedgerEntryPageDto

FieldTypeRequiredDescriptionExample
itemsLedgerEntryDto[]Required
items[].idstringRequiredled_8a31
items[].kindstringRequiredOne of: DEPOSIT HOLD RELEASE CHARGE REFUND ADJUSTMENTCHARGE
items[].amountCentsnumberRequiredSigned, in minor units. Negative takes money out. The balance is their sum.-12400
items[].currencystringRequiredUSD
items[].sourceReferencestringRequiredYour reference for the thing that caused this. Use it to reconcile.hotel:e2e-1788707669
items[].descriptionstring | nullRequiredHotel htl_4b21 2026-11-02
items[].createdAtstringRequired2026-09-06T14:31:09.272Z
nextCursorstring | nullRequirednull

LoadMoreDto

FieldTypeRequiredDescriptionExample
limitnumberOptionalHow many more results to add.20

PatchWebhookEndpointDto

FieldTypeRequiredDescriptionExample
urlstringOptionalhttps://api.example.com/hooks/v2
eventsstring[]OptionalOne of: booking.confirmed booking.failed booking.cancelled booking.modified checkout.created checkout.expired payment.succeeded payment.failed payment.refunded esim.provisioned esim.usage_threshold ledger.low_balance search_quota.threshold settlement.invoice_issued settlement.payment_failed apikey.revoked
descriptionstringOptional
statusstringOptionalDISABLED stops delivery without deleting the endpoint and its history — for a planned maintenance window.One of: ACTIVE DISABLED

PingDto

FieldTypeRequiredDescriptionExample
statusstringRequiredAlways ok if the response arrives.ok
servicestringRequiredWhich service answered.vacabee-partner-api
timestampstringRequiredServer time in UTC, per ISO 8601. Useful for spotting a clock drift on your own side — that is a common cause of rejected webhook signatures.2026-09-05T04:12:33.019Z

RotateWebhookSecretDto

FieldTypeRequiredDescriptionExample
immediatebooleanOptionalKill the previous secret NOW instead of keeping it valid for 24 hours. Only for a leak: without the overlap, every delivery between this call and your deployment fails your signature check. Rotating twice within the overlap is refused unless you set this — the second rotation would silently invalidate a secret we promised would stay valid.false

SearchQuotaDto

FieldTypeRequiredDescriptionExample
daystringRequiredThe calendar day this counter belongs to, in YOUR time zone. The allowance resets by your day, not by UTC.2026-09-10
timezonestringRequiredEurope/Berlin
limitnumberRequiredYour free searches per day, from your commercial terms.1000
usednumberRequiredBillable searches counted today — INCLUDING any above the allowance. Only calls that reach a supplier count; polling, reads and everything in the sandbox do not.312
remainingnumberRequiredHow much of the free allowance is left. Never negative: past the allowance it is 0 and used keeps climbing.688
resetstringRequiredWhen the counter goes back to zero.2026-09-11T00:00:00.000Z
hardCapnumber | nullRequiredYour own ceiling, if you set one. There is no ceiling from our side — past the free allowance you keep searching and pay. A cap is only ever yours, which is why a search we turned away is counted separately.null
rejectednumberRequiredSearches YOUR hard cap turned away today. Zero unless you set a cap.0

SearchTransfersDto

FieldTypeRequiredDescriptionExample
fromTransferPlaceDtoRequired
from.typestringRequiredHow code should be read. IATA is an airport code, ATLAS an accommodation identifier, GPS a lat,lng pair.One of: IATA ATLAS GPS
from.codestringRequiredThe identifier itself.LIS
from.descriptionstringOptionalWhat is at code. Required when type is GPS — coordinates alone do not tell the driver where they are going — and ignored otherwise.Hotel Riu Palace, Palma
toTransferPlaceDtoRequired
to.typestringRequiredHow code should be read. IATA is an airport code, ATLAS an accommodation identifier, GPS a lat,lng pair.One of: IATA ATLAS GPS
to.codestringRequiredThe identifier itself.LIS
to.descriptionstringOptionalWhat is at code. Required when type is GPS — coordinates alone do not tell the driver where they are going — and ignored otherwise.Hotel Riu Palace, Palma
outboundstringRequiredLocal pickup time at the origin, without a timezone suffix. A transfer is booked in the time the driver reads off the clock.2026-11-02T08:30:00
inboundstringOptionalReturn leg. Present = round trip, absent = one way.
adultsnumberRequired
childrennumberOptional0
infantsnumberOptional0
languagestringOptionalISO-639-1 language.en

TerseHealthDto

FieldTypeRequiredDescriptionExample
statusstringRequiredOne of: ok degraded errorok
servicestringRequiredvacabee-partner-api
timestampstringRequired2026-09-05T04:12:33.019Z

TransferCancellationQuoteDto

FieldTypeRequiredDescriptionExample
penaltyCentsnumberRequiredStays with us.1050
refundCentsnumberRequiredCredited back to your account.3150
currencystringRequiredUSD
freeCancellationbooleanRequiredfalse

TransferHolderDto

FieldTypeRequiredDescriptionExample
firstNamestringRequired
lastNamestringRequired
emailstringRequired
phonestringRequiredE.164. The driver calls this number when the pickup goes wrong.+351912345678

TransferOfferDto

FieldTypeRequiredDescriptionExample
quoteIdstringRequiredBook with this. Valid ~20 minutes, then 410 offer_expired.qte_9dJ2…
directionstring | nullRequiredARRIVAL
transferTypestring | nullRequiredPRIVATE
vehiclestring | nullRequiredCar
categorystring | nullRequiredStandard
priceCentsnumber | nullRequiredYour PURCHASE price.4200
currencystringRequiredUSD
minPaxnumber | nullRequired1
maxPaxnumber | nullRequired3
luggagestring | nullRequired2
journeyMinutesstring | nullRequired35
pickupobject | nullRequiredMeeting point and whether the pickup time must be reconfirmed.
fromobject | nullRequired
toobject | nullRequired
cancellationPoliciesobject[]RequiredVERBATIM from the supplier. Not summarised into a boolean — a cancellation policy is a legal consequence, and "free until 18:00 the day before" does not survive being turned into refundable: true.

TransferOrderDto

FieldTypeRequiredDescriptionExample
idstring | nullRequired
externalReferencestring | nullRequiredThe externalReference you sent when creating this booking.my-order-4711
statusstring | nullRequiredCONFIRMED
referencestring | nullRequiredThe supplier's reference.
amountCentsnumber | nullRequired4200
currencystringRequiredUSD
voucherUrlstring | nullRequiredPrintable voucher — show it to the traveller.
holderobjectRequiredThe lead passenger.
legsobject[]Required
cancelledAtstring | nullRequired
refundCentsnumber | nullRequiredCredited back on cancellation.
settlementTransferSettlementDtoRequired
settlement.fundsstringRequiredWhat happened to the money on your account. captured and released are final. still_held means the supplier gave no verdict yet and our reconciliation worker is finishing the job — do NOT invoice your customer on that state; poll the order until it settles.One of: captured released still_held

TransferOrderPageDto

FieldTypeRequiredDescriptionExample
itemsTransferOrderDto[]Required
items[].idstring | nullRequired
items[].externalReferencestring | nullRequiredThe externalReference you sent when creating this booking.my-order-4711
items[].statusstring | nullRequiredCONFIRMED
items[].referencestring | nullRequiredThe supplier's reference.
items[].amountCentsnumber | nullRequired4200
items[].currencystringRequiredUSD
items[].voucherUrlstring | nullRequiredPrintable voucher — show it to the traveller.
items[].holderobjectRequiredThe lead passenger.
items[].legsobject[]Required
items[].cancelledAtstring | nullRequired
items[].refundCentsnumber | nullRequiredCredited back on cancellation.
items[].settlementTransferSettlementDtoRequired
items[].settlement.fundsstringRequiredWhat happened to the money on your account. captured and released are final. still_held means the supplier gave no verdict yet and our reconciliation worker is finishing the job — do NOT invoice your customer on that state; poll the order until it settles.One of: captured released still_held
nextCursorstring | nullRequiredPass as cursor to get the next page. null means this was the last one. The value is opaque — read it, do not build it.null

TransferPlaceDto

FieldTypeRequiredDescriptionExample
typestringRequiredHow code should be read. IATA is an airport code, ATLAS an accommodation identifier, GPS a lat,lng pair.One of: IATA ATLAS GPS
codestringRequiredThe identifier itself.LIS
descriptionstringOptionalWhat is at code. Required when type is GPS — coordinates alone do not tell the driver where they are going — and ignored otherwise.Hotel Riu Palace, Palma

TransferSearchResultDto

FieldTypeRequiredDescriptionExample
offersTransferOfferDto[]Required
offers[].quoteIdstringRequiredBook with this. Valid ~20 minutes, then 410 offer_expired.qte_9dJ2…
offers[].directionstring | nullRequiredARRIVAL
offers[].transferTypestring | nullRequiredPRIVATE
offers[].vehiclestring | nullRequiredCar
offers[].categorystring | nullRequiredStandard
offers[].priceCentsnumber | nullRequiredYour PURCHASE price.4200
offers[].currencystringRequiredUSD
offers[].minPaxnumber | nullRequired1
offers[].maxPaxnumber | nullRequired3
offers[].luggagestring | nullRequired2
offers[].journeyMinutesstring | nullRequired35
offers[].pickupobject | nullRequiredMeeting point and whether the pickup time must be reconfirmed.
offers[].fromobject | nullRequired
offers[].toobject | nullRequired
offers[].cancellationPoliciesobject[]RequiredVERBATIM from the supplier. Not summarised into a boolean — a cancellation policy is a legal consequence, and "free until 18:00 the day before" does not survive being turned into refundable: true.

TransferSettlementDto

FieldTypeRequiredDescription
fundsstringRequiredWhat happened to the money on your account. captured and released are final. still_held means the supplier gave no verdict yet and our reconciliation worker is finishing the job — do NOT invoice your customer on that state; poll the order until it settles.One of: captured released still_held

TravellerDto

FieldTypeRequiredDescriptionExample
firstNamestringRequiredAda
lastNamestringRequiredLovelace
dateOfBirthstringOptionalISO date. Most airlines require it; without it the order can be rejected.1990-05-17
genderstringOptionalAs the airline records it. Pass through what your customer stated.f
titlestringOptionalSalutation as the airline records it — mr, ms, mrs, miss, dr. Optional here and required by most carriers: leaving it out answers "Each passenger must include 'title'" from the airline.mr

UpstreamStatusDto

FieldTypeRequiredDescriptionExample
statusstringRequiredOne of: ok degraded errorok
servicestringRequiredvacabee-profile
responseTimenumberOptional21

WebhookAttemptDto

FieldTypeRequiredDescriptionExample
idstringRequired
attemptNumbernumberRequiredWhich attempt this is, counted from 1.3
startedAtstringRequired2026-09-05T09:12:44.000Z
durationMsnumberRequired842
requestHeadersmap<string, string> | nullRequiredThe headers of THIS attempt, signature included. It differs in every attempt because it is computed over a fresh timestamp — with the old one the retry would fail your replay window.
statusCodenumber | nullRequiredNull means: no response arrived at all (timeout, DNS, connection).500
responseBodystring | nullRequiredTruncated to 4096 characters — somebody else's response is unbounded in length.
responseHeadersmap<string, string> | nullRequired
errorstring | nullRequired
manualbooleanRequiredWas this attempt triggered by hand?
triggeredBystring | nullRequired

WebhookBookingDataDto

FieldTypeRequiredDescriptionExample
bookingTypestringRequiredOne of: hotel flight transfer esim
bookingReferencestringRequiredABC123
statusstringOptionalCONFIRMED
currencystringOptionalEUR
totalAmountCentsnumberOptionalWhat the guest paid, in the smallest unit — not our purchase price.48900
checkInDatestringOptional2026-11-02
checkOutDatestringOptional2026-11-06
departureDatestringOptional2026-11-02T08:15:00Z
returnDatestringOptional
hotelNamestringOptionalHotel Adlon
originstringOptionalBER
destinationstringOptionalLIS
passengerCountnumberOptional2
guestCountnumberOptional2
refundedbooleanOptionalOnly on booking.cancelled: whether money goes back. A cancellation without a refund (non-refundable rate) leaves the margin standing.
reasonstringOptionalOnly on booking.failed: a machine-readable cause, so you can branch without parsing prose. checkout_expired means nobody paid the hosted checkout in time — nothing was reserved and nothing has to be undone; quote again if the traveller still wants it. Treat an unknown value the way you treat an unknown event type: as "it failed", not as an error of your own.One of: checkout_expired checkout_failedcheckout_expired

WebhookCheckoutDataDto

FieldTypeRequiredDescriptionExample
bookingTypestringRequiredWhich vertical the checkout belongs to.One of: hotel flight transfer esim
bookingReferencestringRequiredYOUR externalReference for this booking — the same string you sent, so this needs no lookup table on your side.ORD-8817
amountCentsnumberRequiredWhat the traveller is asked to pay, in the smallest unit.48900
currencystringRequiredEUR
expiresAtstringRequiredOn checkout.created: when the link stops being payable. On checkout.expired: when it did.2026-09-05T10:15:00.000Z

WebhookDeliveryDetailDto

FieldTypeRequiredDescriptionExample
idstringRequireddl_01J8…
eventTypestringRequiredOne of: booking.confirmed booking.failed booking.cancelled booking.modified checkout.created checkout.expired payment.succeeded payment.failed payment.refunded esim.provisioned esim.usage_threshold ledger.low_balance search_quota.threshold settlement.invoice_issued settlement.payment_failed apikey.revoked webhook.test
eventIdstringRequiredThe business ID of what triggered this — your booking, your refund.bk_01J8…
statusstringRequiredPENDING is waiting for the next automatic attempt. EXHAUSTED means: the automatic attempts are used up — nothing more happens on its own, but it can still be retried by hand. FAILED means: your endpoint answered 410 Gone, and we stopped instead of calling again.One of: PENDING SUCCEEDED FAILED EXHAUSTED
attemptCountnumberRequired3
nextAttemptAtstring | nullRequired
lastStatusCodenumber | nullRequired503
lastErrorstring | nullRequired
lastAttemptAtstring | nullRequired
succeededAtstring | nullRequired
createdAtstringRequired
endpointobjectRequired
endpoint.idstringOptional
endpoint.urlstring | nullOptional
endpoint.descriptionstring | nullOptional
retryablebooleanRequiredSending a successful delivery again would create a second event on your side — which is why it is not retryable.
payloadobjectRequiredByte for byte what we sent. A retry sends exactly that again, not a freshly serialized version of it.
attemptsWebhookAttemptDto[]Required
attempts[].idstringRequired
attempts[].attemptNumbernumberRequiredWhich attempt this is, counted from 1.3
attempts[].startedAtstringRequired2026-09-05T09:12:44.000Z
attempts[].durationMsnumberRequired842
attempts[].requestHeadersmap<string, string> | nullRequiredThe headers of THIS attempt, signature included. It differs in every attempt because it is computed over a fresh timestamp — with the old one the retry would fail your replay window.
attempts[].statusCodenumber | nullRequiredNull means: no response arrived at all (timeout, DNS, connection).500
attempts[].responseBodystring | nullRequiredTruncated to 4096 characters — somebody else's response is unbounded in length.
attempts[].responseHeadersmap<string, string> | nullRequired
attempts[].errorstring | nullRequired
attempts[].manualbooleanRequiredWas this attempt triggered by hand?
attempts[].triggeredBystring | nullRequired

WebhookDeliveryDto

FieldTypeRequiredDescriptionExample
idstringRequireddl_01J8…
eventTypestringRequiredOne of: booking.confirmed booking.failed booking.cancelled booking.modified checkout.created checkout.expired payment.succeeded payment.failed payment.refunded esim.provisioned esim.usage_threshold ledger.low_balance search_quota.threshold settlement.invoice_issued settlement.payment_failed apikey.revoked webhook.test
eventIdstringRequiredThe business ID of what triggered this — your booking, your refund.bk_01J8…
statusstringRequiredPENDING is waiting for the next automatic attempt. EXHAUSTED means: the automatic attempts are used up — nothing more happens on its own, but it can still be retried by hand. FAILED means: your endpoint answered 410 Gone, and we stopped instead of calling again.One of: PENDING SUCCEEDED FAILED EXHAUSTED
attemptCountnumberRequired3
nextAttemptAtstring | nullRequired
lastStatusCodenumber | nullRequired503
lastErrorstring | nullRequired
lastAttemptAtstring | nullRequired
succeededAtstring | nullRequired
createdAtstringRequired
endpointobjectRequired
endpoint.idstringOptional
endpoint.urlstring | nullOptional
endpoint.descriptionstring | nullOptional
retryablebooleanRequiredSending a successful delivery again would create a second event on your side — which is why it is not retryable.

WebhookDeliveryHeadersDto

FieldTypeRequiredDescriptionExample
X-Vacabee-SignaturestringRequiredHMAC-SHA256 over {t}.{raw body}, hex. Compare in constant time and check the age (default 300 s). Verify over the RAW body — parsing and re-serialising yields a different string and therefore a different signature. Recomputed on every attempt, because otherwise every retry would fail your time window.t=1788570420,v1=8f2c…
X-Vacabee-EventstringRequiredbooking.confirmed
X-Vacabee-DeliverystringRequiredThe delivery. The same across all attempts.dl_01J8…
X-Vacabee-AttemptstringRequiredWhich attempt this is, from 1.1
X-Vacabee-TimestampstringRequired1788570420

WebhookDeliveryListDto

FieldTypeRequiredDescriptionExample
deliveriesWebhookDeliveryDto[]Required
deliveries[].idstringRequireddl_01J8…
deliveries[].eventTypestringRequiredOne of: booking.confirmed booking.failed booking.cancelled booking.modified checkout.created checkout.expired payment.succeeded payment.failed payment.refunded esim.provisioned esim.usage_threshold ledger.low_balance search_quota.threshold settlement.invoice_issued settlement.payment_failed apikey.revoked webhook.test
deliveries[].eventIdstringRequiredThe business ID of what triggered this — your booking, your refund.bk_01J8…
deliveries[].statusstringRequiredPENDING is waiting for the next automatic attempt. EXHAUSTED means: the automatic attempts are used up — nothing more happens on its own, but it can still be retried by hand. FAILED means: your endpoint answered 410 Gone, and we stopped instead of calling again.One of: PENDING SUCCEEDED FAILED EXHAUSTED
deliveries[].attemptCountnumberRequired3
deliveries[].nextAttemptAtstring | nullRequired
deliveries[].lastStatusCodenumber | nullRequired503
deliveries[].lastErrorstring | nullRequired
deliveries[].lastAttemptAtstring | nullRequired
deliveries[].succeededAtstring | nullRequired
deliveries[].createdAtstringRequired
deliveries[].endpointobjectRequired
deliveries[].endpoint.idstringOptional
deliveries[].endpoint.urlstring | nullOptional
deliveries[].endpoint.descriptionstring | nullOptional
deliveries[].retryablebooleanRequiredSending a successful delivery again would create a second event on your side — which is why it is not retryable.
nextCursorstring | nullRequiredPass on as cursor to fetch the next page. Null = end.
deliveryEnabledbooleanRequiredFalse means: nothing is going out on OUR side right now. Events are still being recorded and will go out as soon as it is fixed. This is not a configuration error on your side.

WebhookEndpointDto

FieldTypeRequiredDescriptionExample
idstringRequiredep_01J8…
urlstringRequiredhttps://api.example.com/hooks/vacabee
eventsstring[]RequiredOne of: booking.confirmed booking.failed booking.cancelled booking.modified checkout.created checkout.expired payment.succeeded payment.failed payment.refunded esim.provisioned esim.usage_threshold ledger.low_balance search_quota.threshold settlement.invoice_issued settlement.payment_failed apikey.revoked
secretPrefixstringRequiredOnly the beginning of the signing secret, so that you can tell two endpoints apart. The full value appears exactly once: on creation and on rotation.whsec_LL2z9E
statusstringRequiredOne of: ACTIVE DISABLED
descriptionstring | nullRequired
lastDeliveryAtstring | nullRequired2026-09-05T09:12:44.000Z
createdAtstringRequired2026-08-30T11:02:00.000Z
canDeliverbooleanRequiredFalse for endpoints predating delivery: for those we only hold a hash of the secret, and you cannot sign with a hash. Such endpoints receive nothing until the secret has been rotated once.

WebhookEsimDataDto

FieldTypeRequiredDescriptionExample
purchaseIdstringRequiredpur_01J8…
iccidstring | nullOptional8931…
planNamestring | nullOptionalEurope 10 GB / 30 days
statusstringOptionalPROVISIONED

WebhookEventDto

FieldTypeRequiredDescriptionExample
idstringRequiredStable across ALL retries of the same delivery. The key for deduplication: delivery is at-least-once, not exactly-once.evt_bk_01J8…
typestringRequiredA receiver should acknowledge an unknown type with 2xx rather than fail — otherwise a new event can knock over an existing receiver.One of: booking.confirmed booking.failed booking.cancelled booking.modified checkout.created checkout.expired payment.succeeded payment.failed payment.refunded esim.provisioned esim.usage_threshold ledger.low_balance search_quota.threshold settlement.invoice_issued settlement.payment_failed apikey.revokedbooking.confirmed
createdAtstringRequired2026-09-05T10:00:00.000Z
dataWebhookBookingDataDto | WebhookEsimDataDto | WebhookCheckoutDataDtoRequiredThe payload. Shape depends on type.

WebhookRetryResultDto

FieldTypeRequiredDescriptionExample
deliveredbooleanRequiredDid your endpoint answer with a 2xx this time?
deliveryWebhookDeliveryDetailDtoRequired
delivery.idstringRequireddl_01J8…
delivery.eventTypestringRequiredOne of: booking.confirmed booking.failed booking.cancelled booking.modified checkout.created checkout.expired payment.succeeded payment.failed payment.refunded esim.provisioned esim.usage_threshold ledger.low_balance search_quota.threshold settlement.invoice_issued settlement.payment_failed apikey.revoked webhook.test
delivery.eventIdstringRequiredThe business ID of what triggered this — your booking, your refund.bk_01J8…
delivery.statusstringRequiredPENDING is waiting for the next automatic attempt. EXHAUSTED means: the automatic attempts are used up — nothing more happens on its own, but it can still be retried by hand. FAILED means: your endpoint answered 410 Gone, and we stopped instead of calling again.One of: PENDING SUCCEEDED FAILED EXHAUSTED
delivery.attemptCountnumberRequired3
delivery.nextAttemptAtstring | nullRequired
delivery.lastStatusCodenumber | nullRequired503
delivery.lastErrorstring | nullRequired
delivery.lastAttemptAtstring | nullRequired
delivery.succeededAtstring | nullRequired
delivery.createdAtstringRequired
delivery.endpointobjectRequired
delivery.endpoint.idstringOptional
delivery.endpoint.urlstring | nullOptional
delivery.endpoint.descriptionstring | nullOptional
delivery.retryablebooleanRequiredSending a successful delivery again would create a second event on your side — which is why it is not retryable.
delivery.payloadobjectRequiredByte for byte what we sent. A retry sends exactly that again, not a freshly serialized version of it.
delivery.attemptsWebhookAttemptDto[]Required
delivery.attempts[].idstringRequired
delivery.attempts[].attemptNumbernumberRequiredWhich attempt this is, counted from 1.3
delivery.attempts[].startedAtstringRequired2026-09-05T09:12:44.000Z
delivery.attempts[].durationMsnumberRequired842
delivery.attempts[].requestHeadersmap<string, string> | nullRequiredThe headers of THIS attempt, signature included. It differs in every attempt because it is computed over a fresh timestamp — with the old one the retry would fail your replay window.
delivery.attempts[].statusCodenumber | nullRequiredNull means: no response arrived at all (timeout, DNS, connection).500
delivery.attempts[].responseBodystring | nullRequiredTruncated to 4096 characters — somebody else's response is unbounded in length.
delivery.attempts[].responseHeadersmap<string, string> | nullRequired
delivery.attempts[].errorstring | nullRequired
delivery.attempts[].manualbooleanRequiredWas this attempt triggered by hand?
delivery.attempts[].triggeredBystring | nullRequired

WebhookTestResultDto

FieldTypeRequiredDescriptionExample
deliveredbooleanRequired
deliveryWebhookDeliveryDetailDtoOptional
delivery.idstringRequireddl_01J8…
delivery.eventTypestringRequiredOne of: booking.confirmed booking.failed booking.cancelled booking.modified checkout.created checkout.expired payment.succeeded payment.failed payment.refunded esim.provisioned esim.usage_threshold ledger.low_balance search_quota.threshold settlement.invoice_issued settlement.payment_failed apikey.revoked webhook.test
delivery.eventIdstringRequiredThe business ID of what triggered this — your booking, your refund.bk_01J8…
delivery.statusstringRequiredPENDING is waiting for the next automatic attempt. EXHAUSTED means: the automatic attempts are used up — nothing more happens on its own, but it can still be retried by hand. FAILED means: your endpoint answered 410 Gone, and we stopped instead of calling again.One of: PENDING SUCCEEDED FAILED EXHAUSTED
delivery.attemptCountnumberRequired3
delivery.nextAttemptAtstring | nullRequired
delivery.lastStatusCodenumber | nullRequired503
delivery.lastErrorstring | nullRequired
delivery.lastAttemptAtstring | nullRequired
delivery.succeededAtstring | nullRequired
delivery.createdAtstringRequired
delivery.endpointobjectRequired
delivery.endpoint.idstringOptional
delivery.endpoint.urlstring | nullOptional
delivery.endpoint.descriptionstring | nullOptional
delivery.retryablebooleanRequiredSending a successful delivery again would create a second event on your side — which is why it is not retryable.
delivery.payloadobjectRequiredByte for byte what we sent. A retry sends exactly that again, not a freshly serialized version of it.
delivery.attemptsWebhookAttemptDto[]Required
delivery.attempts[].idstringRequired
delivery.attempts[].attemptNumbernumberRequiredWhich attempt this is, counted from 1.3
delivery.attempts[].startedAtstringRequired2026-09-05T09:12:44.000Z
delivery.attempts[].durationMsnumberRequired842
delivery.attempts[].requestHeadersmap<string, string> | nullRequiredThe headers of THIS attempt, signature included. It differs in every attempt because it is computed over a fresh timestamp — with the old one the retry would fail your replay window.
delivery.attempts[].statusCodenumber | nullRequiredNull means: no response arrived at all (timeout, DNS, connection).500
delivery.attempts[].responseBodystring | nullRequiredTruncated to 4096 characters — somebody else's response is unbounded in length.
delivery.attempts[].responseHeadersmap<string, string> | nullRequired
delivery.attempts[].errorstring | nullRequired
delivery.attempts[].manualbooleanRequiredWas this attempt triggered by hand?
delivery.attempts[].triggeredBystring | nullRequired