Record a cash payment
const url = 'https://terminal.api.example.com/v1/terminal/cash_payments';const options = { method: 'POST', headers: {'Content-Type': 'application/json'}, body: '{"idempotency_key":"sale-2026-10-08-0042","amount":25000,"tendered":30000,"currency":"php"}'};
try { const response = await fetch(url, options); const data = await response.json(); console.log(data);} catch (error) { console.error(error);}curl --request POST \ --url https://terminal.api.example.com/v1/terminal/cash_payments \ --header 'Content-Type: application/json' \ --data '{ "idempotency_key": "sale-2026-10-08-0042", "amount": 25000, "tendered": 30000, "currency": "php" }'Called by a paired terminal, over mutual TLS with its certificate, to record cash it took at the
counter. The merchant and store are the terminal’s assignment, never the body’s. The payment is
already made, so it is recorded as a succeeded payment intent on the terminal’s channel, with the
receipt data to print and the change due. As with every cash payment it is accepted, not yet
deposited: see Accepting cash.
The terminal’s own idempotency_key makes a retry safe. The same key with the same terms answers
the same payment with 200 and records nothing twice; the same key with other terms is refused with
409. Use a new key for each sale.
Authorizations
Section titled “Authorizations”Request Bodyrequired
Section titled “Request Bodyrequired”object
The terminal’s key for this sale.
The price, in the currency’s minor unit.
The cash handed over. At least amount.
A three-letter ISO 4217 code, in either case.
Responses
Section titled “Responses”A sale already recorded under this key with the same terms, answered again. Also the answer to a retry after a 503 when the first call had recorded the sale, even when no answer was ever received for it. The same body as 201.
object
The id of the payment intent made for this payment.
succeeded: the cash was accepted.
The price, in the currency’s minor unit.
The cash the customer handed over.
tendered less amount: the change to give back.
A three-letter ISO 4217 code, in lower case.
What the terminal prints.
object
The payment intent’s id.
A three-letter ISO 4217 code, in lower case.
The store the terminal is assigned to.
Example
{ "object": "terminal_cash_payment", "status": "succeeded", "currency": "php", "receipt": { "description": "Cash accepted", "currency": "php" }}The payment was recorded. Sent with the receipt data.
object
The id of the payment intent made for this payment.
succeeded: the cash was accepted.
The price, in the currency’s minor unit.
The cash the customer handed over.
tendered less amount: the change to give back.
A three-letter ISO 4217 code, in lower case.
What the terminal prints.
object
The payment intent’s id.
A three-letter ISO 4217 code, in lower case.
The store the terminal is assigned to.
Example
{ "object": "terminal_cash_payment", "livemode": true, "payment_intent": "pi_01j9x4c7n2p5r8t1v3w6y9z0b2", "status": "succeeded", "amount": 25000, "tendered": 30000, "change_due": 5000, "currency": "php", "receipt": { "description": "Cash accepted", "reference": "pi_01j9x4c7n2p5r8t1v3w6y9z0b2", "amount": 25000, "tendered": 30000, "change_due": 5000, "currency": "php", "store_label": "Makati branch" }}validation_failure: invalid_parameter for a parameter missing or not valid, named in param; invalid_json for a body that is not JSON; unsupported_media_type for a body that is not sent as JSON; not_acceptable when the request will not accept JSON; method_not_allowed for an HTTP method the path does not take.
object
object
A stable name for this refusal. Branch on this, never on message.
For the developer reading a log. May be reworded.
The field or parameter at fault, when there is one.
Example
{ "error": { "type": "validation_failure", "code": "invalid_parameter", "message": "A whole number of minor units, from 1 to 999999999999999999.", "param": "amount", "request_id": "req_4f1c0b2e9a7d" }}authentication_failure with terminal_credential_refused: the certificate does not count. Pair the terminal again. See Pairing a terminal.
object
object
A stable name for this refusal. Branch on this, never on message.
For the developer reading a log. May be reworded.
The field or parameter at fault, when there is one.
Example
{ "error": { "type": "validation_failure" }}conflict with idempotency_key_reused: this idempotency_key recorded a different sale. Use a new key for a new sale.
object
object
A stable name for this refusal. Branch on this, never on message.
For the developer reading a log. May be reworded.
The field or parameter at fault, when there is one.
Example
{ "error": { "type": "validation_failure" }}policy_rejection: this payment cannot be taken now. code says why:
code |
Meaning |
|---|---|
account_inactive |
Your account is not active. |
not_permitted_for_account |
Your account may not use this method yet. |
method_not_enabled |
The method is not enabled for your account. |
method_not_set_up |
The method is enabled but its setup is not finished. |
method_suspended |
The method is suspended for your account. |
pricing_unavailable |
No price is set for this method and amount. |
payment_method_unavailable |
The method cannot take this payment on this channel yet. |
no_payment_method_available |
None of your methods can take this payment now. |
test_mode_unavailable |
Confirming only: this method cannot be simulated in test mode. |
limit_exceeded |
Confirming only: this payment would take your account past its limit. |
restricted |
Confirming only: payments are restricted for your account, store or terminal at the moment. |
risk_declined |
Confirming only: this payment was declined by a risk check. |
not_available |
Unavailable for another reason. |
object
object
A stable name for this refusal. Branch on this, never on message.
For the developer reading a log. May be reworded.
The field or parameter at fault, when there is one.
Example
{ "error": { "type": "policy_rejection", "code": "method_suspended", "message": "This payment method is suspended for your account.", "param": null, "request_id": "req_4f1c0b2e9a7d" }}dependency_failure with service_unavailable: the platform did not answer, or failed part-way. Whether the sale was recorded is not known; the cash is in the till either way. Retry the same request with the same idempotency_key, and never make a new key for the retry: if the sale was recorded, in whole or in part, you are answered with that payment, with 200, and it is recorded once.
object
object
A stable name for this refusal. Branch on this, never on message.
For the developer reading a log. May be reworded.
The field or parameter at fault, when there is one.
Example
{ "error": { "type": "validation_failure" }}