Accept payments with Kushki One

The Payment API lets you process card-present payments from your POS system to a Kushki ONE terminal running in semi-integrated mode. The terminal handles the entire cardholder interaction and the EMV chip: your system sends the amount and receives the result.

Requirements

How it works

Every endpoint shares the same request and response structure, no matter how you connect your POS to the terminal. The only thing that changes between topologies is the base URL.

TopologyBase URL
Local Network (LAN / Wi-Fi)http://{TERMINAL_IP}:6868/terminal/v1
Cloud (Internet) — UAThttps://uat-cloudt.kushkipagos.com/terminal/v1/{terminalSerial}
Cloud (Internet) — Productionhttps://cloudt.kushkipagos.com/terminal/v1/{terminalSerial}

Sync and async modes

Every operation that requires terminal interaction exists in two variants, under separate path prefixes.

ModePrefixBehaviorWhere the result arrives
Sync/sync/Holds the connection open until the acquirer answersIn the HTTP response
Async/async/Returns immediately with a TERMINAL_ACKNOWLEDGED acknowledgement. Never blocksWebhook only

Available operations

OperationPathSyncAsyncWhat it does
Sale/chargeImmediate charge: authorizes and captures in a single step
Authorization/authorizationReserves funds without capturing them
Capture/captureCharges the funds from an approved authorization
Re-authorization/re_authorizationExtends the amount or the validity of an active authorization
Post-tip/pos_tipAdds a tip to an already-authorized transaction
Void/voidVoids a transaction on the same day, before the cut-off
Refund/refundReturns funds from a settled transaction
Abort/abortCancels an operation in progress on the terminal
Online search/transaction_search_onlineQueries transaction history at the acquirer
Local search/transaction_search_localQueries the history stored on the terminal

Key concepts

client_transaction_id

A UUID v4 identifier that your POS system generates. It works as an idempotency key: if the terminal already processed that identifier, it detects the duplicate and prevents a double charge.

Generate a new one for every sale. Reuse it only when you are retrying the exact same operation after a network failure.

transaction_reference

An identifier that Kushki generates and returns in the response of every approved transaction, inside rawResponse. It is the link between operations in the same lifecycle.

Amount structure

The amount object is sent in the currency’s minor units, as an integer. The number of decimals depends on the country.

Optional sale fields

These fields apply to /charge. The terminal handles the cardholder prompts and ignores the field when the capability is not enabled in the DMS.

FieldTypeWhat it does
amount.tipintegerAdds a tip to the total
cashback_amountintegerCash withdrawal on top of the purchase. Send 0 if it does not apply
query_deferredbooleanWith true, the terminal offers installment options to the cardholder

On /re_authorization you also have omit_card: with true, the operation runs without asking for the card.

Payment flows

Direct sale

The most common retail flow. The terminal activates the reader when it receives the request and waits for the cardholder to complete the interaction.

POST /sync/charge → 200 OK (approved: true)

Pre-authorization and capture

Use this when the final amount is unknown at the time of the interaction: hotels, fuel stations, open-tab restaurants.

POST /sync/authorization → store transaction_reference
↓ (hours or days later)
POST /sync/capture → with the stored transaction_reference

You can insert /re_authorization to raise the reserved amount or extend the window before capturing. Send subtotal_iva0: 0 to extend the validity only.

Authorization validity

Card typeValidity from authorization
Debit (Visa / Mastercard)7 days
Credit (Visa / Mastercard)28 days

The capture can reach up to 110% of the total authorized, adding the authorization and every non-canceled re-authorization. Only one capture is allowed per cycle.

Post-tip

Adds a tip to an already-authorized transaction. Use it when the cardholder decides the amount after the initial charge. Send the value in amount.tip alongside the original transaction_reference.

Void and refund

OperationWhen to use it
Void (/void)The same day as the transaction, before the processor’s cut-off time
Refund (/refund)Once the transaction has settled, or if the cut-off has passed

Authentication

Include these headers with every request:

HeaderValue
AuthorizationHMAC-SHA256 signature of the request body, Base64-encoded, using your Business-Code as the key
timestampUnix timestamp in milliseconds

Direct sale example

A 120.00 COP charge with no tax breakdown.

  • Javascript
  • Python
const payload = {
amount: {
iva: 0, subtotal_iva: 0, subtotal_iva0: 12000,
extra_taxes: { airport_tax: 0, iac: 0, ice: 0, travel_agency: 0 },
},
client_transaction_id: crypto.randomUUID(),
};
const res = await fetch(`${BASE_URL}/sync/charge`, {
method: "POST",
headers: buildHeaders(payload),
body: JSON.stringify(payload),
});
const data = await res.json();
console.log(data.approved, data.rawResponse.transaction_reference);
import uuid, requests
payload = {
"amount": {
"iva": 0, "subtotal_iva": 0, "subtotal_iva0": 12000,
"extra_taxes": {"airport_tax": 0, "iac": 0, "ice": 0, "travel_agency": 0},
},
"client_transaction_id": str(uuid.uuid4()),
}
res = requests.post(f"{BASE_URL}/sync/charge",
headers=build_headers(payload), json=payload)
data = res.json()
print(data["approved"], data["rawResponse"]["transaction_reference"])

Best practices

  • Generate a unique client_transaction_id per sale and reuse it only when retrying the same operation.
  • Persist the transaction_reference as soon as you receive the authorization or charge response.
  • Check that the amount fields add up to the expected total before you send the request.
  • Log the client_transaction_id and the HTTP code of every operation to make reconciliation and support easier.
  • Call /abort only while a transaction is active on the terminal. Calling it afterwards returns 409 Conflict.
  • Sign exactly the same bytes you send: serialize once, sign that string and send that same string.
Async services and webhooks

Integrate async mode: lifecycle states, event structure and the delivery retry policy.

Error catalog

Look up the error models, the codes by category and the corrective actions for every endpoint.