cli.gs
Sign in

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/reversal with the orderId and a reason.
  • 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 REVERSED and the publisher is notified. If you've set a status callback, your shop receives conversion.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

text
POST https://cli.gs/api/v1/track/reversal
Authorization: Bearer YOUR_API_KEY
Content-Type: application/json
FieldRequiredMeaning
orderIdone of the twoThe order id you reported the conversion with
conversionIdone of the twoAlternatively, the conversionId from our answer to the report
reasonyesWhy the commission is taken back (see below)
eventTypenoSALE (default), LEAD or SIGNUP, if the same order id was reported for several event types
notenoFree 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

reasonUse it when
RETURNThe goods were returned
CANCELLEDThe order was cancelled before or after shipping
CHARGEBACKThe payment was charged back
DUPLICATEThe order was reported twice under different ids
FRAUDThe order was fraudulent
TESTA real (non-test) report that was only a test
ORDER_UPDATEThe order was replaced by another one
POLICY_VIOLATIONThe publisher broke the programme terms
OTHERNone 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

bash
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

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

json
{ "ok": true, "conversionId": "cmg3x1q2w0001abcd9876efgh", "status": "REVERSED", "duplicate": false }
HTTPBodyMeaning
200ok: true, duplicate: falseReversed now
200ok: true, duplicate: trueWas already reversed or rejected. Nothing changed
400error: "invalid payload" or "orderId or conversionId is required"Fix the request, e.g. an unknown reason
401e.g. "invalid api key"Check key and signature
404error: "CONVERSION_UNKNOWN"No conversion with this order id in this programme. Often the sale never came through a publisher link, which is fine
409error: "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.