/api/v1/refundsUse this endpoint when a customer is refunded for a payment that was originally attributed to an affiliate. Anderro will locate the matching commission and either void it (full refund) or proportionally reduce it (partial refund), and emit a commission.refunded webhook so your systems stay in sync.
Send an idempotency key to safely retry a refund. Without a key, partial refunds are incremental: each request deducts the specified amount again. A commission that has already been fully refunded returns 409.
Identifying the payment
You must provide exactly one identifier strategy. Pick the strongest one you have:
| Parameter | Type | Description |
|---|---|---|
commission_id | string | Anderro's internal commission id. Cleanest pointer - get it from GET /api/v1/commissions and store it alongside your order in your database. |
external_id | string | Your own transaction id (Stripe charge id, Paddle transaction id, internal order id). Recommended for production integrations - the merchant already has this id without any new storage. |
email | string | Customer email. Fallback heuristic; must be combined with payment_amount_cents to disambiguate multiple payments from the same customer. |
payment_amount_cents | integer | Required when identifying by email. Must exactly match the original payment amount. |
Email + payment_amount_cents searches live payments newest first after trimming and lowercasing the email. It uses the newest matching payment only when the match is unique; several matches return 409 ambiguous_payment. Use external_id or commission_id to resolve ambiguity.
Optional parameters
| Parameter | Type | Description |
|---|---|---|
refund_amount_cents | integer | Incremental refund delta in cents, not the cumulative refunded total. Omitted = full refund. Smaller value = partial refund (commission scaled proportionally, status unchanged). Larger than the original payment returns 422. |
reason | string | Free-form reason (max 500 chars). Stored on the commission and forwarded to the affiliate in the notification email. |
idempotency_key | string | Unique key for this refund, 1–128 characters. Also accepted in the Idempotency-Key header. Reuse it when retrying the same request. |
Keys are scoped to your product and retained for 30 days. Reusing a key with the same JSON body returns the original response body and status without applying another refund. Reusing it with a different body returns 409. JSON field order does not matter, and the key can move between the body and header. If both are supplied, they must match. After 30 days, the key can be used for a new refund. Validation errors do not reserve a key.
Example requests
Full refund via commission id (recommended):
curl -X POST https://anderro.com/api/v1/refunds \
-H "Content-Type: application/json" \
-H "x-api-key: sk_your_secret_key" \
-d '{
"commission_id": "comm_abc123",
"reason": "Customer requested refund within trial period"
}'
Full refund via Stripe charge id:
curl -X POST https://anderro.com/api/v1/refunds \
-H "Content-Type: application/json" \
-H "x-api-key: sk_your_secret_key" \
-d '{
"external_id": "ch_3NqLm12abcdef",
"reason": "Chargeback"
}'
Partial refund:
curl -X POST https://anderro.com/api/v1/refunds \
-H "Content-Type: application/json" \
-H "x-api-key: sk_your_secret_key" \
-d '{
"external_id": "ch_3NqLm12abcdef",
"refund_amount_cents": 1500,
"idempotency_key": "refund_order_123_partial_1",
"reason": "Pro-rata refund for canceled mid-month"
}'
Fallback via email + amount:
curl -X POST https://anderro.com/api/v1/refunds \
-H "Content-Type: application/json" \
-H "x-api-key: sk_your_secret_key" \
-d '{
"email": "[email protected]",
"payment_amount_cents": 4900
}'
Responses
{
"data": {
"commission_id": "comm_abc123",
"partnership_id": "p_xyz789",
"payment_amount_cents": 4900,
"refund_amount_cents": 4900,
"previous_commission_cents": 980,
"new_commission_cents": 0,
"status": "refunded",
"fully_refunded": true
}
}
new_commission_cents is what the commission is now worth after the refund (zero on a full refund, proportionally reduced on a partial refund). status is the new commission status - refunded on a full refund, unchanged (pending / approved / paid) on a partial refund.
{
"error": "Validation failed",
"details": {
"fieldErrors": {},
"formErrors": ["Provide one of: commission_id, external_id, or (email + payment_amount_cents)"]
}
}
{
"error": "No payment matching that identifier was found for this product"
}
{
"error": "Commission already fully refunded",
"details": {
"commission_id": "comm_abc123"
}
}
Email and amount must identify exactly one live payment. If multiple payments match, the response is:
{
"error": "ambiguous_payment",
"details": {"message": "Provide external_id or commission_id"}
}
Reusing an idempotency key with a different request body also returns 409, with error set to Idempotency key was already used with a different body.
{
"error": "refund_amount_cents must be > 0 and <= the original payment",
"details": {
"payment_amount_cents": 4900
}
}
How refunds affect commissions
The effect of a refund depends on the commission's current status:
| Current status | Effect of full refund |
|---|---|
pending (in hold period) | Status → refunded, amount zeroed. No money changes hands. |
approved (ready for payout) | Reduced in place: status → refunded, amount zeroed. Any open payout is recalculated; no negative adjustment is created. |
paid (already paid out) | Status → refunded, amount zeroed. A negative adjustment reduces an existing pending, approved, or failed payout; otherwise it is netted into the next payout. Processing and completed payouts remain unchanged. The affiliate is notified by email. |
rejected | Already voided; refund is a no-op. |
Only commissions that were paid create negative refund adjustments. Unapplied adjustments appear as adjustment_cents on payout.created, whose amount_cents is already net of those adjustments. Do not also invoice the affiliate for the clawback: that would recover the same money twice.
Partial refunds proportionally reduce amount_cents but leave the status unchanged. Multiple partial refunds against the same commission accumulate until they sum to the full payment, at which point the commission flips to refunded.
Idempotency
Use the same idempotency key and body for retries within 30 days. Use a new key for each distinct partial refund. Without a key, retrying a partial refund deducts again; a commission that's already fully refunded returns 409 with the existing commission_id.
Affiliate notification
When a refund either fully voids a commission or claws back a commission that was already paid out, the affiliate receives a transactional email notification. There is no opt-out - losing earned income is information the affiliate must receive.