API safety

Errors, retries, and side effects

Recovery is operation-specific. Create checkout may replay only after an ambiguous transport result or HTTP 502, 503, or 504, with the same key and byte-identical retained body. An indeterminate Confirm becomes GET-only and a submitted Cancel becomes GET-only while pending or unknown. Never turn a timeout into a second payment, booking, or cancellation.

Common error body

Every Agent API error uses one envelope. Branch on error.code, never English text. Keep error.requestId for support and observability. retryable=true means a bounded retry may be appropriate; the transaction rules below still take precedence.

{
  "error": {
    "code": "payment_not_authorized",
    "message": "Payment must be authorized before confirming the booking",
    "status": 400,
    "requestId": "req_...",
    "retryable": false,
    "details": []
  }
}

Stable error codes

The message below is representative and useful for debugging; branch only on error.code. retryable is the value in the error envelope, not permission to repeat a write without the recovery rule.

CodeHTTPRepresentative messageEndpoint / scoperetryableCaller action
invalid_request400 / 422Request validation failedAny endpoint with invalid fieldsfalseCorrect the reported fields; do not repeat unchanged input.
invalid_json400Body is not valid JSONJSON POST endpointsfalseSend syntactically valid JSON.
unsupported_media_type415Unsupported Media TypeJSON POST endpointsfalseSend Content-Type: application/json.
payload_too_large413Request body is too largeAny endpointfalseReduce the request body before retrying.
method_not_allowed405Method Not AllowedWrong method on an Agent API routefalseUse the method documented for that path.
invalid_api_key401Invalid Agent API keyAll authenticated endpointsfalseReplace, rotate, or reactivate the server-side key.
forbidden403Agent Commerce connection is not configuredApplication, environment, connection, or checkout authorizationfalseInspect capabilities and application configuration; do not bypass the boundary.
agent_commerce_live_disabled403Live Agent Commerce has been disabled for this partner. Use a Test application or contact support.Every Live requestfalseStop. Use Test, or wait until the workspace's Live program is enabled; never fall back to a different key or environment.
agent_commerce_terms_not_approved403Live Agent Commerce is unavailable while the current Agent API Program Terms are pending approval. Use a Test application.Every Live requestfalseStop. Live remains fail-closed until Brek approves the offered agreement version.
agent_commerce_terms_not_current403Accept the current Agent API Program Terms before using Live Agent Commerce.Every Live requestfalseStop. Read capabilities and have the workspace owner review and accept the current approved version.
agent_commerce_terms_acceptance_missing403Recorded acceptance of the current Agent API Program Terms is required before using Live Agent Commerce.Every Live requestfalseStop. An authorized workspace owner must accept the current approved agreement before Live can run.
not_found404Cannot GET /api/v1/agent-commerce/...Unknown Agent API routefalseCorrect the endpoint path.
quote_not_found_or_expired404Quote not found, expired, or owned by another applicationCreate checkoutfalseLoad current offers, create a new Quote, and obtain approval again.
checkout_not_found404Checkout session not foundRetrieve checkout or ConfirmfalseVerify the application-scoped checkout ID; do not guess another identifier.
booking_not_found404Booking not foundRetrieve, preview, or cancel bookingfalseVerify the application-scoped bookingReference.
invalid_stay_dates400checkIn and checkOut must be valid YYYY-MM-DD dates, and checkOut must be after checkInSearch, hotel offers, or QuotefalseSend valid future ISO dates with checkOut after checkIn; do not repeat unchanged input.
agent_commerce_invalid_occupancy400Exact-date search supports 1 to 3 rooms.Exact SearchfalseSend 1–3 rooms. Every room must have 1–8 adults age 18–120 and may have 0–8 children age 0–17; the full stay may have at most 10 adults and 10 children.
agent_commerce_invalid_flexible_dates400At least one check-in date from destination-local today through 30 days from today is required.Flexible searchfalseUse an accepted preset or explicit date inside the published destination-local window.
agent_commerce_invalid_flexible_occupancy400Flexible dates currently supports one room with 1–2 adults. Choose exact dates for larger groups or children.Flexible searchfalseUse one room with one or two adults, or switch to exact-date Search.
flexible_search_unavailable503Flexible search is temporarily unavailable because the Brek cache-only inventory endpoint could not be reached. Use exact-date search and retry flexible search later.Flexible searchtrueUse exact-date Search now. Retry flexible discovery later with bounded backoff; it has no transaction side effect.
agent_commerce_search_unavailable503The Brek retail search is temporarily unavailable. Retry this exact search before showing prices.Exact SearchtrueRetry the same read with bounded backoff. Do not show stale, partial, or inferred prices.
agent_commerce_identity_bridge_unavailable503The Brek inventory identity bridge is temporarily unavailable. This request is safe to retry with backoff.Exact SearchtrueRetry the same read with bounded backoff. Never infer or substitute an MCP, consumer, provider, or source property identifier.
agent_commerce_public_provider_unsupported400Only Expedia.com and Booking.com are supported.Resolve public redirectfalseUse the exact provider and request body from a current resolve_public_redirect action; never rename or invent a provider.
agent_commerce_public_redirect_unavailable400The selected public provider did not return a trusted HTTPS destination. Search again before redirecting the traveler.Resolve public redirectfalseDiscard the old action and Search again. Never construct, repair, or guess an external provider URL.
idempotency_key_required400Idempotency-Key header is required and must be at least 8 charactersCreate checkout, Confirm, CancelfalseAdd a unique key for this logical write.
idempotency_conflict409Idempotency-Key was already used with different inputCreate checkout, Confirm, CancelfalseRetrieve current state. Never rotate the key to force a duplicate operation.
quote_checkout_conflict409This Quote is already reserved for a checkout. Reuse the original create request to recover it, or create a fresh Quote for another checkout.Create checkoutfalseRecover the existing checkout identified in error.details, or obtain a fresh Quote. Never rotate the key or alter the body to create a second checkout from this Quote.
checkout_abandon_not_allowed409Test checkout cannot be abandoned from its current statusAbandon Test checkoutfalseRetrieve the same checkout and follow its typed next action. Never use Abandon after Confirm was attempted, never submit another booking write, and never create an automatic replacement checkout from the same Quote.
payment_not_authorized400Payment must be authorized before confirming the bookingConfirmfalseRetrieve checkout and finish hosted or Stripe.js authorization.
checkout_expired400Checkout session has expiredPayment authorization or ConfirmfalseCreate a fresh Quote and checkout; do not reuse the expired session.
checkout_expiry_payment_release_pending409Checkout expiry is being reconciled and payment-hold release is not yet confirmed.Confirm after checkout expiryfalseGET the same checkout and follow expiration.nextAction. Do not Confirm or create another checkout from this Quote. Only payment.status=voided with expiration.phase=voided proves release.
state_conflict409Checkout session cannot be changed from its current statusCheckout, Confirm, preview, or Cancel state transitionfalseRetrieve the resource and follow its current state; do not force another write.
confirmation_in_progress409Checkout session is already being confirmedConfirmfalsePoll the existing checkout instead of submitting another confirmation.
confirmation_payment_pending409The hotel is booked but payment capture has not settled. Retrieve the checkout; do not create another booking.Confirm after supplier successfalseGET-poll the same checkout. Never start another checkout or hotel booking.
confirmation_outcome_unknown409The supplier booking outcome is not yet known. Do not retry with a new key; retrieve the checkout and contact Brek support.Confirm after an ambiguous supplier outcomefalseStop all booking writes, GET-poll the checkout, and contact support.
confirmation_failed409The supplier booking failed. Retrieve the checkout for its terminal state.Confirm recoveryfalseRetrieve the failed checkout. Start a new Quote only after showing the traveler that no booking was created.
confirmation_preflight_failed400Booking validation failed before supplier dispatch. Payment authorization was released; retrieve this checkout for its terminal state.Confirm before any supplier dispatchfalseGET the original checkout and require status=failed and payment.status=voided. Do not retry Confirm or reuse this Quote; a new attempt needs a fresh Quote and traveler approval.
confirmation_payment_void_pending409The supplier booking failed, but the payment release outcome is not yet known. Retrieve this checkout; do not create another booking.Confirm or GET recovery after proven booking failurefalseGET-poll only the original checkout while confirmation.nextAction.type=poll_checkout. Payment release is proven only by payment.status=voided; never resubmit Confirm.
invalid_approval_attestation400Confirmation requires fresh human approval bound to this checkout's exact approvalReceipt fingerprintConfirmfalseStop. Re-present the current approval receipt, obtain fresh human approval, and submit a correctly bound attestation only if the checkout remains payment_authorized.
quote_expired_requote_required409The approved quote expired or changed. Its payment hold was released; quote the room again and obtain fresh traveler approval.Confirm before supplier executionfalseDo not Confirm again. Create a new Quote and checkout, present the current terms, and obtain fresh payment and human approval.
quote_expired_payment_release_pending409The approved quote expired or changed. Payment-hold release is not yet confirmed.Confirm before supplier executionfalseGET the same checkout and follow expiration.nextAction. Do not Confirm or create another checkout from this Quote until Brek proves release or support resolves it.
cancellation_approval_required403Create a cancellation preview, obtain human approval, and pass its short-lived approvalTokenLive CancelfalsePreview, present exact consequences, obtain human approval, then send the new token.
cancellation_human_approval_required403Live cancellation requires fresh human approval bound to the exact cancellation preview fingerprintLive CancelfalseDiscard the attempted write; create a fresh preview, present its consequences, obtain human approval, and bind the attestation to previewFingerprint.
invalid_cancellation_approval403Cancellation approval token is invalid. Create a fresh preview and obtain approval again.Live CancelfalseDiscard the token; re-preview, re-present, and obtain approval again.
cancellation_preview_expired409Cancellation preview expired. Create a fresh preview and obtain approval again.Live CancelfalseDiscard the expired token; re-preview, re-present, and re-approve.
cancellation_preview_stale409Cancellation consequences changed. Create a fresh preview and obtain approval again.Live Cancel, before supplier executionfalseShow the changed refund and penalty, then obtain new approval before a new Cancel.
cancellation_approval_unavailable503Live cancellation approval signing is not configuredLive preview or CancelfalseStop. Do not attempt cancellation until Brek restores approval signing.
cancellation_approval_release_failed409Cancellation was not sent to the supplier, but the local claim could not be released. Retrieve the booking before retrying.Live Cancel, before supplier executionfalseA new Cancel attempt is permitted only if the booking is still confirmed, after a fresh preview, consequence presentation, and human approval.
cancellation_in_progress409Booking cancellation is in progress. Do not retry; retrieve the booking to poll its status.CancelfalseGET-poll the booking and follow cancellation.nextAction; never POST Cancel again.
cancellation_requires_attention409Booking cancellation requires attention. Do not retry; retrieve the booking and contact Brek support.Cancel replay or concurrent CancelfalseStop cancellation writes, retrieve the booking, and contact support.
cancellation_outcome_unknown409The supplier cancellation outcome is unknown. Do not retry cancellation; retrieve the booking and contact Brek support.Cancel after an ambiguous supplier outcomefalseNever retry Cancel. GET-poll the booking, then contact support.
rate_limited429Too Many RequestsPer-application request windowtrueHonor Retry-After and retry with bounded jitter.
capability_not_enabled403 / 503Checkout mode 'native' is disabled for this applicationDisabled payment, supplier, refund, or application capabilityfalseStop and re-read capabilities; do not cross a disabled boundary.
service_unavailable503The required Agent Commerce service is unavailablePayment, supplier, refund, checkout, or preview dependencyfalseFor Create checkout only, this 503 permits recovery with the same key and byte-identical retained body. Otherwise follow an operation-specific zero-side-effect rule; Confirm and submitted Cancel recover with GET only.
internal_error500–599The Agent API could not complete the requestUnexpected server failureread-dependentRetry reads, Search, offers, or Quote with bounded backoff. Confirm and submitted Cancel recover with GET only. For Create checkout, replay only after an ambiguous transport result or HTTP 502, 503, or 504, with the same key and byte-identical retained body.

