Track Refund
Reverse a commission when a sale is refunded
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 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.
Example 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 | Dollar amount deducted from the partner's commission balance. 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
| Status | When |
|---|---|
| 400 | transaction_id is missing from the request body. |
| 401 | Authorization header is missing, malformed, or the API token is invalid. |
| 404 | No sale record found for the given transaction_id in the authenticated program. |
| 500 | Internal error during commission reversal. |
Side Effects
When a commission is reversed or refunded, Affitor:
- transitions the commission to
reversedorrefundedstatus - deducts the proportional commission amount from the partner's and partner-program's balance (clamped at zero unless the commission was already paid, in which case the balance can go negative)
- sends an in-app notification to the partner
- sends a manual-review warning to the advertiser if the commission had already been paid out
These side effects are fire-and-forget and do not affect the HTTP response.
Related
- Track Sale — record the original sale
- Payment Tracking — Stripe webhook alternative