Returns and reversals
Take back a commission when an order is returned.
When a customer returns an order, cancels it, or the payment is charged back, the commission for it shouldn't be paid. On this page you'll learn how your shop reports that with a reversal: one request per order, and the booked money goes back to where it came from.
In short:
- Send
POST https://cli.gs/api/v1/track/reversalwith theorderIdand areason. - A reversal takes back the whole commission and refunds commission and network fee to your credit.
- It works until the commission is locked for a payout. After that, the answer is
409 CONVERSION_FINAL. - Reversals are safe to repeat and are signed like conversion reports.
What a reversal does
- If the commission was booked, it's taken off the publisher's balance, whether pending or available at that moment. The commission and the network fee are refunded to your credit.
- If the conversion was never booked, for example because it was still parked for review, only its status changes.
- The conversion's status becomes
REVERSEDand the publisher is notified. If you've set a status callback, your shop receivesconversion.reversed.
How long a reversal is possible
A reversal is possible as long as the conversion is still pending, approved, matured or waiting for review. Once its commission has been locked for a publisher payout, it's final. The request is then refused with 409 CONVERSION_FINAL.
So report returns promptly. Set a holding period that covers your return window: see Terms and commission rules. The holding period is the wait between approval and payout.
The request
POST https://cli.gs/api/v1/track/reversal
Authorization: Bearer YOUR_API_KEY
Content-Type: application/json| Field | Required | Meaning |
|---|---|---|
orderId | one of the two | The order id you reported the conversion with |
conversionId | one of the two | Alternatively, the conversionId from our answer to the report |
reason | yes | Why the commission is taken back (see below) |
eventType | no | SALE (default), LEAD or SIGNUP, if the same order id was reported for several event types |
note | no | Free text up to 2,000 characters, shown with the conversion under Conversions |
Send either orderId or conversionId. Reversals are signed exactly like conversion reports: see Signed requests.
Reasons
reason | Use it when |
|---|---|
RETURN | The goods were returned |
CANCELLED | The order was cancelled before or after shipping |
CHARGEBACK | The payment was charged back |
DUPLICATE | The order was reported twice under different ids |
FRAUD | The order was fraudulent |
TEST | A real (non-test) report that was only a test |
ORDER_UPDATE | The order was replaced by another one |
POLICY_VIOLATION | The publisher broke the programme terms |
OTHER | None of the above; explain in note |
A reversal always takes back the whole commission. Partial returns aren't supported. If part of an order comes back, decide per your terms whether to reverse the whole conversion.
Examples
cURL
curl -X POST https://cli.gs/api/v1/track/reversal \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "orderId": "100234", "reason": "RETURN", "note": "Returned on 12 October" }'Node.js
// Call this from your refund or cancellation handler.
async function reportReversal(orderId, reason, note) {
const res = await fetch("https://cli.gs/api/v1/track/reversal", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.CLIGS_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({ orderId: String(orderId), reason, note }),
signal: AbortSignal.timeout(5000),
});
const data = await res.json().catch(() => ({}));
if (res.ok) return data; // { ok: true, conversionId, status, duplicate }
if (res.status === 404) return null; // no conversion for this order: nothing to reverse
if (res.status === 409) {
console.warn("Commission already final", data.conversionId);
return null;
}
if (res.status >= 500 || res.status === 429) throw new Error("retry later");
throw new Error(`cli.gs refused the reversal: ${res.status} ${data.error}`);
}
// await reportReversal("100234", "RETURN", "Returned on 12 October");In WooCommerce, the hooks woocommerce_order_status_refunded and woocommerce_order_status_cancelled are good places to call this. Call it for orders that carry a _cligs_ref.
Responses
{ "ok": true, "conversionId": "cmg3x1q2w0001abcd9876efgh", "status": "REVERSED", "duplicate": false }| HTTP | Body | Meaning |
|---|---|---|
| 200 | ok: true, duplicate: false | Reversed now |
| 200 | ok: true, duplicate: true | Was already reversed or rejected. Nothing changed |
| 400 | error: "invalid payload" or "orderId or conversionId is required" | Fix the request, e.g. an unknown reason |
| 401 | e.g. "invalid api key" | Check key and signature |
| 404 | error: "CONVERSION_UNKNOWN" | No conversion with this order id in this programme. Often the sale never came through a publisher link, which is fine |
| 409 | error: "CONVERSION_FINAL" | The commission is locked for a payout and final. Nothing is adjusted later |
Like conversion reports, reversals are safe to repeat. Test conversions can't be reversed; they never moved money in the first place.
Next steps
If some shop pages can't call your server, read about the tracking pixel as a fallback.