API reference

Errors

One envelope for every failure the gateway produces, one closed list of machine-readable types, and — per type — the status it arrives with and whether the same call can ever succeed.

The envelope

Every error response of the gateway has the same shape: an error object with a machine-readable type, a human-readable message and the requestId of the call. Some types add fields of their own — missingScope, retryAfter, the two amounts on a price change — and those are additive, so an unknown extra field is to be ignored rather than rejected.

The full shape, field by field: ApiErrorDto and ApiErrorDetailDto.

Branch on type and never on message: the wording is rewritten whenever it can be made clearer, and that is not a breaking change. New types are added at any time, so treat one you do not know like a generic failure of its status class.

Status codes

These are the statuses an operation page lists under its own Errors block. Each is described once, here.

StatusWhat it means
401

No key, or an invalid, revoked or expired key, or a valid key sent to the other environment. error.type tells them apart: authentication_required, invalid_api_key, api_key_revoked, api_key_expired, wrong_environment. On wrong_environment the key is fine and the base URL is not — a vcb_live_… key was sent to the sandbox host or a vcb_test_… key to the live host; error.gatewayEnvironment names the host, error.keyEnvironment the key. Do not rotate the key, change the URL.

402

The balance does not cover this operation (insufficient_funds), or the account is not active (account_not_active). Do NOT retry — the answer only changes once a top-up happens. error.availableCents and error.requiredCents name the shortfall, error.currency the account's currency.

403

The key is not allowed to do this. On insufficient_scope, error.missingScope names the missing scope; on ip_not_allowed the request came from an address outside the allowlist.

409

The same idempotency key was used with a different body (idempotency_key_reuse), or a request with this key is still in flight (idempotency_key_in_progress). The second case may be retried after a short wait.

429

Quota exhausted. Retry-After and error.retryAfter give the wait in seconds. This response MAY be retried — after the stated time.

500

A failure on our side. The requestId in the response belongs in every support request.

503

A supplier is unreachable (upstream_unavailable). May be retried, with growing backoff.

Every error type

The closed list of error.type. It is part of the contract: a value is added additively, and renaming one would take a new major version.

TypeStatusWhenRetry?
authentication_required401No Authorization header, or one the gateway cannot read as a bearer key.NoSend the key.
invalid_api_key401The key is not one we issued, or its secret does not match.NoCheck the key you deployed, then issue a new one in the portal.
api_key_revoked401The key existed and was switched off in the partner portal.NoA revoked key never comes back — issue a new one.
api_key_expired401The key is past the expiry date it was issued with.NoIssue a new key.
insufficient_scope403The key is valid and does not carry the scope this operation needs; error.missingScope names it.NoIssue a key with that scope — scopes are fixed at issue time.
ip_not_allowed403The request came from an address outside the key's allowlist.NoAdd the address in the portal, or call from an allowed one.
wrong_environment401A vcb_live_… key was sent to the sandbox host, or a vcb_test_… key to the live host.NoThe key is fine and the base URL is not. Change the URL, do not rotate the key.
invalid_request400A malformed body, a failed validation, or a property the endpoint does not know — unknown fields are rejected, not ignored.NoFix the request.
not_found404Unknown resource, or an id that belongs to no object of yours.No
method_not_allowed405The path exists and does not answer this method.No
unsupported_media_type415A body that is not application/json.No
idempotency_key_required400A POST without Idempotency-Key, or a key that is not 8 to 255 printable ASCII characters.NoSend a key and keep it for every retry of that same operation.
idempotency_key_reuse409The same idempotency key was already used with a different body.NoA decision, not a glitch: either resend the original body, or use a new key for the new one.
idempotency_key_in_progress409A request with this key is still running.YesAfter a short wait, with the same key and the same body.
offer_expired410A rate, offer or quote handle is past its lifetime of roughly twenty minutes, or is unknown.NoNot as sent — search again and re-price. It is deliberately not a 404, which would send you looking for a bug.
price_changed409The supplier now prices the offer above the ceiling the request allowed. Nothing was booked.NoOnly after deciding on the new price: book again with a ceiling that covers it.
insufficient_funds402The prepaid balance does not cover this operation. Raised before a supplier is contacted; carries availableCents and requiredCents.NoAfter a top-up.
search_quota_exceeded429The daily search ceiling set for your account has been reached.NoNot today — it resets at the time in error.resetAt, and the ceiling itself is yours to change in the portal.
account_not_active403The account is read-only or suspended.NoAn invoicing matter, not a key problem — topping up does not help.
not_implemented501The operation is real, documented and not available for this key yet. Reserved — nothing raises it today.No
settlement_not_allowed403The request asked to settle in a mode this key does not carry. The mode follows the key, not the body.NoUse a key issued for that mode.
settlement_not_available501The mode is agreed for your account and this gateway cannot execute it — today that is traveller-paid (vacabee) settlement.NoNothing was booked, reserved or charged. Book with a key that settles as partner until it ships.
rate_limit_exceeded429Too many requests in the running window, for this key or from this address.YesAfter the seconds in Retry-After and error.retryAfter.
upstream_unavailable503A supplier or an internal service did not answer.YesWith growing backoff and the original idempotency key. Bounded, not forever.
upstream_error503A supplier answered, with a failure or a rejection.YesWith backoff — but a rejection does not become an acceptance, so give up early.
internal_error500A failure on our side.YesWith backoff and the original idempotency key. Quote requestId if it persists.

Where this is explained further

The reasoning behind the retry rules — which key to reuse, what a replay returns, and what a 5xx does to an idempotency record — is on Idempotency, and what the /v1 surface is allowed to change without notice is on Errors and versioning.