Skip to content

🤖 Explain with AI

Refunds API

The Refunds API creates full or partial refunds for a captured payment and lists the refunds of that payment.

Base URL

https://api.4geeks.io

Authentication

All endpoints require the X-Api-Key header:

X-Api-Key: sk_test_your_api_key_here

Every refund is checked against the owner of the payment, so a key can only refund its own payments. See Authentication for details.

How a refund is routed

The refund is issued with the credential and the account of the payment’s environment (test or live), so test refunds never touch live money.

  • Destination charges (the money sits in the merchant’s connected account and the platform took its fee): the refund reverses the merchant transfer (reverse_transfer: true) and returns the platform fee (refund_application_fee: true) by default.
  • Direct charges (the charge was created on the merchant’s connected account): the merchant already funds the refund, so only the platform fee is returned. reverse_transfer is not applied.
  • Platform charges (no connected account): no transfer or fee is involved.

The API inspects the charge and only sends the flags that apply, so the request never fails because of an inapplicable Connect flag.


Create Refund

Creates a refund for a payment. Omit amount for a full refund of the remaining amount.

POST /v1/refunds

Headers

Header Required Description
X-Api-Key Yes Merchant API key
Idempotency-Key No Reusing it within 24 hours returns the original refund instead of creating a new one

Request Body

Field Type Required Description
payment_id string Yes The checkout session ID returned by the Payments API, or the payment’s gateway ID
amount integer No Amount to refund in the currency’s minor unit (cents). Defaults to the full remaining amount
reason string No duplicate, fraudulent or requested_by_customer
reverse_transfer boolean No Destination charges only. Default: true
refund_application_fee boolean No Return the platform fee. Default: true

The refundable amount is what was captured minus what was already refunded. Requesting more answers 409 amount_exceeds_refundable and a payment fully refunded answers 409 already_refunded.

Request Example

curl -X POST 'https://api.4geeks.io/v1/refunds' \
  -H 'X-Api-Key: sk_test_your_api_key_here' \
  -H 'Content-Type: application/json' \
  -H 'Idempotency-Key: refund-order-1044' \
  -d '{
    "payment_id": "sess_1a2b3c4d5e6f",
    "amount": 2500,
    "reason": "requested_by_customer"
  }'
import requests

response = requests.post(
    'https://api.4geeks.io/v1/refunds',
    headers={
        'X-Api-Key': 'sk_test_your_api_key_here',
        'Idempotency-Key': 'refund-order-1044'
    },
    json={
        'payment_id': 'sess_1a2b3c4d5e6f',
        'amount': 2500,
        'reason': 'requested_by_customer'
    }
)
print(response.json())
const response = await fetch('https://api.4geeks.io/v1/refunds', {
    method: 'POST',
    headers: {
        'X-Api-Key': 'sk_test_your_api_key_here',
        'Content-Type': 'application/json',
        'Idempotency-Key': 'refund-order-1044'
    },
    body: JSON.stringify({
        payment_id: 'sess_1a2b3c4d5e6f',
        amount: 2500,
        reason: 'requested_by_customer'
    })
});
const data = await response.json();
console.log(data);

Response (200 OK)

Field Type Description
id string Refund ID
object string Object type (refund)
payment_id string Session (or payment) that was refunded
payment_intent_id string Gateway payment intent that was refunded
amount integer Refunded amount in the currency’s minor unit
currency string Currency code
status string Refund status (pending, succeeded, failed, canceled)
reason string Reason sent with the refund
created_at string (datetime) Creation timestamp

Response Example

{
    "id": "ref_m3n4o5p6q7r8",
    "object": "refund",
    "payment_id": "sess_1a2b3c4d5e6f",
    "payment_intent_id": "pi_1a2b3c4d5e6f",
    "amount": 2500,
    "currency": "USD",
    "status": "succeeded",
    "reason": "requested_by_customer",
    "created_at": "2026-11-11T15:04:05Z"
}

Refund a Payment