What to do by status

400

Correct invalid input only where that operation's contract permits another attempt. If payment state changed outside Brek, retrieve checkout state; the Confirm and Cancel recovery rules below take precedence.

401

The key is missing or invalid. Replace it before retrying.

403

The application, environment, or checkout mode does not allow the operation.

404

The resource is absent or outside this application's scope.

409

Retrieve current state. Never rotate the Idempotency-Key to force a second transaction.

429

Wait for Retry-After, then retry with bounded backoff.

503

The required payment, supplier, or refund path is unavailable. For Create checkout only, recover with the same key and byte-identical retained body; if an identifier was returned, retrieve current state. Other writes follow their GET-only or stop rule.

5xx read

For Search and GET requests, use bounded exponential backoff with jitter.

5xx write

Create checkout may replay only after HTTP 502, 503, or 504, with the same key and byte-identical retained body; a different 5xx is not replay permission. An indeterminate Confirm becomes GET-only. A submitted Cancel becomes GET-only while pending or unknown unless an operation-specific error explicitly proves supplier cancellation was not attempted.

Operation safety matrix

OperationMethodSide effectRetry rule
CapabilitiesGETNoneSafe
Search hotelsPOSTNoneSafe
Flexible searchPOSTNoneSafe
Hotel room offersPOSTNoneSafe
Resolve public redirectPOSTRead-only provider recheck; creates no Brek transactionSafe as a fresh read; use only when the returned public-rate action requires it
QuotePOSTCreates a short-lived quote; no payment or bookingSafe; create a new quote after expiry
Create checkoutPOSTCreates a payment sessionOne checkout per Quote. Only after an ambiguous transport result or HTTP 502, 503, or 504, replay the same Idempotency-Key with the byte-identical retained body; changed input conflicts and a different checkout requires a fresh Quote
Authorize paymentStripe.js / hostedChanges payment state outside the Brek APIRetrieve checkout before asking the traveler to retry
Abandon Test checkoutPOSTReleases an unconfirmed Stripe Test authorization and makes the checkout terminalUse only before Confirm. Repeat the same bodyless operation or GET the same checkout until terminal void proof is returned; never use it in Live
Confirm bookingPOSTMay capture payment and book a hotelAfter an indeterminate response or typed confirmation 409, GET-poll only the original checkout and never submit another booking write
Retrieve checkout or bookingGETNoneSafe
Preview cancellationGETNone; calculates current consequences and may issue a sensitive five-minute Live approval tokenSafe to repeat. Never log or expose the token; use only the newest human-approved preview
Cancel bookingPOSTMay cancel and refundPreview, present consequences, and obtain approval before the first submission. After submission, use GET only while pending or unknown. A new attempt is allowed only for an explicitly documented pre-supplier failure, after a fresh preview, presentation, and approval

