Track Refund
Reverse a commission when a sale is refunded
Last updated Oct 15, 2026
POST /api/v1/track/refund
Reverses the affiliate commission tied to a previously tracked sale. Send one call per refund. Each call's refund_amount_cents is added to what was already refunded on the sale (capped at the sale amount), and the commission is reduced proportionally: refunded while part of the sale remains, reversed once all of it is refunded. Omitting refund_amount_cents refunds everything that is left. This is the non-Stripe counterpart to the Stripe refund webhook — both use the same underlying reversal logic.
Authentication
All requests must include your program API key as a Bearer token.
Authorization: Bearer YOUR_PROGRAM_API_KEY
Content-Type: application/jsonThe key is scoped to a single program. The refund lookup is restricted to sales that belong to the authenticated program.
Request
Fields
The transaction_id originally passed to POST /api/v1/track/sale. Used to locate the sale record.
This refund's amount in the same unit as the sale's amount_cents — not the running total refunded so far. Omit or set to 0 to refund the rest of the sale. Must be a positive integer when provided.
Your own id for this refund (for example the Stripe refund id). Recommended. A refund whose refund_id was already applied is ignored, so retries are safe. Without it, a second call with the same amount for the same sale is treated as a retry and ignored. Ignored when the amount is omitted or 0 (refunding the rest is already safe to retry).
Human-readable reason for the refund. Recorded on the commission and included in partner/advertiser notifications.
refund or chargeback. Recorded as the reason when refund_reason is not sent or empty.
refundchargebackExample request body
curl -X POST https://api.affitor.com/api/v1/track/refund \
-H "Authorization: Bearer YOUR_PROGRAM_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"transaction_id": "txn_abc123",
"refund_amount_cents": 5000,
"refund_id": "re_123",
"refund_reason": "Customer requested cancellation"
}'Multiple refunds and retries
- Two partial refunds of $30 and $20 on a $100 sale: send
3000then2000(each with its ownrefund_id). The commission is reduced by 50% in total. - Retries: resending a call with the same
refund_idreturnsstatus: "skipped"and changes nothing. Withoutrefund_id, the amount is used to detect the retry.
Response
Success — commission reversed or refunded
{
"success": true,
"status": "reversed",
"deductionAmount": 25.00,
"commission_id": 88
}| Field | Type | Description |
|---|---|---|
success | boolean | Always true on a 200 response. |
status | string | "reversed" (sale fully refunded), "refunded" (partially refunded), or "skipped" (refund already applied, or commission already reversed). |
deductionAmount | number | Commission amount reversed by this call, in the commission's currency. Taken from the partner's balance only when the commission was already credited (past pending, not a test). Present when status is reversed or refunded. |
commission_id | integer | ID of the affected commission record. |
Success — sale has no commission
{
"success": true,
"status": "no_commission",
"message": "Sale has no commission to reverse"
}Already-processed refund (idempotent)
{
"success": true,
"status": "skipped",
"reason": "already reversed",
"commission_id": 88
}Errors
Errors use the platform envelope with a stable error.code, except a retired API key and rate limiting, which use the typed envelope.
| Status | error.code | When |
|---|---|---|
| 400 | invalid_request | transaction_id is missing, or reason is not refund or chargeback. |
| 401 | unauthorized | Authorization header is missing, malformed, or the API token is invalid. |
| 404 | not_found | No sale record found for the given transaction_id in the authenticated program. |
| 500 | internal_error | Internal error during commission reversal. |
Side Effects
When a commission is reversed or refunded, Affitor:
- transitions the commission to
reversedorrefundedstatus - if the commission was already credited to the partner (any status past
pending, and not a test), deducts the proportional amount from the partner's balance in the same transaction as the status change; the balance can go negative, and open withdrawals are reduced to fit - sends an in-app notification to the partner
- sends a manual-review warning to the advertiser if the commission had already been paid out
The status change and the deduction are applied before the response. The notifications are sent afterwards and never change the response.
Related
- Track Sale — record the original sale
- Payment Tracking — Stripe webhook alternative