Agreements
An agreement is your customer's PayTo authority for you to collect payments. You create it, they authorise it at their bank, and Cashwo collects what it allows.
The agreement object
{
"agreement_id": 4127,
"status": "ACTIVE",
"payment_ref": "INV-10482",
"merchant_reference": "CUST-2291",
"frequency": "MONTHLY",
"initial_amount": 50.00,
"amount": 120.00,
"currency": "AUD",
"start_date": "2026-11-01",
"end_date": "2027-10-31",
"next_payment_date": "2026-12-01",
"customer": { "name": "Jane Citizen", "email": "jane@example.com", "phone": "412345678" },
"collection_type": "Link",
"archived": false,
"created_at": "2026-10-23 09:56:25",
"updated_at": "2026-10-24 08:12:03",
"url": "https://transaction.cashwo.com.au/?t=AQx4b7…"
}url is the customer's link. It doesn't expire: after verifying, the customer can reopen it to see the agreement and their payment history.
Statuses
- AWAITING: created; the customer hasn't authorised it yet.
- ACTIVE: authorised at the customer's bank. Payments are collected when due.
- SUSPENDED: paused by you or by the customer at their bank. It can be resumed.
- CANCELLED: ended by you or the customer, or expired. Nothing more is collected.
- FAILED: the authorisation failed at the bank. The customer can try again from the link.
POSTcreate_agreement
Creates an agreement in AWAITING status and returns the link to send your customer, by email, SMS or on your checkout page. Nothing is debited until they authorise it. Send an Idempotency-Key.
| Field | Type | Rules & meaning | |
|---|---|---|---|
payment_ref | Required | string | 2–21 characters. Shown to the customer and on their bank statement, e.g. an invoice or member number. |
merchant_reference | Required | string | 2–21 characters. Your own reference; it comes back in webhooks as merchant_ref. |
customer_name | Required | string | Letters and spaces only, up to 30 characters. |
customer_email | Required | string | A valid email. The customer can receive their verification code here. |
customer_phone | Required | string | Australian mobile, e.g. 0412345678 or +61412345678. Spaces are ignored. |
frequency | Required | string | See frequencies. |
amount | Required | number | Each scheduled instalment, AUD, up to 2 decimals, greater than 0. Ignored for ONETIME. |
initial_amount | Optional | number | A one-off amount collected the day the agreement becomes ACTIVE. Required (above 0) for ONETIME. |
agreement_start_date | Optional | string | YYYY-MM-DD; defaults to today. Ignored for ONETIME. |
agreement_end_date | Optional* | string | YYYY-MM-DD, today or later, not before the start date. Omit for no end date. *Required for ONETIME: the last day the customer can authorise. |
Frequencies
| Value | Collection |
|---|---|
WEEKLY, FORTNIGHTLY, MONTHLY, QUARTERLY, SEMIANNUAL, ANNUAL, DAILY | Scheduled. initial_amount (if any) is collected on activation day; instalments of amount start one period later and repeat until agreement_end_date. |
ONETIME | One payment of initial_amount, collected the day the customer authorises. Enabled on request. |
ADHOC | No schedule. You collect each payment (up to amount) from the merchant portal. |
Example
curl -X POST "$CASHWO_API_URL" \
-H "Authorization: Bearer $CASHWO_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: invoice-10482-create" \
-d '{
"action": "create_agreement",
"payment_ref": "INV-10482",
"merchant_reference": "CUST-2291",
"customer_name": "Jane Citizen",
"customer_email": "jane@example.com",
"customer_phone": "0412345678",
"frequency": "MONTHLY",
"initial_amount": 50.00,
"amount": 120.00,
"agreement_start_date": "2026-11-01",
"agreement_end_date": "2027-10-31"
}'$payload = [
'action' => 'create_agreement',
'payment_ref' => 'INV-10482',
'merchant_reference' => 'CUST-2291',
'customer_name' => 'Jane Citizen',
'customer_email' => 'jane@example.com',
'customer_phone' => '0412345678',
'frequency' => 'MONTHLY',
'initial_amount' => 50.00,
'amount' => 120.00,
'agreement_start_date' => '2026-11-01',
'agreement_end_date' => '2027-10-31',
];
$ch = curl_init(getenv('CASHWO_API_URL'));
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('CASHWO_API_KEY'),
'Content-Type: application/json',
'Idempotency-Key: invoice-10482-create',
],
CURLOPT_POSTFIELDS => json_encode($payload),
]);
$result = json_decode(curl_exec($ch), true);
curl_close($ch);
if (!empty($result['success'])) {
$agreementId = $result['agreement_id'];
$paymentLink = $result['url']; // send this to the customer
} else {
$code = $result['error_code'] ?? 'unknown';
$errors = (array) ($result['error'] ?? []);
}const res = await fetch(process.env.CASHWO_API_URL, { method: 'POST', headers: { Authorization: `Bearer ${process.env.CASHWO_API_KEY}`, 'Content-Type': 'application/json', 'Idempotency-Key': 'invoice-10482-create', }, body: JSON.stringify({ action: 'create_agreement', payment_ref: 'INV-10482', merchant_reference: 'CUST-2291', customer_name: 'Jane Citizen', customer_email: 'jane@example.com', customer_phone: '0412345678', frequency: 'MONTHLY', initial_amount: 50.0, amount: 120.0, agreement_start_date: '2026-11-01', agreement_end_date: '2027-10-31', }), }); const result = await res.json(); if (result.success) { const { agreement_id, url } = result; // send url to the customer } else { console.error(result.error_code, result.error); }
Response
{
"success": true,
"url": "https://transaction.cashwo.com.au/?t=AQx4b7…",
"agreement_id": 4127,
"agreement": { /* the agreement object */ }
}POSTget_agreement
Send agreement_id, or merchant_reference to get the newest agreement with that reference. Returns { "success": true, "agreement": { … } }, or 404 not_found.
{ "action": "get_agreement", "agreement_id": 4127 }POSTlist_agreements
Newest first, with pagination. All filters are optional.
| Field | Meaning |
|---|---|
status | e.g. AWAITING, ACTIVE, SUSPENDED, CANCELLED. |
merchant_reference, payment_ref, customer_email | Exact match. |
created_from, created_to | YYYY-MM-DD, inclusive. |
include_archived | true to include agreements archived in the portal. |
limit, starting_after | Page size (1–100, default 25) and cursor. |
{ "action": "list_agreements", "status": "ACTIVE", "limit": 50 }Cancel, suspend, resume
POSTcancel_agreement, suspend_agreement and resume_agreement each take agreement_id. Send an Idempotency-Key.
| Current status | Allowed | Result |
|---|---|---|
| AWAITING | cancel | 200. Cancelled at once; the customer's link can no longer be authorised. agreement_cancelled webhook. |
| ACTIVE | cancel, suspend | 202. Sent to the customer's bank; the status changes, and a webhook arrives, when the bank confirms. |
| SUSPENDED | cancel, resume | 202, as above. |
| Already in that status | any | 200; nothing changes. |
| Anything else | 409 invalid_status_change. |
One-time agreements can only be cancelled. 502 bank_rejected means the bank refused; 504 bank_timeout means it didn't answer, so fetch the agreement before retrying.
{ "action": "suspend_agreement", "agreement_id": 4127 }
{
"success": true,
"requested_status": "SUSPENDED",
"agreement": { "status": "ACTIVE", … },
"message": "Requested at the bank. The status changes, and an agreement webhook is sent, when the bank confirms."
}