C Cashbanx Webhooks
PTEN

Integration documentation

Webhooks

How Cashbanx notifies a partner about the life cycle of a purchase — from identification through to the cashback payment.

Document NR-001/01 Version 1.0 — August 2026 Audience integrated partners

01How it works

Cashbanx tracks the purchase your user made at the partner store and notifies you at every relevant change. There are four events, each with its own URL in your environment. We send a POST; you respond.

  1. The user leaves your environment for the store through a Cashbanx link. That click is recorded and carries the userIdentifier — the user's identifier in your system.
  2. The affiliate network reports the purchase to us. We match it to the click and send RECOGNIZED_PURCHASE.
  3. When the network confirms the purchase we send CONFIRMED_PURCHASE; if it cancels, we send CANCELED_PURCHASE.
  4. When the cashback withdrawal is paid, we send PAID_CASHBACK.
Not every purchase goes through all four

A canceled purchase receives neither a confirmation nor a payment. And an event with no URL configured is simply not sent — it is not queued waiting for you to configure it later.

02What we need from you

ItemDescription
RECOGNIZED_PURCHASEURL of the endpoint that receives the identified purchase.
CONFIRMED_PURCHASEURL of the endpoint that receives the confirmation.
CANCELED_PURCHASEURL of the endpoint that receives the cancellation.
PAID_CASHBACKURL of the endpoint that receives the withdrawal payment.
API keyA secret value we will send on every POST so you can confirm the call is ours. Up to 255 characters.

The URLs are independent: they may point to four distinct endpoints or to the same one — the webhookType field in the body tells you which event arrived. We recommend HTTPS on all of them.

Each environment has its own configuration. Staging URLs and keys are not the production ones.

03Authentication

The default is a static key in a header. Every POST leaving Cashbanx carries:

x-api-key: <the key agreed with you>

Reject any call that does not carry the correct key. It is the only factor identifying the origin — we do not sign the body and we send no timestamp; protection against third parties is the key plus TLS.

Variant: OAuth 2.0

For partners that require an access token, Cashbanx can obtain a token from your authentication endpoint before each delivery and send the webhook with Authorization: Bearer instead of x-api-key. In that arrangement we need the login URL and the credentials, and we support two ways of presenting them:

  • x-www-form-urlencoded body — we send the agreed parameters (for example grant_type, client_id, client_secret) in the body of the login POST.
  • Authorization: Basic — we send the credential in the header, with an empty body.

In both cases we expect a JSON response containing an access_token field. This is arranged case by case; the standard integration uses x-api-key only.

04The request

POSTthe URL you configured for the event
PropertyValue
MethodPOST
Content-Typeapplication/json
Authenticationx-api-key
Timeout30 seconds
Redirectsfollowed automatically
Answer fast, process later

Thirty seconds without a response counts as a failure and enters the retry queue. If your processing is slow, accept the notification, respond 200 and handle the body asynchronously.

05The four events

EventWhen we send itNote
RECOGNIZED_PURCHASE The affiliate network reported the purchase and we identified it. The purchase starts out pending. Nothing has been credited yet.
CONFIRMED_PURCHASE The affiliate network confirmed the purchase. End of the store's cancellation window. This is where cashback stops being provisional.
CANCELED_PURCHASE The affiliate network canceled the purchase. Final state. No further event is sent for that purchase.
PAID_CASHBACK The cashback withdrawal changed to paid. Final state of the financial cycle.

06Payload

The body is always the same envelope: the event type and the transaction.

{
  "webhookType": "RECOGNIZED_PURCHASE | CONFIRMED_PURCHASE | CANCELED_PURCHASE | PAID_CASHBACK",
  "transaction": { }
}

Examples per event

The purchase was identified and is pending. Nothing has been credited yet.

{
  "webhookType": "RECOGNIZED_PURCHASE",
  "transaction": {
    "id": 23026,
    "clickOrigin": "Android Mobile",
    "voucherCode": "",
    "status": "Pending",
    "statusUpdateDate": null,
    "type": "Identified",
    "cashbackStatus": "Pending",
    "amended": false,
    "amendReason": "",
    "createdDate": "2026-08-14T07:12:25.000Z",
    "updatedDate": null,
    "transactionDate": "2026-08-13T23:59:00.000Z",
    "paidDate": null,
    "thirdPartyId": "1527912405",
    "purchaseOrderNumber": "16181113",
    "paymentOrderNumber": null,
    "amount": 350,
    "oldAmount": null,
    "currency": "BRL",
    "sourceAmount": null,
    "sourceCurrency": "",
    "quotationValue": null,
    "oldQuotationValue": null,
    "commission": 17.5,
    "oldCommission": null,
    "commissionPercentage": 0.05,
    "commissionReceiptDate": null,
    "cashbackValue": 10.5,
    "oldCashbackValue": null,
    "cashbackPercentage": 0.03,
    "cashbackPoints": 350,
    "taxValue": 1.75,
    "partnerValue": 0,
    "companyValue": 5.25,
    "paidPartnerValue": false,
    "paymentDatePartnerValue": null,
    "productBasket": null,
    "photo": "",
    "store": {
      "id": 3176,
      "name": "Nike",
      "logo": "https://cashbanx.s3.amazonaws.com/retangle-logos/nike.png",
      "circleLogo": "https://cashbanx.s3.amazonaws.com/circle-logos/nike.png"
    },
    "user": {
      "id": 8842,
      "userIdentifier": "3c217db6-fa75-4252-94dd-af8e10b77454",
      "name": "Maria Souza"
    },
    "goOut": {
      "id": 1251,
      "additionalProps": "mothers-day-campaign",
      "date": "2026-08-13 09:56:47",
      "userAgent": "Mozilla/5.0 (iPhone; CPU iPhone OS 18_3_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148"
    }
  }
}