Convenience route for the same operation, with the payment ID in the path. The body has the same fields as Create Refund except payment_id.

POST /v1/payments/{payment_id}/refund

Request Example

curl -X POST 'https://api.4geeks.io/v1/payments/sess_1a2b3c4d5e6f/refund' \
  -H 'X-Api-Key: sk_test_your_api_key_here' \
  -H 'Content-Type: application/json' \
  -d '{"amount": 2500}'
import requests

response = requests.post(
    'https://api.4geeks.io/v1/payments/sess_1a2b3c4d5e6f/refund',
    headers={'X-Api-Key': 'sk_test_your_api_key_here'},
    json={'amount': 2500}
)
print(response.json())
const response = await fetch('https://api.4geeks.io/v1/payments/sess_1a2b3c4d5e6f/refund', {
    method: 'POST',
    headers: {
        'X-Api-Key': 'sk_test_your_api_key_here',
        'Content-Type': 'application/json'
    },
    body: JSON.stringify({amount: 2500})
});
const data = await response.json();
console.log(data);

Response (200 OK)

Same schema as Create Refund.

Sessions created with the branded checkout

A session created with POST /v1/payments/session then confirmed with POST /v1/payments/intent has no gateway checkout object to look up from the session ID. Pass the payment_intent_id returned by /intent as the path parameter (or as payment_id in POST /v1/refunds).


List Refunds

Lists the refunds of one of the caller’s payments.

GET /v1/refunds

Query Parameters

Parameter Type Required Default Description
payment_intent_id string Yes - Payment intent whose refunds are listed
limit integer No 10 Page size, 1–100
offset integer No 0 Number of results to skip

The payment intent must belong to the merchant that owns the API key; anything else answers 404.

Because the gateway does not expose counts, total_items is a lower bound for this endpoint and has_more tells whether another page exists.

Request Example

curl -X GET 'https://api.4geeks.io/v1/refunds?payment_intent_id=pi_1a2b3c4d5e6f&limit=5' \
  -H 'X-Api-Key: sk_test_your_api_key_here'
import requests

response = requests.get(
    'https://api.4geeks.io/v1/refunds',
    headers={'X-Api-Key': 'sk_test_your_api_key_here'},
    params={'payment_intent_id': 'pi_1a2b3c4d5e6f', 'limit': 5}
)
print(response.json())
const response = await fetch('https://api.4geeks.io/v1/refunds?payment_intent_id=pi_1a2b3c4d5e6f&limit=5', {
    headers: {'X-Api-Key': 'sk_test_your_api_key_here'}
});
const data = await response.json();
console.log(data);

Response (200 OK)

Field Type Description
items array[RefundResponse] Array of refund objects
total_items integer Total number of refunds (lower bound)
limit integer Requested page size
offset integer Requested offset
has_more boolean Whether another page exists

Response Example

{
    "items": [
        {
            "id": "ref_m3n4o5p6q7r8",
            "object": "refund",
            "payment_id": "pi_1a2b3c4d5e6f",
            "payment_intent_id": "pi_1a2b3c4d5e6f",
            "amount": 2500,
            "currency": "USD",
            "status": "succeeded",
            "reason": "requested_by_customer",
            "created_at": "2026-11-11T15:04:05Z"
        }
    ],
    "total_items": 1,
    "limit": 5,
    "offset": 0,
    "has_more": false
}

Errors

Status Meaning
401 Missing or invalid API key
404 The payment does not exist or belongs to another merchant
409 payment_not_refundable, already_refunded, amount_exceeds_refundable, refund_requires_payment_intent, gateway_account_not_configured
422 Validation error (amount, reason, missing payment_id)
429 Rate limit exceeded
502 The gateway rejected the refund

Error Response (422 Validation Error)

{
    "detail": [
        {
            "loc": ["body", "payment_id"],
            "msg": "field required",
            "type": "value_error.missing"
        }
    ]
}

Still have questions? Ask on Discord or explore the tutorials.