Building the integration
Refunds & voids
Reverse the points a sale earned, and re-credit any reward that was redeemed against it.
POST/webhooks/genericorPOST/v1/refundComing
Not yet enabled in production
The refund path is built and in review. The shape below is final — build your sender against it now and Perx will confirm the enablement date. Until then, send only sale.completed.
Sending a refund
Send sale.refunded when a sale is refunded, whole or in part. Perx reverses the points earned on the original sale — proportionally for a partial refund — and re-credits any points reward redeemed against it.
{
"eventId": "evt_2026-08-27_00099",
"eventType": "sale.refunded",
"occurredAt": "2026-08-27T11:02:10Z",
"store": { "id": "12345" },
"transaction": {
"id": "INV-2026-0042",
"currency": "MVR"
},
"refund": {
"id": "CRN-2026-0007",
"amount": 45.00
}
}| Field | Req | Notes |
|---|---|---|
transaction.id | yes | The original sale’s id, exactly as sent on the earn event. This is how Perx finds what to reverse. |
refund.id | yes | Unique id for this refund — the idempotency key for the reversal. |
refund.amount | no | Omit for a full refund. When present, points reverse in proportion to the original order total. |
Response statuses
| status | Meaning |
|---|---|
REVERSED | Points removed and/or re-credited as shown. |
ALREADY_PROCESSED | This refund.id was already handled. Success. |
NOTHING_TO_REVERSE | No matching earn or redemption — e.g. the original sale never reached Perx. Not an error. |
Voids and cancellations
- A void before you ever sent the sale needs no event at all — just do not send the sale.
- A void after you sent
sale.completedshould go assale.refundedwith the full amount. sale.voidedis accepted and ignored. It is useful as a harmless no-op for a “Test connection” button in your settings screen.
Edge cases you do not have to handle
- Refunding more than was earned. If the customer has already spent those points, Perx applies the merchant’s underflow policy. Do not check balances first.
- Refund arriving before its sale. Handled — ordering is not required.
- Two partial refunds of one sale. Distinct events, because idempotency is on
refund.id, nottransaction.id.