Accept your first payment
Take a card sale over the API against the sandbox, read the result back, then prove your decline handling with an amount the sandbox always refuses.
Signed in, you can run this blueprint against your own sandbox merchant one call at a time, with the values from each call threaded into the next. Sign in to run it.
4 steps, 3 API callsPaymentsTransactions
Samples use {{API_KEY}} for your API key and {{BASE_URL}} for this instance's API address. Anything else in double braces is a value an earlier step gave you.
Set up your sandbox
1. Get an API key for a sandbox merchant
On your side
Create an API key against a sandbox merchant in the application. Every call below sends it as an api-key header. Keep the key out of source control, which is why the samples on this page leave it as a placeholder. A sandbox merchant routes to the loopback processor, so nothing here reaches a card network.
Values this step gives you
Take the payment
2. Create a card sale
API call
POST /api/transactions
Send one request to create and run the sale. The amount below is the sandbox's guaranteed approval, so the simulated card never declines it. If the request is refused instead, the response lists each field your merchant's configuration requires, such as the tax amount and purchase order number of Level 2 data.
Values this step gives you
{{transactionId}}The id of the created transaction, from the response body's id property.
curl -X POST "{{BASE_URL}}/api/transactions" \
-H "api-key: {{API_KEY}}" \
-H "Content-Type: application/json" \
-d '{
"transactionType": "Sale",
"cardData": {
"cardNumber": "4111111111111111",
"nameOnCard": "Jane Doe",
"expirationMonth": 12,
"expirationYear": 2030,
"cvv": 123
},
"invoiceData": {
"amounts": { "base": 10.00, "total": 10.00 }
}
}'using var http = new HttpClient { BaseAddress = new Uri("{{BASE_URL}}") };
http.DefaultRequestHeaders.Add("api-key", "{{API_KEY}}");
var response = await http.PostAsJsonAsync("/api/transactions", new
{
transactionType = "Sale",
cardData = new
{
cardNumber = "4111111111111111",
nameOnCard = "Jane Doe",
expirationMonth = 12,
expirationYear = 2030,
cvv = 123
},
invoiceData = new
{
amounts = new { @base = 10.00m, total = 10.00m }
}
});
response.EnsureSuccessStatusCode();
var created = await response.Content.ReadFromJsonAsync<JsonElement>();
var transactionId = created.GetProperty("id").GetString();What this step answers with
Abridged to the properties this step depends on. A real response carries more.
{
"id": "9f1c2d3e-4b5a-4c7d-8e9f-0a1b2c3d4e5f",
"merchantId": "3a7b1c9d-2e4f-4a6b-8c8d-9e0f1a2b3c4d",
"transactionType": "Sale",
"resultCode": "Ok",
"authorizedAmount": 10.00,
"creationTime": "2026-02-04T18:22:41.517Z",
"responseData": {
"resultCode": "Ok",
"resultMessage": "Approved"
}
}3. Read the transaction back
API call
GET /api/transactions/{{transactionId}}
Read the transaction you just created and check its response data. Build this in from the start. The create response and the stored transaction are the same record. A reconciliation path that trusts only the create response has nowhere to go when a request times out.
Values this step gives you
curl "{{BASE_URL}}/api/transactions/{{transactionId}}" \
-H "api-key: {{API_KEY}}"var transaction = await http.GetFromJsonAsync<JsonElement>(
$"/api/transactions/{transactionId}");What this step answers with
Abridged to the properties this step depends on. A real response carries more.
{
"id": "{{transactionId}}",
"merchantId": "3a7b1c9d-2e4f-4a6b-8c8d-9e0f1a2b3c4d",
"transactionType": "Sale",
"resultCode": "Ok",
"authorizedAmount": 10.00,
"creationTime": "2026-02-04T18:22:41.517Z",
"responseData": {
"resultCode": "Ok",
"resultMessage": "Approved"
}
}Prove your decline handling
4. Make the sandbox decline you
API call
POST /api/transactions
Send the same request with an amount of 10.01. The sandbox refuses it and returns "AUTH DECLINED" in the response body. A refusal is a normal response rather than a transport error, so write that handling now. The testing guide lists every amount the sandbox reacts to.
Values this step gives you
curl -X POST "{{BASE_URL}}/api/transactions" \
-H "api-key: {{API_KEY}}" \
-H "Content-Type: application/json" \
-d '{
"transactionType": "Sale",
"cardData": {
"cardNumber": "4111111111111111",
"nameOnCard": "Jane Doe",
"expirationMonth": 12,
"expirationYear": 2030,
"cvv": 123
},
"invoiceData": {
"amounts": { "base": 10.01, "total": 10.01 }
}
}'What this step answers with
Abridged to the properties this step depends on. A real response carries more.
{
"id": "9f1c2d3e-4b5a-4c7d-8e9f-0a1b2c3d4e5f",
"merchantId": "3a7b1c9d-2e4f-4a6b-8c8d-9e0f1a2b3c4d",
"transactionType": "Sale",
"resultCode": "Decline",
"authorizedAmount": null,
"creationTime": "2026-02-04T18:22:41.517Z",
"responseData": {
"resultCode": "Decline",
"resultMessage": "AUTH DECLINED"
}
}