Field dictionary

The presence column says what you can count on. There are three cases, and the difference between them is what decides whether your code needs a guard:

  • always — the key is in every delivery, with a useful value. Safe to read directly.
  • may be empty — the key is always there, but the value may be null (numeric and date fields) or an empty string (text fields). It never goes missing; it may say nothing.
  • may be missing — the key may not exist in the JSON. Reading it without a guard breaks.

The rule behind it: a text field with no value arrives as "", a numeric or date field with no value arrives as null, and whatever does not apply to that delivery disappears rather than arriving null.

FieldPresenceDescription
webhookTypealwaysWhich event arrived. One of the four values in section 05.
transaction.idalwaysPurchase identifier at Cashbanx. Stable across every event — this is your reconciliation key.
transaction.statusalwaysPurchase state.
transaction.typealwaysHow the purchase was attributed to the user.
transaction.cashbackStatusalwaysCashback state.
transaction.createdDatealwaysWhen the purchase was created at Cashbanx.
transaction.transactionDatealwaysWhen the purchase happened at the store.
transaction.statusUpdateDatemay be emptyDate of the last state change. null while the purchase has not changed state.
transaction.updatedDatemay be emptyWhen the purchase was updated at Cashbanx. null if it never was.
transaction.paidDatemay be emptyWhen the cashback was paid. Only carries a value in PAID_CASHBACK.
transaction.clickOriginmay be emptyDevice the click came from. Empty string when the network does not report it.
transaction.voucherCodemay be emptyCoupon code used. Empty string on most purchases.
transaction.amendedmay be emptytrue if the affiliate network changed the purchase after reporting it.
transaction.amendReasonmay be emptyReason for the change. Empty string when amended is false.
transaction.thirdPartyIdmay be emptyPurchase identifier at the affiliate network.
transaction.purchaseOrderNumbermay be emptyOrder number at the store.
transaction.paymentOrderNumbermay be emptyPayment order number at the affiliate network. Usually only arrives after payment.
transaction.amountmay be emptyTotal purchase amount. Can be null — the column accepts null and some networks do not report the amount.
transaction.oldAmountmay be emptyPrevious amount, when the purchase was changed. null if it was never changed.
transaction.currencymay be emptyPurchase currency.
transaction.sourceAmountmay be emptyAmount in the source currency, for a converted purchase.
transaction.sourceCurrencymay be emptySource currency, for a converted purchase.
transaction.quotationValuemay be emptyExchange rate applied in the conversion.
transaction.oldQuotationValuemay be emptyPrevious exchange rate, when it was revised.
transaction.commissionmay be emptyAffiliate network commission on the purchase.
transaction.oldCommissionmay be emptyPrevious commission, when changed.
transaction.commissionPercentagealwaysCommission rate, as a fraction (0.05 = 5%).
transaction.commissionReceiptDatemay be emptyWhen the commission was received from the affiliate network.
transaction.cashbackValuealwaysThe user's cashback amount. Goes to zero on a canceled purchase.
transaction.oldCashbackValuemay be emptyPrevious cashback, when changed. This is where the original amount of a canceled purchase ends up.
transaction.cashbackPercentagealwaysCashback rate, as a fraction.
transaction.cashbackPointsmay be missingThe same cashback converted into points. Only present for partners whose programme runs on points; for the others the key does not appear.
transaction.taxValuealwaysTaxes withheld on the commission.
transaction.partnerValuealwaysShare of the commission owed to the partner.
transaction.companyValuealwaysShare of the commission retained by Cashbanx.
transaction.paidPartnerValuealwaystrue if the partner's share has already been paid out.
transaction.paymentDatePartnerValuemay be emptyDate of the payout to the partner.
transaction.productBasketmay be emptyPurchase items. null for most networks, which do not send them.
transaction.photomay be emptyReceipt URL. Empty string when there is none.
storealwaysThe store object. Arrives as {} on a purchase with no associated click.
store.idmay be missingStore identifier in your programme's catalogue — the same id the store endpoints of the API return, not a global id.
store.namemay be missingStore name.
store.logomay be missingRectangular logo URL.
store.circleLogomay be missingCircular logo URL.
useralwaysThe user object. Arrives as {} on a purchase with no associated click.
user.userIdentifiermay be missingThe user's identifier in your system, exactly as you sent it on the click. This is how you recognise whose purchase it is. May be an empty string on an unidentified purchase.
user.idmay be missingUser identifier at Cashbanx. Absent if the user is not registered on our side.
user.namemay be missingUser name at Cashbanx. Absent for the same reason.
goOutalwaysThe click object. Arrives as {} on a purchase with no associated click.
goOut.idmay be missingIdentifier of the click that originated the purchase.
goOut.datemay be missingClick date and time. Format YYYY-MM-DD HH:MM:SS, not ISO.
goOut.userAgentmay be missingBrowser user agent at the moment of the click.
goOut.additionalPropsmay be missingFree text you sent on the click and we return here. Useful for campaign, source or any correlation of your own.
withdraw.idmay be missingWithdrawal identifier. The withdraw key only exists in CONFIRMED_PURCHASE and PAID_CASHBACK.
withdraw.statusmay be missingWithdrawal state.
Absent fields do not arrive as null

