Recurring Billing Guide
Create and manage recurring payment contracts that charge a stored payment method on a schedule.
Cresora Recurring lets you create a contract that automatically charges a stored payment method on a fixed schedule.
Concepts
- Contract — the recurring agreement: a stored payment method, an amount, a frequency, and a schedule (start date, optional end date or installment count).
- Installment — a single scheduled charge generated from a contract.
There is no separate "plan" or "subscription" object — the contract is the recurring entity. Each scheduled charge is materialized as a normal transaction, so it shows up in your transaction reporting like any other payment.
Prerequisites — tokenize the payment method first
A contract charges a vault token, never a raw card or bank number. Capture the payment method once via a Hosted Payment Page tokenization session, then pass the resulting token as payment_method_token. See the Tokenization Guide →.
Create a recurring contract
curl -X POST https://api.cresoracommerce.ai/api/v1/contracts \
-H "Authorization: Bearer csk_ab12cd34_xxxxxxxxxxxxxxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{
"merchant_id": "b1e7c0f2-3a4d-4e5f-8a9b-0c1d2e3f4a5b",
"payment_method_type": "CARD",
"payment_method_token": "cvt_01885fec-4444-7a31-9bbb-3e5b9a1f8c42",
"amount": "99.00",
"currency": "USD",
"frequency": "MONTHLY",
"start_date": "2026-08-01"
}'import requests, uuid
resp = requests.post(
"https://api.cresoracommerce.ai/api/v1/contracts",
headers={
"Authorization": "Bearer csk_ab12cd34_xxxxxxxxxxxxxxxxxxxxxxxx",
"Idempotency-Key": str(uuid.uuid4()),
},
json={
"merchant_id": "b1e7c0f2-3a4d-4e5f-8a9b-0c1d2e3f4a5b",
"payment_method_type": "CARD",
"payment_method_token": "cvt_01885fec-4444-7a31-9bbb-3e5b9a1f8c42",
"amount": "99.00",
"currency": "USD",
"frequency": "MONTHLY",
"start_date": "2026-08-01",
},
)
contract = resp.json() # contract["id"]const resp = await fetch("https://api.cresoracommerce.ai/api/v1/contracts", {
method: "POST",
headers: {
Authorization: "Bearer csk_ab12cd34_xxxxxxxxxxxxxxxxxxxxxxxx",
"Content-Type": "application/json",
"Idempotency-Key": crypto.randomUUID(),
},
body: JSON.stringify({
merchant_id: "b1e7c0f2-3a4d-4e5f-8a9b-0c1d2e3f4a5b",
payment_method_type: "CARD",
payment_method_token: "cvt_01885fec-4444-7a31-9bbb-3e5b9a1f8c42",
amount: "99.00",
currency: "USD",
frequency: "MONTHLY",
start_date: "2026-08-01",
}),
});
const contract = await resp.json();Bounding the schedule (optional)
By default a contract charges indefinitely until you cancel it. To bound it, supply either:
end_date— stop after this date, orremaining_installments— a fixed number of charges, ortotal_amount— a fixed contract total.
Manage a contract
| Action | Endpoint |
|---|---|
| Get status | GET /api/v1/contracts/{contractId} |
| Retry a failed installment | POST /api/v1/contracts/{contractId}/retry |
| Cancel | POST /api/v1/contracts/{contractId}/cancel |
Contracts move through PENDING_ACTIVATION → ACTIVE → COMPLETED (or CANCELLED / FAILED), and can be PAUSED, then resumed back to ACTIVE. Limited field-level updates are supported via PATCH /api/v1/contracts/{contractId}: next_charge_date and end_date (with an optional reason). amount, frequency, and max_retries are not editable — sending any of them is rejected with 422 recurring_update_not_supported, because the amount and cadence are part of the terms the payer consented to at enrollment. The payment method cannot be changed either. To change any of these, cancel the contract and create a new one.
ACH recurring contracts
For ACH payment methods (payment_method_type of ACH_WEB, ACH_PPD, or ACH_CCD), you must also supply the NACHA authorization context on create: ach_sec_code, ach_account_type (CHECKING / SAVINGS), name_on_check, and authorization_timestamp (when the consumer authorized the debit).
NACHA requires you to re-notify the consumer when the debit date or amount changes, or when enough time has elapsed since the original authorization. Cresora captures the original authorization_timestamp on the contract, but sending the re-notification to your customer before the next charge is your responsibility. Monitor contract.payment_success / contract.payment_failed to track each installment.
Webhook events
| Event | When |
|---|---|
contract.created | Contract created |
contract.payment_success | A scheduled installment charged successfully |
contract.payment_failed | A scheduled installment failed (soft decline) |
contract.completed | Final installment paid — contract complete |
contract.paused | Contract paused |
contract.resumed | Contract resumed |
contract.cancelled | Contract cancelled before completion |
Frequencies
frequency accepts one of:
| Value | Meaning |
|---|---|
DAILY | Every day |
WEEKLY | Every week |
BIWEEKLY | Every 2 weeks |
MONTHLY | Every month |
QUARTERLY | Every 3 months |
ANNUALLY | Every year |