Refund all or part of a payment, never more than is left. Every refund is listed here however it was made (the dashboard, the API, an approved request), each with its own id (re_…). The money goes back to the card or account that paid. For the full pattern with retries and approvals, see Issue refunds safely.
| Topic | Detail |
|---|---|
| Endpoints | `POST /api/v1/refunds`, `GET /api/v1/refunds`, `GET /api/v1/refunds/{id}` |
| Key permissions | refunds:write, refunds:read |
| Samples | curl; Node.js 18+ with the built-in fetch (in an ES module); the Node SDK (preview: not on npm yet); Python with requests. |
| Every request can also answer | 401 UNAUTHORIZED, 401 API_KEY_EXPIRED, 403 NOT_ENROLLED, 403 PERMISSION_DENIED, 403 IP_NOT_ALLOWED, 429 RATE_LIMITED, 500 INTERNAL_ERROR. See Error codes. |
The refund object
{
"object": "refund",
"livemode": true,
"id": "re_8812",
"paymentId": "pay_7Hq2LmX9cR4tVb",
"amountCents": 2500,
"currency": "usd",
"reason": "Requested by customer",
"status": "succeeded",
"failureReason": null,
"refundId": null,
"createdAt": "2026-10-20T09:12:44.000Z"
}| Field | Type | Description |
|---|---|---|
object | string | The type of object. Always refund. |
livemode | boolean | False for test-mode data (an ak_test_ key); true for live data. |
id | string | re_… for a refund; re_appr_… for one that needed an owner's approval. |
paymentId | string | The refunded payment, pay_…. |
amountCents | integer | Refunded (or to be refunded). Integer cents. |
currency | string | usd. |
reason | string or null | Why, as recorded: a preset (requested_by_customer, duplicate, fraudulent, other) shown as words, your own note, or both. |
status | string | succeeded; pending (sent, not settled yet); pending_approval (waiting for an owner: nothing refunded yet); or failed. |
failureReason | string or null | One of approval_declined, failed, canceled. |
refundId | string or null | For an approved re_appr_…: the refund it became, re_…. |
createdAt | string (date-time) | When it was made or asked for. |
Endpoints
Refund a payment
/api/v1/refundsrefunds:writeSupports Idempotency-Key.Refund all or part of a payment, never more than is left. Over the business's refund approval limit nothing is refunded: the answer is 202 with status pending_approval while an owner decides. Answers 201 with the refund, or 202 with status: "pending_approval" and an id starting re_appr_ when it is over your business's refund approval limit. The Idempotency-Key also keys the refund at the payments network, so a retry after a lost response never refunds twice.
Headers
Idempotency-Keystring- Recommended. A unique key such as a UUID: a retry with the same key within 24 hours gets the first response back instead of acting twice. See Idempotency.
Body
paymentIdstringrequired- The payment to refund,
pay_…. amountCentsinteger- How much, in cents. Leave out to refund everything still left. At least 1 ($0.01).
reasonstring- requested_by_customer, duplicate, fraudulent, other; or your own words. Up to 500 characters.
notestring- Added to a preset reason. Up to 500 characters.
curl -X POST https://anytime-capital.com/api/v1/refunds \
-H "Authorization: Bearer $ANYTIME_API_KEY" \
-H "Idempotency-Key: refund-order-1042" \
-H "Content-Type: application/json" \
-d '{
"paymentId": "pay_7Hq2LmX9cR4tVb",
"amountCents": 2500,
"reason": "requested_by_customer"
}'const res = await fetch("https://anytime-capital.com/api/v1/refunds", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.ANYTIME_API_KEY}`,
"Content-Type": "application/json",
"Idempotency-Key": "refund-order-1042",
},
body: JSON.stringify({
paymentId: "pay_7Hq2LmX9cR4tVb",
amountCents: 2500,
reason: "requested_by_customer",
}),
});
const refund = await res.json();
if (!res.ok) throw new Error(`${refund.error.code}: ${refund.error.message}`);import { AnytimeCapital } from "@anytimecapital/node";
const client = new AnytimeCapital({ apiKey: process.env.ANYTIME_API_KEY! });
const refund = await client.refunds.create(
{
paymentId: "pay_7Hq2LmX9cR4tVb",
amountCents: 2500,
reason: "requested_by_customer",
},
{ idempotencyKey: "refund-order-1042" },
);import os
import requests
res = requests.post(
"https://anytime-capital.com/api/v1/refunds",
headers={
"Authorization": f"Bearer {os.environ['ANYTIME_API_KEY']}",
"Idempotency-Key": "refund-order-1042",
},
json={
"paymentId": "pay_7Hq2LmX9cR4tVb",
"amountCents": 2500,
"reason": "requested_by_customer",
},
timeout=30,
)
refund = res.json()
if not res.ok:
raise RuntimeError(f"{refund['error']['code']}: {refund['error']['message']}"){
"object": "refund",
"livemode": true,
"id": "re_8812",
"paymentId": "pay_7Hq2LmX9cR4tVb",
"amountCents": 2500,
"currency": "usd",
"reason": "Requested by customer",
"status": "succeeded",
"failureReason": null,
"refundId": null,
"createdAt": "2026-10-20T09:12:44.000Z"
}Errors
- 400
INVALID_JSON - The body isn't valid JSON, or couldn't be read. An empty body reads as
{}. SendContent-Type: application/jsonand a JSON object. - 400
INVALID_INPUT - A field, filter,
limitorcursoris missing or invalid. The message starts with the field's name, as inamountCents: …, or readsInvalid cursor.Fix the named field. Don't retry the same request unchanged. - 400
INVALID_IDEMPOTENCY_KEY - The
Idempotency-Keyheader is empty, longer than 255 characters, or has characters other than printable ASCII. Use a UUID. See Idempotency. - 404
NOT_FOUND - No such object on your business (an id from another business reads the same), or no such endpoint under
/api/v1, which still answers in JSON. Check the id and the path. Ids are case-sensitive. - 409
NOT_REFUNDABLE - The payment can't be refunded in its current state, or it was paid in stablecoins, which settle straight to your wallet. Check the payment's
statusandamountRefundedCentswithGET /api/v1/payments/{id}. Refund a stablecoin payment by sending the tokens back from your wallet. - 409
REFUND_PENDING_APPROVAL - Another refund on this payment is already waiting for an owner's approval. Wait for the owner's decision:
GET /api/v1/refunds?paymentId=…&status=pending_approvalshows it. - 422
IDEMPOTENCY_KEY_REUSED - The
Idempotency-Keywas used in the last 24 hours with a different body or endpoint. Use a new key for a new request; reuse a key only to retry the same request. - 500
REFUND_NOT_RECORDED - The refund was sent, but our record of it isn't there yet. Don't refund again. List
GET /api/v1/refunds?paymentId=…in a minute to find it. - 502
PROCESSOR_ERROR - The payments network didn't answer in time. Retry with the same
Idempotency-Key. A refund is keyed at the network by that key too, so a retry never refunds twice. - 503
NOT_CONFIGURED - The part of Anytime Capital Payments this needs isn't available right now. Retry later, with the same
Idempotency-Keyon a write.
List refunds
/api/v1/refundsrefunds:readRefunds however they were made, newest first. status=pending_approval lists refunds waiting for an owner's approval instead.
Query
paymentIdstring- Only refunds of this payment.
statusstringsucceeded,pendingorfailed; orpending_approvalfor the refunds waiting for an owner's approval (a separate list: its cursor works only with the same filter).limitinteger- How many objects per page, 1–100. Default 25. See Pagination.
cursorstring- The previous page's
nextCursor, for the page after it.
curl "https://anytime-capital.com/api/v1/refunds?paymentId=pay_7Hq2LmX9cR4tVb" \
-H "Authorization: Bearer $ANYTIME_API_KEY"const res = await fetch("https://anytime-capital.com/api/v1/refunds?paymentId=pay_7Hq2LmX9cR4tVb", {
headers: { Authorization: `Bearer ${process.env.ANYTIME_API_KEY}` },
});
const page = await res.json();
if (!res.ok) throw new Error(`${page.error.code}: ${page.error.message}`);import { AnytimeCapital } from "@anytimecapital/node";
const client = new AnytimeCapital({ apiKey: process.env.ANYTIME_API_KEY! });
const page = await client.refunds.list({ paymentId: "pay_7Hq2LmX9cR4tVb" });import os
import requests
res = requests.get(
"https://anytime-capital.com/api/v1/refunds",
headers={"Authorization": f"Bearer {os.environ['ANYTIME_API_KEY']}"},
params={"paymentId": "pay_7Hq2LmX9cR4tVb"},
timeout=30,
)
page = res.json()
if not res.ok:
raise RuntimeError(f"{page['error']['code']}: {page['error']['message']}"){
"object": "list",
"data": [
{
"object": "refund",
"livemode": true,
"id": "re_8812",
"paymentId": "pay_7Hq2LmX9cR4tVb",
"amountCents": 2500,
"currency": "usd",
"reason": "Requested by customer",
"status": "succeeded",
"failureReason": null,
"refundId": null,
"createdAt": "2026-10-20T09:12:44.000Z"
}
],
"hasMore": false,
"nextCursor": null
}Errors
- 400
INVALID_INPUT - A field, filter,
limitorcursoris missing or invalid. The message starts with the field's name, as inamountCents: …, or readsInvalid cursor.Fix the named field. Don't retry the same request unchanged.
Get a refund
/api/v1/refunds/{id}refunds:readOne refund (re_…) or one that needed approval (re_appr_…). For a re_appr_… id: refundId names the refund it became once an owner approved it; a declined one is failed with failureReason: "approval_declined".
Path
idstringrequiredre_…orre_appr_….
curl https://anytime-capital.com/api/v1/refunds/re_8812 \
-H "Authorization: Bearer $ANYTIME_API_KEY"const res = await fetch("https://anytime-capital.com/api/v1/refunds/re_8812", {
headers: { Authorization: `Bearer ${process.env.ANYTIME_API_KEY}` },
});
const refund = await res.json();
if (!res.ok) throw new Error(`${refund.error.code}: ${refund.error.message}`);import { AnytimeCapital } from "@anytimecapital/node";
const client = new AnytimeCapital({ apiKey: process.env.ANYTIME_API_KEY! });
const refund = await client.refunds.retrieve("re_8812");import os
import requests
res = requests.get(
"https://anytime-capital.com/api/v1/refunds/re_8812",
headers={"Authorization": f"Bearer {os.environ['ANYTIME_API_KEY']}"},
timeout=30,
)
refund = res.json()
if not res.ok:
raise RuntimeError(f"{refund['error']['code']}: {refund['error']['message']}"){
"object": "refund",
"livemode": true,
"id": "re_8812",
"paymentId": "pay_7Hq2LmX9cR4tVb",
"amountCents": 2500,
"currency": "usd",
"reason": "Requested by customer",
"status": "succeeded",
"failureReason": null,
"refundId": null,
"createdAt": "2026-10-20T09:12:44.000Z"
}Errors
- 404
NOT_FOUND - No such object on your business (an id from another business reads the same), or no such endpoint under
/api/v1, which still answers in JSON. Check the id and the path. Ids are case-sensitive.
{
"object": "refund",
"livemode": true,
"id": "re_appr_57",
"paymentId": "pay_7Hq2LmX9cR4tVb",
"amountCents": 75000,
"currency": "usd",
"reason": "Requested by customer",
"status": "pending_approval",
"failureReason": null,
"refundId": null,
"createdAt": "2026-10-20T09:12:44.000Z"
}Events
refund.created when a refund is made, then refund.succeeded or refund.failed; the payment's own payment.refunded carries amountRefundedCents, the total refunded so far. See Webhooks.