When a value does not apply — cashbackPoints on a cashback programme, withdraw on a purchase with no withdrawal, user.id for a user we have no record of — the key disappears from the JSON. Treat absence and null as equivalent, and do not depend on any optional key being present.

For the same reason your parser must ignore fields it does not know: new fields may appear without notice, and that is not considered a breaking change.

Enum domains

Every enum travels by name, as text — never as a number.

FieldPossible values
transaction.statusPending, Approved, Declined, Received
transaction.typeUnidentified, Identified, NoAssignment
transaction.cashbackStatusPending, Ahead, Reversed, Finished, Canceled, Analyzing
withdraw.statusPending, Confirmed, Disapproved, Waiting

Dates follow ISO 8601 in UTC (2026-08-14T07:12:25.000Z). One inherited exception: goOut.date arrives as YYYY-MM-DD HH:MM:SS.

07Expected values per event

The values are not free — it is the state of the purchase that decides which webhook goes out. Use this matrix to validate what you receive:

Event status cashbackStatus withdraw.status
RECOGNIZED_PURCHASE Pending Pending, Analyzing, Ahead absent
CONFIRMED_PURCHASE Approved Pending, Analyzing, Ahead Pending or Waiting
CANCELED_PURCHASE Declined Canceled absent
PAID_CASHBACK Approved or Received Finished Confirmed

In CONFIRMED_PURCHASE, the withdraw object appears when a withdrawal already exists for the purchase — which depends on how your programme is configured. If a withdrawal was created and moved before the confirmation, withdraw.status reflects its real state at that moment and may carry another value from the domain.

08Your response

What decides the fate of the notification is the HTTP status code you return. The body of your response is not interpreted.

You respond We record What happens
200–299 SENT Delivered. We will not send this event again for this purchase.
422 CANCELED Received and refused by your decision. We do not retry, and we do not send it again. Use it when you do not want this event for this purchase.
others FAILED Any other code, a timeout or a network error. Enters the retry queue.
A business error is not a 200

Responding 200 ends the notification for good. If processing failed on your side and you want another attempt, respond with an error code — 500, for example. And reserve 422 for a deliberate refusal, because it is final too.

09Retries

A notification that failed is retried up to 3 times, with growing intervals:

AttemptApproximate intervalCumulative
1st retry~5 minutes~5 min after the failure
2nd retry~15 minutes~20 min
3rd retry~45 minutes~1 h 05 min

The intervals carry a random variation of up to 20% either way, so that an outage which knocked down many notifications at once does not send them all back at the same instant. Once the three attempts are exhausted, we stop.

A recovery job resumes retries left pending by an interruption on our side. Because of it, a notification may arrive hours after the original failure — which reinforces the point of the next section.

10Idempotency and ordering

  • Deduplicate on webhookType + transaction.id. That pair identifies the notification. We deliver each of them successfully only once per purchase, but a response lost in transit can make us resend something you have already processed.
  • A retry rebuilds the body from scratch. It reflects the state of the purchase at the moment of the retry, not at the first attempt. Values such as cashbackStatus or withdraw.status may arrive different from what they would have been — and the second version is the correct one.
  • We do not guarantee ordering. A retried CONFIRMED_PURCHASE may arrive after a PAID_CASHBACK. Decide from transaction.status, cashbackStatus and the dates, never from the order in which the requests arrived.

11Delivery with side effects

For some programmes — agreed case by case, and stated in your integration contract — a successful response to CONFIRMED_PURCHASE means that the partner has already credited the user. In that arrangement Cashbanx marks the withdrawal as paid at that very moment, and PAID_CASHBACK follows.

If this is your case, respond 2xx to CONFIRMED_PURCHASE only after the credit is effective on your side. An optimistic 200 closes the payment at Cashbanx without the user having received anything.

12Support

Questions about the integration, a URL change, a key rotation or a manual resend of a notification: talk to your commercial contact at Cashbanx or write to suporte@cashbanx.com, quoting the transaction.id and the webhookType involved.