Skip to content

Error Codes

API errors are returned with a consistent standard error response. Raw bank error codes (bank transaction/result codes) are not surfaced to the customer; you only see the stable codes below.

{
"error": {
"code": "terminal_not_allowed",
"message": "Terminal is not allowed for this partner.",
"traceId": "trc_9f12ab"
}
}
Field Description
code Stable, machine-readable error code (catalog below).
message Human-readable description.
traceId A trace identifier to share in support requests.
  • Validation and business-rule violations → 4xx (e.g., 400, 409, 422).
  • Authentication failure → 401; authorization or access denial → 403.
  • The requested resource was not found or is outside your access scope → 404.
  • Rate limit exceeded → 429 (the response may carry a Retry-After header telling you how long to wait).
  • The platform is temporarily at its capacity ceiling → 503 (reserved — not currently produced; see the code catalog below for details and the 429 distinction).
  • Every error response carries an error.code and a traceId.
Code HTTP Meaning
validation_error 400 The request body failed validation (a missing or invalid field).
sensitive_data_not_allowed 400 A free-text field (for example, a payment link’s items[].name/items[].note/payerName) has a value where, after removing spaces and hyphens, the entire field matches a card-number or IBAN pattern. The check only triggers when the whole field matches; a card number or IBAN embedded inside other text is not rejected.
duplicate_partner_tx_code 409 The same partnerTxCode was used with a different request (or belongs to a payment link) — the code is in use for a different charge. The same partnerTxCode with the same request does not produce this error; it returns a 200 idempotent replay. See Idempotency: partnerTxCode.
terminal_not_allowed 403 The terminal does not belong to the partner or is inactive.
partner_not_active 403 The partner account is not active (suspended or closed); new payments and partner-initiated refunds are blocked.
amount_limit_exceeded 422 A per-transaction min/max amount limit is configured for your account and the amount is outside that range. Without a configured limit (the default), only positivity is validated: a negative or 0 amount is rejected with 400 validation_error.
pos_per_tx_limit_exceeded 422 A per-transaction limit configured on the bank connection behind your terminal was exceeded. This differs from amount_limit_exceeded: that one is your own account’s limit; this one is a limit on your bank connection. See bank connection limits.
pos_daily_limit_exceeded 422 A daily limit configured on the bank connection behind your terminal is full. See bank connection limits.
pos_weekly_limit_exceeded 422 A weekly limit configured on the bank connection behind your terminal is full. See bank connection limits.
pos_monthly_limit_exceeded 422 A monthly limit configured on the bank connection behind your terminal is full. See bank connection limits.
pos_time_window_limit_exceeded 422 A limit configured for a specific time window on the bank connection behind your terminal is full. See bank connection limits.
installment_not_allowed 422 The card is not eligible for installments, the terminal has no installment permission, the card’s brand is known but this brand × installment count is not offered at your terminal, or a payment link’s installment restriction was violated. See the remedy under installment mismatch.
payment_not_found 404 The requested payment was not found or does not belong to this partner.
payment_not_refundable 422 The payment is not in a refundable state.
refund_amount_exceeds_remaining 422 The requested refund amount exceeds the remaining refundable balance.
refund_not_yet_available 422 On the default refund endpoint (POST .../refunds), a same-processing-day partial refund: available after day-end.
refund_method_not_available 422 On the POST .../refunds/void endpoint, the forced method cannot be applied to this payment: the requested amount does not equal the entire remaining balance, or the payment already has an approved credit refund.
refund_in_progress 409 A pending refund already exists for this payment (single-flight), or this partnerRefundCode was already used (duplicate code).
refund_not_found 404 The requested refund was not found or does not belong to this partner.
direct_refund_not_allowed 422 This payment was made on a direct-collection (direct) terminal; a credit refund is not possible — only a same-day full void is available.
payment_link_not_found 404 The requested payment link was not found or does not belong to this partner.
payment_link_already_paid 409 The payment link is already completed. (Rarely, the same error is returned if your cancellation races with a payment completing at that exact moment.)
payment_link_expired 409 The payment link’s window has expired; it can no longer be cancelled.
payment_link_cancelled 409 The payment link was already cancelled.
payment_link_items_amount_mismatch 422 The items you sent when creating a payment link do not add up to the amount field. Partial itemization is not supported.
rate_limited 429 Too many requests: the rate limit was exceeded. Wait a while and retry.
service_overloaded 503 The platform is temporarily at its capacity ceiling; your request was rejected in a controlled way. Not currently produced (reserved — the capacity control runs in observation mode today; this behavior applies once it is activated). When active: the response carries a Retry-After header (in seconds); wait that long and retry with exponential backoff. ⚠ Don’t confuse this with rate_limited (429): rate_limited means a client quota for that endpoint was exceeded on an API-key, account, or source-IP axis; service_overloaded is the platform’s overall capacity signal.

Installment mismatch (installment_not_allowed)

Section titled “Installment mismatch (installment_not_allowed)”

