Payment references
SET API · platform keys · Live since 2026-11-12
A payment reference tells SET which order a payment is for. Your customer has one Cash-Out Address for each chain and stablecoin, and anyone can pay it from any wallet, including a payer SET has never seen. A reference reserves one order's amount on that address. When a payment of that amount arrives, SET settles the reference and tells you which order was paid.
A merchant key gets 403 payment_references_platform_only. A business paid for its own goods issues an invoice instead.
The calls
| Call | Scope | What it does |
|---|---|---|
POST /v1/payment_references | write | Takes {cashOutAddress, amount, expiresAt, payer?, metadata?} and answers payableAmount and reference. reference is the same as id; quote it on your order |
GET /v1/payment_references | read | Every reference, paged like every list. Filters: customer, state, cashOutAddress, cashOutPayment |
GET /v1/payment_references/{id} | read | One reference |
POST /v1/payment_references/{id}/cancel | write | Open references only. Frees the payable amount |
POST /v1/payment_references/{id}/match | write | {cashOutPayment}: your own match, for an open or expired reference |
amountis the order total, with at most two decimals, and at least 2.00 (400 amount_too_smallbelow it).expiresAtfalls within the next 30 days.payertakes the same body asPOST /v1/cash_out_payments/{id}/sender. SET seals it at rest and never shows it again.- A business or customer under a restriction gets
403 restrictedwhen it creates a reference or matches one by hand. It can still read, list and cancel. A payment that reaches the address is still recorded, and held under the restriction.
The amount to ask for
payableAmount is the order amount plus a tag between 0.000001 and 0.009999. The tag is unique among the address's open references and is the same kind of tag an invoice carries. Ask the payer for payableAmount exactly. The tag is under a cent, and the payer pays it.
If one address has too many open references of the same amount, SET answers 409 payable_amount_exhausted. Cancel or settle some first.
How a payment is matched
SET reads, once, the amount the chain shows reaching the address and compares it with the address's open references:
| The payment is | The reference becomes | Rule |
|---|---|---|
Exactly a reference's payableAmount | matched | exact |
At least the order's amount and less than a cent above it, and only one open reference fits | underpaid (with shortBy) below the payable amount, overpaid (with overBy) above it | cents |
| Anything else | No reference is settled |
If two references of the same amount both fit a payment by cents, SET picks neither. The payment stays as it is and you can match it yourself. A payment whose amount the chain cannot give yet is tried again on its next event.
Matching only labels the money. Whatever reaches the address is paid to your customer's bank either way.
The Travel Rule
When you give a payer at creation, SET sends it as the payment's sender, as it does for an invoice's payer. In the sandbox this lifts an unknown_sender hold.
If you gave no payer, the reference settles only a payment whose sender is already named. Otherwise it stays open: name the sender with POST /v1/cash_out_payments/{id}/sender, then match the payment yourself. A manual match with neither a payer nor a named sender answers 409 sender_required.
Whether the payer reached the provider
A settled reference shows senderState, the settling Cash-Out payment's own Travel Rule state (as GET /v1/cash_out_payments/{id} shows it): none, submitted, failed or simulated, and in the sandbox unknown_sender while a payment is held. It is null until a payment settles the reference.
- Refused. When the provider refuses the payer's details, the reference reads
senderState: "failed"andnextnames the step:POST /v1/cash_out_payments/{id}/senderwith the details corrected. Once they are accepted, the reference readssubmitted. The match stands either way: matching labels the money, and the provider pays what reached the address. - Events.
payment_reference.matched,.underpaidand.overpaidare sent after the payer was sent, so each carries thesenderStatethat sending left. A later correction changes the reference and the payment, and sends no new reference event.
GET /v1/capabilities lists payment references as a compliance control, compliance.controls.paymentReferences, with its routes and events.
Where you see it
- On the payment.
GET /v1/cash_out_paymentsandGET /v1/cash_out_payments/{id}carrypaymentReference: the reference the payment settled, or null. - Events.
payment_reference.created,.matched,.underpaid,.overpaid,.expiredand.canceled. Each carries the reference asGETshows it, never the payer. - Expiry. Open references past
expiresAtexpire within about ten minutes, and aGETexpires one it reads late. A payment of an expired reference's amount is not matched by itself; match it yourself if it is still the order's.
Try it in the sandbox
POST /v1/sandbox/unknown_sender {cashOutAddress, amount, senderAddress} simulates a payment from a wallet SET has never seen. It runs the match, so a reference with a payer names that payer and lifts the hold.
Not yet
- Conversion Addresses.
conversionAddressanswers400 conversion_address_not_supported. - A separate address for each order.