This page takes you from credentials to an approved test sale. It assumes you can send an HTTP request and read JSON; nothing else.
You need three things:
- A Reefpay account with sandbox access
- Your API key and your client ID — see Authentication
- Something that sends HTTP requests: curl, Postman, or the Try it button below
A sale authorizes and captures in one call. Send it to POST /v1/transactions with type set to SALE.
The example charges 100.00 US dollars to a test Visa card. Amounts are integers in the smallest currency unit, so 10000 is 100.00 USD, and the minimum the API accepts is 50.
Two things about the request are worth noticing before you adapt it.
billing_addressis what drives AVS. Leave it out and the issuer has no address to verify, so you lose a fraud signal. See AVS and CVV.metadatais yours. Up to 50 keys of your own, returned unchanged on every read of the transaction. It is the usual place for your order ID.
An approved sale returns 200 with the transaction.
The approved sale. Because a SALE authorizes and captures in one step, batch_id is already assigned and the transaction settles at the next batch close. reference_transaction_id is null because this is an original transaction, not a capture, void, or refund of another one.
{ "id": "TRANSACTION-01KEW32V6YNV11T33XGDR7TGWC", "reference_transaction_id": null, "type": "SALE", "result": "APPROVED", "client_id": "CLIENT-01KFDKXMQ637EKEAY410MSQSXB", "client_dba": "Acme Jewelry", "integrations": [ "TSYS" ], "response_code": "00", "response_description": "Approved", "batch_id": "BATCH-01KEW32V6YNV11T33XGDR7TGWC", "is_settled": false, "time_created": "2026-07-15T14:22:05Z", "amount": 10000, "tip": 0, "currency": "USD", "payment_method": { "type": "CARD", "truncated_pan": "****-****-****-5439", "payment_token": "PAYMENT_TOKEN-01KFDKXMQ637EKEAY410MSQSXB", "expiry": { … }, "first_name": "John", "last_name": "Doe", "entry_method": "keyed", "auth_code": "123456", "retrieval_reference_number": "000000603076", "card_type": "UNKNOWN", "card_brand": "Visa", "cvv_result_code": "M", "avs_result_code": "Y", "avs_response": "Exact Match - Street address and postal code match" }, "initiator": "CUSTOMER", "network_transaction_id": "MCC1234567890", "metadata": { "order_id": "ORD-10432", "sales_channel": "web", "customer_reference": "cust-8891" } }
The fields you will use most:
| Field | What it tells you |
|---|---|
result | APPROVED, DECLINED, or ERROR. This is the field to branch on |
id | The transaction's identifier, TRANSACTION-…. Quote it to capture, cancel, or refund |
response_code | The processor's own code. 00 is an approval |
response_description | That code in words |
payment_method.auth_code | The issuer's authorization code |
payment_method.payment_token | A token for charging this card again, without holding the number |
payment_method.avs_result_code | How much of the billing address matched |
batch_id | The settlement batch. Null until the transaction reaches one |
A declined sale also returns 200. The HTTP status tells you the request was well formed and reached the issuer. result tells you what the issuer decided.
{
"result": "DECLINED",
"response_code": "51",
"response_description": "Insufficient funds",
"batch_id": null
}So check result, not the status code. Treating any 200 as success is the most common mistake in a first integration. Handle declines covers what to do with each outcome.