API referencepayments
Create payment intent
POST
/v1/payments/intentAccepts X-Api-Key · oauthClientCredentials · bearer
Create a payment intent from a repriceRef, or on the tenant's own provider account
Returns PSP client secret or Pix QR payload.Show more
Returns PSP client secret or Pix QR payload. Provider behind the seam is config. With paymentSource the intent runs on the tenant's OWN account at the provider, registered as a tenant-owned credential set: the caller states amount, sends no repriceRef, and must send an Idempotency-Key; the intent never pays an Apiq order and the response carries paymentSource instead of fork. Production external API never accepts PAN; SANDBOX/UAT accept a card sealed to the platform key on POST /v1/orders, never on this route. CREDIT_LINE and BR installments ride the same intent object. A deployment with no payment provider configured (provider=none) answers 501 NOT_IMPLEMENTED on every payment route.Headers
- Idempotency-KeystringOptional
OPTIONAL on intent create — except with paymentSource, where it is REQUIRED (400 VALIDATION details.reason IDEMPOTENCY_KEY_REQUIRED) because it keys the provider's own idempotency.Show more
OPTIONAL on intent create — except with paymentSource, where it is REQUIRED (400 VALIDATION details.reason IDEMPOTENCY_KEY_REQUIRED) because it keys the provider's own idempotency. A replay returns the STORED answer, so its `checkout` (the hosted session an intent may carry) may since have been consumed or expired: re-read the intent (GET /v1/payments/{paymentId}) for the live session.
Request body
- repriceRefstringOptionalRequired unless paymentSource is present
- amountMoneyOptionalInteger minor units + ISO-4217, never floats (platform law)
5 child attributes
- amountintegerRequiredMinor units
- currencystringRequiredISO-4217
- fxAppliedbooleanOptionalTrue when the amount was converted from the supplier's currency.
- fxSnapshotIdstringOptionalThe exchange-rate snapshot the amount was converted with, fixed when the price was quoted.
- breakdownarray of PriceBreakdownLineOptional
Sell-side component lines summing to amount; the platform may send it empty.Show more
Sell-side component lines summing to amount; the platform may send it empty. One line shape everywhere — the same PriceBreakdownLine the rule simulation answers. Never carries a supplier net.13 child attributes
- typestringRequiredThe kind of line, for example BASE, TAX, FEE or DISCOUNT.
- amountintegerRequiredMinor units
- feeTypestringOptionalFor FEE lines produced by a FEE_LINE rule — the tenant's fee label
- labelstringOptionalDisplay label in the request locale
- taxComponentstringOptionalFor GST / TAX lines — CGST | SGST | IGST | UTGST | CESS | VAT | SALES_TAX | OCCUPANCY_TAX; open string
- taxCodeTaxCodeOptionalThe statutory classification of a taxed line (SAC/HSN in India, VAT codes elsewhere)
2 child attributes
- schemestringRequiredSAC | HSN | VAT | GST | OTHER; open string
- codestringRequiredThe tax code, for example a SAC or HSN code in India or a VAT code elsewhere.
- rateBasisPointsintegerOptionalThe tax or fee rate in basis points when the line is rate-based (1800 = 18%)
- collectedBystringOptionalPLATFORM | SUPPLIER | PROPERTY — who takes the money; a non-PLATFORM line outside Offer.price is a pay-at-property disclosure
- payableAtstringOptionalBOOKING | PROPERTY | DESK | AFTER_SERVICE
- includedbooleanOptionalTrue when the line is inside the enclosing amount (default); false for a disclosed line outside it
- quantityintegerOptionalHow many units the line covers, when more than one.
- unitAmountintegerOptionalMinor units per unit when quantity is given
- currencystringOptional
ISO-4217 code of this line's amount and unitAmount.Show more
ISO-4217 code of this line's amount and unitAmount. Set ONLY on a disclosed line (included false) that is NOT in the enclosing price's currency, for example a resort fee payable at the property in its local currency. Absent means the enclosing price's currency, as before; a line inside the total never carries it.
- paymentSourcePaymentSourceOptional
v1.49.0: the tenant's OWN account at a payment provider, registered as a tenant-owned credential set through POST /admin/v1/credential-sets.Show more
v1.49.0: the tenant's OWN account at a payment provider, registered as a tenant-owned credential set through POST /admin/v1/credential-sets. The set is validated on first use (the key's mode against the set's environment, then a read-only provider call); a set that cannot serve payments is 409 CONFLICT details.reason PAYMENT_SOURCE_NOT_READY, an unknown one (or another tenant's) 404 NOT_FOUND details.reason PAYMENT_SOURCE_NOT_FOUND, a supplier with no source on this deployment 400 VALIDATION details.reason PAYMENT_SOURCE_NOT_OFFERED, and a deployment without payment sources 501 NOT_IMPLEMENTED details.reason PAYMENT_SOURCES_NOT_ENABLED. When the provider cannot be asked whether a new set's key works, the create is 502 UPSTREAM_UNAVAILABLE details.reason PAYMENT_SOURCE_UNVERIFIED (retryable) and the set is left as it was.2 child attributes
- supplierstringRequiredThe provider's supplier id, e.g.
- credentialSetstringOptionalThe tenant's credential set id; absent = the tenant's own set for the supplier
- methodstringOptional
CARD (hosted fields) | PIX | UPI | CREDIT_LINE | INSTALLMENTS | BNPL | ACH | NETBANKING | WALLET | EMI | APM.Show more
CARD (hosted fields) | PIX | UPI | CREDIT_LINE | INSTALLMENTS | BNPL | ACH | NETBANKING | WALLET | EMI | APM. Open string; a method the provider cannot run is 400 VALIDATION details.reason UNKNOWN_METHOD. NETBANKING, WALLET and EMI are the India rails; EMI carries its plan in installmentPlan. - captureModelstringOptional
MANUAL_CAPTURE | SALE_ONLY | BNPL_AUTH; absent = the provider's default for the method.Show more
MANUAL_CAPTURE | SALE_ONLY | BNPL_AUTH; absent = the provider's default for the method. A model the provider cannot run is 400 VALIDATION details.reason CAPTURE_MODEL_NOT_SUPPORTED. - installmentPlanobjectOptionalBR installments
- sessionPaymentSessionOptional
Optional order context some providers need to open a payment session.Show more
Optional order context some providers need to open a payment session. Amounts are integer minor units in the intent currency. A bad field is 400 VALIDATION details {field: session.*, reason: INVALID_SESSION}; a card number in any text field is 400 VALIDATION details.reason INSTRUMENT_DATA_REFUSED.5 child attributes
- countrystringOptional
- localestringOptional
- orderRefstringOptional
- taxAmountMoneyOptionalInteger minor units + ISO-4217, never floats (platform law)
5 child attributes
- amountintegerRequiredMinor units
- currencystringRequiredISO-4217
- fxAppliedbooleanOptionalTrue when the amount was converted from the supplier's currency.
- fxSnapshotIdstringOptionalThe exchange-rate snapshot the amount was converted with, fixed when the price was quoted.
- breakdownarray of PriceBreakdownLineOptional
Sell-side component lines summing to amount; the platform may send it empty.Show more
Sell-side component lines summing to amount; the platform may send it empty. One line shape everywhere — the same PriceBreakdownLine the rule simulation answers. Never carries a supplier net.13 child attributes
- typestringRequiredThe kind of line, for example BASE, TAX, FEE or DISCOUNT.
- amountintegerRequiredMinor units
- feeTypestringOptionalFor FEE lines produced by a FEE_LINE rule — the tenant's fee label
- labelstringOptionalDisplay label in the request locale
- taxComponentstringOptionalFor GST / TAX lines — CGST | SGST | IGST | UTGST | CESS | VAT | SALES_TAX | OCCUPANCY_TAX; open string
- taxCodeTaxCodeOptionalThe statutory classification of a taxed line (SAC/HSN in India, VAT codes elsewhere)
- rateBasisPointsintegerOptionalThe tax or fee rate in basis points when the line is rate-based (1800 = 18%)
- collectedBystringOptionalPLATFORM | SUPPLIER | PROPERTY — who takes the money; a non-PLATFORM line outside Offer.price is a pay-at-property disclosure
- payableAtstringOptionalBOOKING | PROPERTY | DESK | AFTER_SERVICE
- includedbooleanOptionalTrue when the line is inside the enclosing amount (default); false for a disclosed line outside it
- quantityintegerOptionalHow many units the line covers, when more than one.
- unitAmountintegerOptionalMinor units per unit when quantity is given
- currencystringOptional
ISO-4217 code of this line's amount and unitAmount.Show more
ISO-4217 code of this line's amount and unitAmount. Set ONLY on a disclosed line (included false) that is NOT in the enclosing price's currency, for example a resort fee payable at the property in its local currency. Absent means the enclosing price's currency, as before; a line inside the total never carries it.
- orderLinesarray of objectOptional
4 child attributes
- namestringRequired
- quantityintegerRequired
- unitPriceMoneyRequiredInteger minor units + ISO-4217, never floats (platform law)
5 child attributes
- amountintegerRequiredMinor units
- currencystringRequiredISO-4217
- fxAppliedbooleanOptionalTrue when the amount was converted from the supplier's currency.
- fxSnapshotIdstringOptionalThe exchange-rate snapshot the amount was converted with, fixed when the price was quoted.
- breakdownarray of PriceBreakdownLineOptional
Sell-side component lines summing to amount; the platform may send it empty.Show more
Sell-side component lines summing to amount; the platform may send it empty. One line shape everywhere — the same PriceBreakdownLine the rule simulation answers. Never carries a supplier net.
- taxAmountMoneyOptionalInteger minor units + ISO-4217, never floats (platform law)
5 child attributes
- amountintegerRequiredMinor units
- currencystringRequiredISO-4217
- fxAppliedbooleanOptionalTrue when the amount was converted from the supplier's currency.
- fxSnapshotIdstringOptionalThe exchange-rate snapshot the amount was converted with, fixed when the price was quoted.
- breakdownarray of PriceBreakdownLineOptional
Sell-side component lines summing to amount; the platform may send it empty.Show more
Sell-side component lines summing to amount; the platform may send it empty. One line shape everywhere — the same PriceBreakdownLine the rule simulation answers. Never carries a supplier net.
Responses
- 200Intent with client-side handoff materialPaymentIntentResponse
- 400ApiErrorResponse
VALIDATION or UNKNOWN_DOMAIN. details.reason CAPTURE_MODEL_NOT_SUPPORTED (the provider cannot run the named captureModel), INVALID_SESSION (a bad session.* field, details.field names it) or…Show more
VALIDATION or UNKNOWN_DOMAIN. details.reason CAPTURE_MODEL_NOT_SUPPORTED (the provider cannot run the named captureModel), INVALID_SESSION (a bad session.* field, details.field names it) or INSTRUMENT_DATA_REFUSED (a card number in a text field). v1.49.0 (paymentSource): AMOUNT_FROM_REPRICE_ONLY (an amount without a source), REPRICE_REF_WITH_PAYMENT_SOURCE, AMOUNT_REQUIRED, AMOUNT_NOT_POSITIVE, IDEMPOTENCY_KEY_REQUIRED, PAYMENT_SOURCE_NOT_OFFERED. - 401ApiErrorResponse
UNAUTHENTICATED — missing or expired token.Show more
UNAUTHENTICATED — missing or expired token. Declared on every operation because `security` is declared globally at the document root: every route is authenticated, so every route can answer 401. - 402ApiErrorResponse
PAYMENT_FAILED — the provider refused to create the intent.Show more
PAYMENT_FAILED — the provider refused to create the intent. details.reason PROVIDER_REFUSED_CREATE with declineCode, paymentId and psp. - 403ApiErrorResponse
PERMISSION_DENIED | TAB_DISABLED | OUT_OF_SCOPE | TENANT_MISMATCH, exactly as Forbidden, plus TENANT_SUSPENDED — declared on the verbs that start new business: search and poll, detail, schedules,…Show more
PERMISSION_DENIED | TAB_DISABLED | OUT_OF_SCOPE | TENANT_MISMATCH, exactly as Forbidden, plus TENANT_SUSPENDED — declared on the verbs that start new business: search and poll, detail, schedules, seatmap, ancillaries, rules, reprice, hold and order create, and on the payments routes (intent, confirm, 3ds-callback, checkout-session). While the caller's tenant is SUSPENDED every such request is refused TENANT_SUSPENDED, except an idempotent replay of an answer completed before the suspension (see hold / createOrder), before the entitlement and permission checks, and it is not retryable: the answer changes only when platform ops reinstates the tenant. The post-booking verbs (cancel-quote, cancel, amend), the order reads, and capture and refund on an existing payment intent (whose 403 is PaymentForbidden) stay up, so no traveller is stranded. On the production deployment the same requests are refused PERMISSION_DENIED with details.reason TENANT_NOT_LIVE while the tenant's production activation has not been approved, whichever credential is presented, console session or API key; use the sandbox deployment until activation. Not retryable; the post-booking verbs stay up. - 404ApiErrorResponse
NOT_FOUND — details.reason PAYMENT_SOURCE_NOT_FOUND: the tenant has no such credential set for the supplier.Show more
NOT_FOUND — details.reason PAYMENT_SOURCE_NOT_FOUND: the tenant has no such credential set for the supplier. A set of another tenant answers the same. - 405ApiErrorResponse
VALIDATION — the public edge serves GET, POST, PUT, PATCH and DELETE only; any other method is refused before the platform sees it (details.reason METHOD_NOT_ALLOWED, details.source edge).Show more
VALIDATION — the public edge serves GET, POST, PUT, PATCH and DELETE only; any other method is refused before the platform sees it (details.reason METHOD_NOT_ALLOWED, details.source edge). Not retryable as sent. - 409ApiErrorResponse
CONFLICT: details.reason PAYMENT_SOURCE_NOT_READY — the named credential set cannot serve payments (its key was refused, or it is revoked or disabled).Show more
CONFLICT: details.reason PAYMENT_SOURCE_NOT_READY — the named credential set cannot serve payments (its key was refused, or it is revoked or disabled). QUOTE_EXPIRED | IDEMPOTENCY_CONFLICT | IDEMPOTENCY_IN_PROGRESS. QUOTE_EXPIRED: the repriceRef expired or was spent, or it has no sell price the platform can charge — details.reason NO_SELL_PRICE_STAMP or SELL_PRICE_STAMP_MISMATCH, or REPRICE_REF_HELD when a live intent already holds this RP- with a different method or capture model — reuse that intent or reprice again. Reprice again and create the intent from the new RP-. IDEMPOTENCY_IN_PROGRESS also answers a second create on the same RP- while the first is still being created (details.reason INTENT_CREATE_IN_PROGRESS, retryable). - 413ApiErrorResponse
VALIDATION — the request body is larger than the surface's limit.Show more
VALIDATION — the request body is larger than the surface's limit. Not retryable as sent: shrink the body. Declared on every operation that takes a request body. details carries exactly one key, maxBodyBytes (integer): the limit that applies to this surface, in bytes. - 414ApiErrorResponse
VALIDATION — the request URI (path and query string) is longer than 8 KiB; the public edge refuses it before the platform sees it (details.reason URI_TOO_LONG, details.source edge).Show more
VALIDATION — the request URI (path and query string) is longer than 8 KiB; the public edge refuses it before the platform sees it (details.reason URI_TOO_LONG, details.source edge). Not retryable as sent: send long input in the body of a POST. - 415ApiErrorResponse
VALIDATION — a request body that is not JSON (application/json or application/*+json; a form body only on the OAuth token routes).Show more
VALIDATION — a request body that is not JSON (application/json or application/*+json; a form body only on the OAuth token routes). The public edge refuses it before the platform sees it (details.reason UNSUPPORTED_MEDIA_TYPE, details.source edge). Not retryable as sent: send Content-Type application/json. Declared on every operation that takes a JSON request body. - 429ApiErrorResponse
RATE_LIMITED — the API key or its tenant is over a rate limit; wait for Retry-After, then retry.Show more
RATE_LIMITED — the API key or its tenant is over a rate limit; wait for Retry-After, then retry. Every routed request is counted against two token buckets, the key's and the tenant's (all its keys together): 50 requests a second with a burst of 100 on the standard plan. Every travel verb except polling a search also spends from a supplier budget, per key and per tenant: 10 requests a second with a burst of 20, and a plan may add a daily quota of supplier calls. Answers carry the IETF RateLimit header fields for the tightest bucket consulted (RateLimit-Limit, RateLimit-Remaining, RateLimit-Reset, RateLimit-Policy); a refusal adds Retry-After and details.scope (KEY or TENANT), details.budget (PLATFORM or SUPPLIER), details.reason (RATE or DAILY_QUOTA), details.tier and details.retryAfterMs. A sandbox may run with the limiter off, in which case none of these headers is sent. A search that is still warming is a different answer with a different meaning (keep polling). with the key-velocity check on, also details.reason VELOCITY_LIMIT, a soft breach of the API key's own scope (components/responses/VelocityLimited), nextAction slow_down. - 500INTERNAL — a bug, and traceId is the support handle.ApiErrorResponse
- 501ApiErrorResponse
NOT_IMPLEMENTED — this deployment serves no payments: no payment provider is configured (provider=none), so every payment route answers 501.Show more
NOT_IMPLEMENTED — this deployment serves no payments: no payment provider is configured (provider=none), so every payment route answers 501. Not a capabilities-doc bug: payments are deployment configuration. - 502Also the order read routes (getOrder, listOrderDocuments, listOrderEvents, searchOrders) when the order store times out: details.store names the store, retryable true.ApiErrorResponse
- 503ApiErrorResponse
Errors you should handle
Branch on code and honour retryable — never on the HTTP status alone.
| 400 | VALIDATION message.intent.checkOut must be after message.intent.checkIn | not retryable |
| 400 | UNKNOWN_DOMAIN Unknown domain 'HOTELS'. Did you mean HTL? | not retryable |
| 401 | UNAUTHENTICATED The bearer token is expired. Re-authenticate and retry. | not retryable |
| 402 | PAYMENT_FAILED The payment provider refused to create the payment. | not retryable |
| 403 | PERMISSION_DENIED Role lacks the required scope: booking:write | not retryable |
| 403 | TAB_DISABLED Tenant acme-travel is not entitled to sell CRU | not retryable |
| 403 | OUT_OF_SCOPE The token's scope ladder does not reach this customer subtree | not retryable |
| 403 | TENANT_MISMATCH orderRef MO-8KD-2FQ-9XT does not belong to tenant acme-travel | not retryable |
| 403 | TENANT_SUSPENDED This tenant is suspended: new searches, quotes, holds, orders and payments are refused until it is reinstated. Existing orders can still be read, cancelled and amended. | not retryable |
| 404 | NOT_FOUND | not retryable |
| 405 | VALIDATION This method is not served. | not retryable |
| 409 | QUOTE_EXPIRED The repriceRef has expired or been spent. Reprice again, then create the intent from the new RP-. | not retryable |
| 409 | IDEMPOTENCY_IN_PROGRESS A payment intent for this repriceRef is still being created. Retry shortly. | retryable |
| 409 | IDEMPOTENCY_CONFLICT Idempotency-Key 7f0c33d1-9a1e-4d2c-9f77-1c0f2b3a4d55 was used with a different body; never executed | not retryable |
| 409 | CONFLICT The payment source cannot serve payments: Example Pay refused the key. | not retryable |
| 413 | VALIDATION The request body is larger than this API accepts (262144 bytes). | not retryable |
| 414 | VALIDATION The request URI is longer than 8 KiB. | not retryable |
| 415 | VALIDATION Send application/json. | not retryable |
| 429 | RATE_LIMITED Too many requests for this key on the supplier budget. Wait for the Retry-After interval before trying again. | retryable |
| 500 | INTERNAL An unexpected error occurred. Quote the traceId to support. | retryable |
| 501 | NOT_IMPLEMENTED This deployment serves no payments: no payment provider is configured. | not retryable |
| 502 | UPSTREAM_UNAVAILABLE Every supplier for HTL is circuit-open; no fan-out was attempted | retryable |
| 503 | PROVIDER_DOWN EXAMPLEHOTELS is unavailable; the order was not attempted and nothing was captured | retryable |