Idempotency rules

  • Create checkout, confirm, and cancel require an Idempotency-Key whose trimmed length is at least eight characters. That is the current published input constraint; no maximum length or restricted character set is established. For portability, use a conservative ASCII UUID-like token with no leading or trailing whitespace, and retain its exact bytes.
  • An idempotency key identifies one logical write; it does not make a replay safe. Follow the operation-specific recovery rule below.
  • Submit Create checkout serially and retain the exact serialized request body before the first attempt. The key alone is not permission to send a second request.
  • A Quote can own exactly one checkout. A different logical create for that Quote returns 409 quote_checkout_conflict. Read the typed details[].checkoutSessionId and matching details[].nextAction.type=get_checkout, then retrieve that checkout. Never parse the human reason text. If the typed identifier is absent, stop or obtain a fresh Quote before creating another checkout.
  • Only after an ambiguous transport result or HTTP 502, 503, or 504 may trusted code recover Create checkout with the same key and the byte-identical retained body. A changed body returns 409 idempotency_conflict. v1 does not return a replay header; compare the checkout identifier and retrieve current state.
  • There is no universal replay-retention promise. Lookup scope and recovery lifetime depend on the Quote, checkout session, booking, and operation. Key rotation inside the same application does not bypass those rules. Do not rely on replay after a checkout expires.
  • After an indeterminate Confirm response or typed confirmation 409, make only GET requests for the original checkout. Never issue another booking write, even with the same key.
  • When checkout status is expiry_requires_attention, follow expiration.nextAction. Do not Confirm or create another checkout from the same Quote. Only payment.status=voided together with expiration.phase=voided proves the hold was released.
  • Once Cancel is submitted, use GET booking only while its outcome is pending or unknown. Never resubmit it, even with the same key.
  • Cancellation has a stricter recovery rule. When the booking is cancellation_in_progress, GET-poll the existing booking and do not call Cancel again—even with the same key. When it is cancellation_requires_attention, or when cancellation.outcome=unknown, stop all cancellation writes and contact support.
  • There is no published timeout after which a cancellation becomes safe to resubmit. If an in-progress state does not change, treat it as stale and escalate with the redacted booking reference and request ID.
  • Only an error explicitly documented as occurring before supplier cancellation may start a new Cancel attempt. Create a fresh preview, present the current consequences, and obtain fresh human approval first; if the operation-specific error says to stop, stop.

Idempotency does not replace traveler approval

Your product must retain evidence that the traveler approved the current price, terms, and requested action. An idempotency key only identifies a logical request; it is not proof of approval or a guarantee that an interrupted external transaction can be replayed. After an ambiguous Confirm response, GET-poll only the original checkout and never issue another booking write. After Cancel is submitted, retrieve by GET but do not resubmit while the outcome is pending or unknown; follow the returned cancellation.nextAction and escalate unknown or stale state.

Use authenticated support without sending secrets

When a typed next action says contact_support, stop writes and open authenticated Partner Console support. Initially share the request ID, error code, UTC timestamp, environment, and only the minimum redacted identifier suffix needed. Never send an API key, Authorization header, signed hosted-checkout URL or token, Stripe client secret, card data, traveler PII, model prompt, or full response body. If support needs an exact opaque checkout or booking reference, enter it only in the authenticated form's designated field.