Developers API reference Get sandbox access
API reference

Webhooks

Cashwo sends a signed HTTPS POST to your endpoint whenever an agreement, payment or payout changes in a way you need to act on.

Set up

  1. In the merchant portal, open Settings → Webhooks.
  2. Enter your endpoint. It must be https:// with a valid certificate; redirects aren't followed.
  3. Create a signing secret and store it with your API key.

Events

EventSent when
agreement_activatedThe customer authorised the agreement at their bank.
agreement_suspendedPaused by you, or by the customer at their bank.
agreement_reactivatedA suspended agreement is active again.
agreement_cancelledCancelled by you, the customer, or because it expired.
agreement_failedThe authorisation failed at the bank.
payment_settledMoney arrived.
payment_failedThe debit was declined. payment.final says whether it will be retried.
payment_returnedA settled payment was returned by the bank.
payout_settled, payout_failed, payout_returnedA payout sent from the merchant portal changed status.

Common fields

Every event carries event, event_id (a UUID that stays the same on every retry), created_at and merchant_id, plus:

EventsFields
agreement_*agreement_id, merchant_ref, previous_status, agreement (the agreement object)
payment_*transaction_id, transaction_ref, merchant_ref, amount, currency, status, agreement_id, payment (the payment object), payment_agreement; failures and returns add failure_reason
payout_*payout_id, payout (amount, currency, reference, status, failure_reason)

Example

POST to your endpoint
Content-Type: application/json
X-Cashwo-Event: payment_settled
X-Cashwo-Event-Id: 9199bfcb-6c27-4107-b1b3-909a3fa9f2e0
X-Cashwo-Delivery-Attempt: 1
X-Cashwo-Signature: sha256=5f2c9a…e81b

{
  "event": "payment_settled",
  "event_id": "9199bfcb-6c27-4107-b1b3-909a3fa9f2e0",
  "created_at": "2027-01-01T09:00:41+11:00",
  "merchant_id": 12,
  "transaction_id": 88213,
  "transaction_ref": "3 Inst - INV-10482",
  "merchant_ref": "CUST-2291",
  "amount": 120.00,
  "currency": "AUD",
  "status": "SETTLED",
  "agreement_id": 4127,
  "payment": { /* payment object */ },
  "payment_agreement": { /* agreement object */ }
}

Verify the signature

X-Cashwo-Signature is sha256= followed by the hex HMAC-SHA256 of the raw request body, keyed with your signing secret. Compute it over the exact bytes you received, before parsing the JSON, compare in constant time, and reject the request if it doesn't match.

PHP
$raw      = file_get_contents('php://input');
$given    = $_SERVER['HTTP_X_CASHWO_SIGNATURE'] ?? '';
$expected = 'sha256=' . hash_hmac('sha256', $raw, getenv('CASHWO_WEBHOOK_SECRET'));

if (!hash_equals($expected, $given)) {
    http_response_code(401);
    exit;
}

$event = json_decode($raw, true);
// Record $event['event_id'] first; skip it if you've seen it before.
http_response_code(200);

Delivery and retries

  • Answer with any 2xx status within 15 seconds. Do slow work (emails, fulfilment) after responding.
  • Anything else (an error status, a timeout, no connection) is retried after 1 minute, 5 minutes, 30 minutes, 2 hours, 6 hours and 24 hours: 7 attempts over about 32 hours. After that the event is marked failed.
  • Every attempt is listed under Settings → Webhooks, where you can also replay it by hand.
  • Retries and replays have the same event_id and body. Make your handler idempotent on event_id; X-Cashwo-Delivery-Attempt tells you which attempt it is.
  • Events can arrive out of order. Use the object's status and updated_at, or fetch it from the API, rather than assuming order.