This documentation is in early access. Parameters, response structures and behaviors may change without prior notice. Do not use this version in production without coordinating with the Kushki integrations team first.
Kushki ONE Connect returns a structured JSON payload on any failure, whether the problem started at the terminal, in a validation, in authentication or at the acquirer. That lets you build robust integrations, automate recovery flows and stop depending solely on HTTP status codes.This manual is your diagnostic tool: the data model structure, the catalogs by category, how we map third-party rejections and a quick guide for the common cases.
Every error response shares this structure. Evaluate the type field first to classify the source of the failure, then look up the matching code.
Field
Type
Always present
Description
type
string
✅
Error category. Identifies the source of the failure. See Section 2
code
string
✅
Prefix plus number for Kushki ONE's own families (PAR-003, TER-004, AUTH-001, NF-001, CONF-4007, MAN-30005). Acquirer codes are the processor's own, unprefixed
param
string
—
The exact request field that caused the error. Present only when the failure is attributable to a field — see below
message
string
✅
Human-readable description of the failure
object
object
—
Traceability data: client_transaction_id, terminal_id and serial_number where applicable
On payment endpoints the HTTP status is always 200, whatever happened: it confirms that the terminal processed your request, not that the operation succeeded. Always evaluate the body. The print endpoints are the exception and return real codes — 404 when the job does not exist, 409 while the queue is busy.The five fields above sit at the root of the body. The two envelopes older builds returned, {"success": false, "data": {…}} and {"failure": {…}}, no longer exist. And object.terminal_id may arrive empty on TER-003 — do not depend on it.
{"type":"PARAMETER","code":"PAR-003","param":"amount","message":"Amount must be a value greater than 0","object":{"client_transaction_id":"1018282","terminal_id":"172653","serial_number":"PJ715652"}}
When you integrate over async mode, failures do not arrive in an HTTP response but inside the webhook event, in the failure_reason object.
Field
Type
Description
type
string
Error category, with the same values as Section 2
code
string
Unique error code
message
string
Human-readable description of the failure
{"status":"TERMINAL_REJECTED","previous_status":"CARD_PRESENTED","client_transaction_id":"c5a3f3be-9d6f-4d39-8af5-58dbb589af79","failure_reason":{"type":"TERMINAL","code":"TER-172","message":"The maximum operation time was exceeded."}}
failure_reason appears in two states only
The object arrives only in TERMINAL_REJECTED and DECLINED events. It is not present in any other lifecycle state. Read Async services and webhooks for the details of the state machine.
Differences from LinkFailure
failure_reason carries no param and no object. Traceability comes from the event's own fields: client_transaction_id, terminal.serial_number and merchant_id.
param is not an optional field that happens to be missing sometimes. It is omitted deliberately whenever the error was not caused by a field of your request. The rule in one line: param appears only when the error is attributable to a request field.
Comes with param
Why
PAR-003, TER-003, AUTH-001
Data validation errors — there is a field at fault
NF-001
Carries the identifier that was not found
Comes without param
Why
TER-004
Printer busy, or the cardholder cancelled. The request was valid; the hardware or the person was not
MAN-30005
Card or PIN timeout. Same reason
ACQUIRER
The transaction was valid and the bank declined it
CONF-4007
Terminal provisioning, not the call
PAR-002 with unreadable JSON
The parser fails before deserializing, so there is no field to point at
Without this rule an integrator reads the catalog, sees param among the fields, and codes assuming it always arrives.
These are generated when the request contains fields with the wrong format, values out of range or missing required fields. The param field identifies the specific field. All of them are preventable by validating the payload in your POS system before you call the API.
code
Description
Message template
Example
PAR-001
Required field not sent
Field {{field}} is required
Field amount.subtotal_iva0 is required
PAR-002
Field of the wrong type
Field {{field}} must be a/an {{type}}
Field amount.subtotal_iva must be an integer
PAR-003
Wrong format or invalid value
Field {{field}} must satisfy: {{condition}}
Field amount.iva must satisfy: value >= 0
When type is PARAMETER, the code can also be a numeric code in the 2002 to 2021 range, plus the BAD_FORMAT format code.
JSON parsing error. The request body is not valid JSON or contains a wrong data type
There is no BUSY code
A busy terminal is TER-003 when it is running a transaction and TER-004 when it is printing — both with type: TERMINAL. Over HTTP a busy print queue arrives as a 409.
/charge and /authorization, plus /pos_tip where the tip is the amount
2006
amount.extra_taxes.airport_tax
amount.extra_taxes.airport_tax cannot be negative
All
2007
amount.extra_taxes.iac
amount.extra_taxes.iac cannot be negative
All
2008
amount.extra_taxes.ice
amount.extra_taxes.ice cannot be negative
All
2009
amount.extra_taxes.travel_agency
amount.extra_taxes.travel_agency cannot be negative
All
Currency does not travel in the request
The amount object carries no currency field: the terminal resolves the currency from its own configuration. There is no validator for it — codes that used to check amount.currency, isDeferred and isCashback were withdrawn because none of those fields is ever sent.
Amounts are integers
Errors 2002 through 2009 are frequently triggered by sending amounts with decimals instead of integers in the currency's smallest unit. Read Amount format in Kushki ONE before you start debugging the payload.
Installments travel in the deferred object in every market except Mexico, which uses the boolean query_deferred. The two are mutually exclusive and never travel together. These validations have no numeric code assigned yet:
Condition
Result
deferred.months out of range for the country
PARAMETER error on deferred.months
deferred.credit_type sent outside Chile
PARAMETER error — credit_type exists only in Chile
deferred and query_deferred sent together
PARAMETER error — the two are mutually exclusive
query_deferred sent outside Mexico
PARAMETER error — the field is Mexico only
The ceiling depends on the market: Mexico sends query_deferred as a boolean, Chile allows up to 12 months with credit_type: "03" or up to 48 without it, Colombia up to 48, and Peru 2 to 48 for every card network. The full contract, country by country, is in Accept payments with Kushki One.
These are generated when the physical terminal cannot accept the operation. Unlike hardware errors, they point to a state or connectivity problem, not a component failure.
code
Description
Message template
TER-001
Terminal not linked to the merchant account
Terminal {{serial_number}} does not exist, or is not linked to your account
TER-002
Terminal not responding: offline or off the network
Terminal {{serial_number}} is not responding. It may be offline or not connected to the local network
TER-003
Terminal busy processing a transaction
Terminal {{serial_number}} is busy processing transaction (client_transaction_id: {{id}})
TER-004
Printer busy, or cardholder cancellation. See the section on this code below
Terminal {{serial_number}} is busy executing action {{action}}
TER-005
Terminal in a mode incompatible with the operation
The terminal is in {{mode}} mode and does not allow the requested action
These are generated when credentials are invalid or do not carry enough permissions for the requested action.
code
Description
Message
AUTH-001
Credentials cannot be authenticated
Invalid or expired credentials
AUTH-002
Valid credentials without permission for the resource
Your credentials are valid but do not grant access to this resource
{"type":"AUTH","code":"AUTH-001","message":"Invalid or expired credentials"}
AUTH-001 only in production, on Local Network or Localhost
If the integration passed certification in UAT and fails in production with AUTH-001, check that you send the header encrypted_http_communication: true on every request, in UAT too. Details in Authentication and request encryption.
Never log the full signature
Do not write the value of the Authorization header into your production logs. Log only its presence or absence for diagnostics.
Errors from the Kushki acquirer are wrapped under type: "ACQUIRER" with this mapping rule:
code: the acquirer's native code, unchanged and unprefixed. E020 is returned as E020. Authorization codes are native to the processing network and are left intact to preserve the ISO 8583 traceability the card brands require — do not expect an ACQ- prefix, it never arrives.
message: identical to Kushki's original message.
object: includes client_transaction_id, terminal_id and serial_number where applicable.
HTTP status: we keep the one Kushki originally returned.
Original Kushki error:
{"code":"E020","message":"Either Invalid amount or Currency conversion field overflow"}
Error mapped by Kushki ONE Connect, with the same 400 Bad Request:
{"type":"ACQUIRER","code":"E020","param":"","message":"Either Invalid amount or Currency conversion field overflow","object":{"client_transaction_id":"1018282","terminal_id":"172653","serial_number":"PJ715652"}}
Errors from the manufacturer SDK are wrapped with the MAN- prefix — a single hyphen — followed by the number. The code confirmed in real responses is MAN-30005: the card read or the PIN entry timed out, which you treat as an expected cancellation rather than a failure. For the full Sunmi catalog by functional range, ask your Kushki integration team.
These are generated when the operation is not enabled for that terminal, or when the amount exceeds the configured limits. Unlike PARAMETER errors, they are not fixed by changing the payload.
{"type":"CONFIGURATION","code":"CONF-4001","message":"Tip is not enabled","object":{"client_transaction_id":"c5a3f3be-9d6f-4d39-8af5-58dbb589af69","terminal_id":"172653","serial_number":"SN816265"}}
These errors are not solved in code
A CONFIGURATION error means the capability exists but is not active for that terminal. The terminal ignores fields for disabled capabilities, so this error appears when the capability is partially configured or when its limits are exceeded. To have a capability or a limit changed, write to soporte@kushkipagos.com.
Check that you sign with the Business-Code and that the timestamp unit is correct. In Local Network and Localhost, also check that you send the header encrypted_http_communication: true
AUTH
AUTH-002
Credentials are valid but permissions are missing. Contact integrations
PARAMETER
any
Check the field named in param. Add validation in your POS before calling the API
PARAMETER
2002 to 2009
You most likely sent amounts with decimals instead of integers
INTERNAL
MQTT_REQUEST_FAILED
The Cloud relay could not reach the terminal. Check it is online and retry. Cloud only
TERMINAL
TER-002
The terminal is offline. Check network connectivity
TERMINAL
TER-003
The terminal is busy with a transaction. Wait or use abort
TERMINAL
TER-004
On a print endpoint the printer is busy: retry. On a payment endpoint the cardholder cancelled: do not retry
Log the full payload and escalate to Kushki technical support
What to always log
Store type, code, message, object.client_transaction_id and the timestamp in your logging system. Without those five data points, a diagnosis that takes minutes can take days.
To debug a signature failure, this is the minimum logging pattern:
JavaScript
Python
Request authentication
Resolve AUTH errors by reviewing the two signing mechanisms and their differences.
Async services and webhooks
Interpret the failure_reason object inside the transaction lifecycle.