Initiate a Refund
Use this endpoint to request a full or partial refund for a successful USD card transaction. Refunds are processed manually and asynchronously.
An accepted request reserves funds and returns HTTP
202. It does not mean that the refund has been completed. Verify the final status or wait for a refund webhook before treating the refund as successful.
Read the Refund Overview first for supported transactions, fees, wallet funding, statuses, and the complete integration flow.
The refund fee is based on your merchant agreement. The USD 1.00 fee in the examples is illustrative; use the fee returned by the API as the applicable fee for each refund.
Endpoint
POST https://backendapi.sayswitchgroup.com/api/v1/refundHeaders
Content-Type: application/json
Authorization: Bearer YOUR_SECRET_KEY
Idempotency-Key: UNIQUE_REFUND_REQUEST_KEYUse the test or live secret key that matches the original transaction. Idempotency-Key is strongly recommended for every refund request.
Request Body
{
"transaction_reference": "SSW_TRANSACTION_REFERENCE",
"amount": "25.00",
"merchant_note": "Customer requested a partial refund"
}| Field | Type | Required | Description |
|---|---|---|---|
transaction_reference | string | Conditional | Original successful transaction reference. Required unless reference is supplied. |
reference | string | Conditional | Alternative name for transaction_reference. |
amount | numeric string or number | No | Amount to return. When omitted, the remaining refundable transaction amount is used. Minimum 0.01; it cannot exceed the remaining refundable amount. |
merchant_note | string | No | Merchant description, up to 2,000 characters. |
idempotency_key | string | No | Body alternative to the Idempotency-Key header, up to 190 characters. |
Do not include callbackUrl in a refund request. Refund status notifications are delivered to your configured webhook URL.
Full Refund
Omit amount to refund the transaction’s entire remaining refundable amount.
curl --request POST \
--url https://backendapi.sayswitchgroup.com/api/v1/refund \
--header "Authorization: Bearer YOUR_SECRET_KEY" \
--header "Content-Type: application/json" \
--header "Idempotency-Key: refund-full-unique-key" \
--data '{
"transaction_reference": "SSW_TRANSACTION_REFERENCE",
"merchant_note": "Customer requested a full refund"
}'Keep your secret key on your server. Do not run this request in frontend JavaScript.
Partial Refund
Include amount to refund part of the remaining refundable amount.
curl --request POST \
--url https://backendapi.sayswitchgroup.com/api/v1/refund \
--header "Authorization: Bearer YOUR_SECRET_KEY" \
--header "Content-Type: application/json" \
--header "Idempotency-Key: refund-partial-unique-key" \
--data '{
"transaction_reference": "SSW_TRANSACTION_REFERENCE",
"amount": "25.00",
"merchant_note": "Customer requested a partial refund"
}'Accepted Responses
Available and Pending Funded
When pending funds are needed, the API returns HTTP 202:
{
"success": true,
"message": "Refund submitted and awaiting settlement funding.",
"data": {
"refund_reference": "REF_2026_001",
"transaction_reference": "USD_TXN_2026_001",
"amount": "100.00",
"fee": "1.00",
"total_reserved": "101.00",
"funding": {
"status": "awaiting_settlement",
"available_reserved": "40.00",
"pending_reserved": "61.00",
"pending_funded_amount": "0.00",
"pending_remaining": "61.00",
"funded_at": null
},
"currency": "USD",
"status": "awaiting_settlement",
"external_reference": null,
"requested_at": "2026-09-28T10:00:00+01:00",
"completed_at": null
}
}An awaiting_settlement refund is accepted, but it is not ready for processing. Do not resubmit it. SaySwitch automatically moves it to pending when settlement has completely funded the reservation.
Fully Available Funded
When available balance covers the full amount and fee, the API returns HTTP 202 with a refund status of pending:
{
"success": true,
"message": "Refund submitted and awaiting processing.",
"data": {
"refund_reference": "REF_2026_002",
"transaction_reference": "USD_TXN_2026_002",
"amount": "100.00",
"fee": "1.00",
"total_reserved": "101.00",
"funding": {
"status": "funded",
"available_reserved": "101.00",
"pending_reserved": "0.00",
"pending_funded_amount": "0.00",
"pending_remaining": "0.00",
"funded_at": "2026-09-28T10:00:00+01:00"
},
"currency": "USD",
"status": "pending"
}
}See Funding Status for descriptions of every field in the funding object.
Idempotent Retry
Send the same Idempotency-Key when retrying a request after a timeout or uncertain response. The API returns the original refund and does not reserve the wallet twice.
curl --request POST \
--url https://backendapi.sayswitchgroup.com/api/v1/refund \
--header "Authorization: Bearer YOUR_SECRET_KEY" \
--header "Content-Type: application/json" \
--header "Idempotency-Key: refund-partial-unique-key" \
--data '{
"transaction_reference": "SSW_TRANSACTION_REFERENCE",
"amount": "25.00",
"merchant_note": "Customer requested a partial refund"
}'An idempotent retry returns HTTP 200:
{
"success": true,
"message": "Refund request already received.",
"data": {
"refund_reference": "REFUND_REFERENCE",
"transaction_reference": "SSW_TRANSACTION_REFERENCE",
"amount": "25.00",
"fee": "1.00",
"total_reserved": "26.00",
"funding": {
"status": "funded",
"available_reserved": "26.00",
"pending_reserved": "0.00",
"pending_funded_amount": "0.00",
"pending_remaining": "0.00",
"funded_at": "2026-09-28T10:00:00+01:00"
},
"currency": "USD",
"status": "pending",
"external_reference": null,
"requested_at": "2026-09-27T12:00:00+01:00",
"completed_at": null
}
}Track a Refund
Use GET /api/v1/refund to retrieve your refunds. Use GET /api/v1/refund/status/{refund_reference} to verify one refund, and listen for refund webhooks. Both endpoints return refunds for the authenticated test or live environment only.
Do not assume that HTTP 202 means success. The Refund Overview explains the complete status and settlement lifecycle.
Validation and Eligibility Errors
Transaction Not Found
HTTP 404:
{
"success": false,
"message": "Transaction not found."
}Transaction Is Not Successful
HTTP 422:
{
"success": false,
"message": "Only successful transactions can be refunded."
}Non-Card Transaction
HTTP 422:
{
"success": false,
"message": "Only card transactions can currently be refunded."
}Unsupported Currency
HTTP 422:
{
"success": false,
"message": "Refunds are not currently supported for NGN transactions."
}Amount Exceeds the Remaining Refundable Amount
HTTP 422:
{
"success": false,
"message": "Refund amount exceeds the remaining refundable amount."
}Insufficient Eligible Funds
HTTP 422:
{
"success": false,
"message": "Insufficient funds to cover the refund amount and fee.",
"required": "101.00",
"available_balance": "20.00",
"pending_balance": "50.00",
"shortfall": "31.00",
"currency": "USD"
}pending_balance is the eligible pending balance after existing refund holds, not necessarily the gross pending balance. If available balance plus eligible pending balance cannot cover the refund and fee, the request is rejected and no funds are deducted or reserved.
Transaction Already Fully Refunded
HTTP 409:
{
"success": false,
"message": "This transaction has already been fully refunded."
}Invalid Request Fields
HTTP 422:
{
"success": false,
"message": "Invalid refund request.",
"errors": {
"transaction_reference": [
"The transaction reference field is required when reference is not present."
]
}
}Encrypted S2S Merchants
Merchants without full payload encryption send the normal JSON request shown above. If S2S payload encryption is enabled for your account, encrypt the complete inner refund payload using your existing SaySwitch encryption flow.
The decrypted inner payload can contain:
{
"transaction_reference": "SSW_TRANSACTION_REFERENCE",
"amount": "25.00",
"merchant_note": "Customer requested a partial refund",
"idempotency_key": "refund-encrypted-unique-key"
}Send the encrypted value in the outer request body:
{
"data": "ENCRYPTED_PAYLOAD"
}Encrypted merchants receive an encrypted response and must decrypt it using their existing response-decryption process. The refund GET endpoints do not require an encrypted request body, but their responses remain encrypted when encryption is enabled.
Encrypted Request Examples
curl --request POST \
--url https://backendapi.sayswitchgroup.com/api/v1/refund \
--header "Authorization: Bearer YOUR_SECRET_KEY" \
--header "Content-Type: application/json" \
--header "Idempotency-Key: refund-encrypted-unique-key" \
--data '{
"data": "ENCRYPTED_PAYLOAD"
}'The encryption helper names above represent your existing implementation. Follow API Payload Encryption for the configured encryption and response-decryption process.
Never include raw card numbers, CVVs, PINs, or other card information in a refund request.