installment_not_allowed can occur for four distinct reasons:

  1. The card is not eligible for installments (installment sales are supported only for certain cards).
  2. Your terminal’s effective installment set (which may be narrowed on your account; see Installment Options) does not cover this installment count.
  3. The card’s brand is known, but this brand × installment count is not offered at your terminal. Installment Options tells you exactly which installment counts are offered: every count missing from that list produces this error.
  4. The allowedInstallmentCounts you sent when creating a payment link is empty, or is not a subset of your terminal’s effective installment set.

When it occurs: reason 2 does not depend on the card’s brand, so it is evaluated in the POST /v1/payments call itself, at create time. Reasons 1 and 3 require the card’s brand, so they only surface during the Card Handoff’s 3D relay. Reason 4 is evaluated at create time on POST /v1/payment-links.

The remedy is the same for reasons 1-3: the installment count is fixed when you create the payment (POST /v1/payments) and cannot be changed on the same payment. If you get this error, create a new cardless payment with a different installment count; the same payment can only be retried with a different card — the installment count stays fixed. ⚠ Note: reasons 1 and 3 surface during the 3D relay, so each attempt draws down this payment’s card attempt allowance; once it runs out the payment is locked to failed (card_attempt_limit_exceeded) and the paymentRef can no longer be used. In other words, “retry with a different card” is not unlimited. See Payment Flow — card attempt limit. To see this coming ahead of time, query which installment counts and commission rates the card qualifies for on your checkout using Installment Options. For reason 4, the remedy is to resend allowedInstallmentCounts as a subset of your terminal’s effective installment set (this restriction cannot be changed once the link is created).

Your bank may configure separate per-transaction, daily, weekly, monthly, and time-window limits on the bank connection behind your terminal. When one of these limits is full, the request is rejected with 422; you learn which one from the specific code returned. Threshold or current-usage values never appear in any response.

Code Window
pos_per_tx_limit_exceeded Per transaction
pos_daily_limit_exceeded Daily
pos_weekly_limit_exceeded Weekly
pos_monthly_limit_exceeded Monthly
pos_time_window_limit_exceeded A specific time window

When it occurs: this can surface at two different moments, and the behavior differs:

  • At payment creation (POST /v1/payments): the request is rejected with 422 directly to your server, with the specific code in error.code. The payment is not created; partnerTxCode is not consumed, so you can retry the same request later.
  • At the start of 3D (during Card Handoff): the rejection happens in the buyer’s browser/WebView. The buyer sees a generic, sterile error page — no code-specific message is shown, and error.code is not returned to your server. The payment’s status does not change (it stays initiated) and this attempt does not draw down the card attempt allowance; you can retry with the same paymentRef. This also applies to payments started via a payment link.

If you get this error, wait a while and retry, or contact your business’s account manager.

The codes below occur during Card Handoff. For payment_ref_invalid / payment_ref_expired / payment_prepare_already_started / card_attempt_limit_exceeded, the remedy is the same: create a new cardless payment (POST /v1/payments), then start the handoff again with the new paymentRef/handoffUrl it returns. virtual_pos_inactive and psp_inactive are different: your terminal’s bank connection (or the payment provider behind it) is temporarily inactive, so creating a new payment reproduces the same error — wait until it is resolved or contact support. virtual_pos_card_type_not_supported is different too, and it depends on the card: creating a new payment with the same card reproduces the same error — ask the buyer for another card. payment_declined also depends on the card, but needs no new payment: the payment stays initiated, so the buyer can continue on the same payment with a different card.

Code HTTP Meaning
payment_ref_invalid 404 The paymentRef is invalid/unknown.
payment_ref_expired 404 The paymentRef has expired.
payment_prepare_already_started 409 3D has already been started for this paymentRef (single-use).
card_attempt_limit_exceeded 429 The allowed number of card attempts for this paymentRef was exceeded; the payment is locked to failed. In your retry logic, treat this as a trigger to create a new cardless payment, not to retry with a different card on the same payment.
virtual_pos_inactive 422 The bank connection backing this terminal is currently inactive; a new payment cannot be prepared. In-flight (already started) transactions are unaffected.
psp_inactive 422 The payment provider behind this terminal’s bank connection is currently inactive; a new payment cannot be prepared. In-flight (already started) transactions are unaffected. The payment stays initiated and the attempt does not draw down the card attempt allowance.
virtual_pos_card_type_not_supported 422 The bank connection backing this terminal does not support this card type; the payment cannot be prepared. In practice you see this most often with foreign-issued cards. The payment stays initiated, but the attempt draws down the card attempt allowance. To see it coming: Installment Options — options comes back empty for an unsupported card type.
payment_declined 422 The bank declined the card while 3D verification was being started (a card-level reason: e.g., an invalid card, or a transaction on a closed card). The payment’s status does not change (it stays initiated); the buyer can try a different card on the same payment. The attempt draws down the card attempt allowance; once it runs out the payment is locked to failed (card_attempt_limit_exceeded).