Create and manage schedules
Create and manage scheduled payments by returning scheduling actions from your collection module lifecycle hooks.
Create a schedule
A collection module creates a scheduled payment by returning a schedule_payment action from a lifecycle hook.
The examples use
addMonthsas a date helper. Replace it with your module's date utility if you use a different one.
export const afterPolicyIssued = async ({ policy }) => {
const billingPeriodEnd = addMonths(policy.first_debit_date, 1);
return [
{
name: 'schedule_payment',
scheduled_for: policy.first_debit_date,
expected_amount: policy.premium_amount + (policy.outstanding_balance ?? 0),
currency: policy.currency,
premium_type: 'recurring',
billing_period_start: policy.first_debit_date,
billing_period_end: billingPeriodEnd,
// payment_method_id omitted, so the platform uses the policy's payment method.
},
];
};The hook returns an array of actions. Return [] to schedule nothing. Return more than one action to schedule several payments at once.
Action fields
| Field | Required | Description |
|---|---|---|
name | Yes | Always "schedule_payment". |
scheduled_for | Yes | The date the payment is due. A calendar date, not a time, for example "2026-08-01". |
expected_amount | Yes | The amount to collect, in the smallest currency unit. |
currency | Yes | The ISO currency code, for example "ZAR". |
premium_type | Yes | The kind of payment. Most schedules use recurring. Other values: pro_rata, arrears, ad_hoc, cover_period, collection_request, manual_eft, premium_refund. |
billing_period_start | Yes | The first day of the period this payment covers. |
billing_period_end | Yes | The last day of the period this payment covers. |
payment_method_id | No | A specific payment method. Leave it out to use the policy's payment method. |
The platform validates each action, records the schedule, and emits a collection_scheduled event.
Make payments recur
A schedule_payment action schedules one payment. To collect every period, return a new action from the lifecycle hook that runs after each payment. Recurrence comes from your hooks, not from configuration.
afterPolicyIssuedschedules the first payment when the policy goes live.afterPaymentSucceededschedules the next payment after each payment succeeds.
export const afterPaymentSucceeded = async ({ policy, payment }) => {
const next = addMonths(payment.billing_period_start, 1);
const nextPeriodEnd = addMonths(next, 1);
return [
{
name: 'schedule_payment',
scheduled_for: next,
expected_amount: policy.premium_amount,
currency: policy.currency,
premium_type: 'recurring',
billing_period_start: next,
billing_period_end: nextPeriodEnd,
},
];
};afterPaymentSucceeded is what keeps the chain going. Each successful payment runs the hook again, and the hook returns the next schedule_payment. Your hook decides the next due date and amount, so you control the cadence.
Recurrence depends entirely on your hooks. If your module does not implement
afterPaymentSucceededor another hook that returns the nextschedule_payment, a policy is scheduled once and never billed again.
Change or cancel a schedule
To move a scheduled payment to a different date, return a reschedule_payment action. To cancel one before it is created, return an unschedule_payment action. Both take the id of the schedule you are changing.
// Return these from a lifecycle hook, the same way you return schedule_payment actions.
// `scheduledPayment` is the schedule you are changing; your module looks it up (for example,
// from the hook input or by querying your own records).
// Move a scheduled payment to a new date.
return [
{
name: 'reschedule_payment',
scheduled_payment_id: scheduledPayment.id,
new_scheduled_for: '2026-08-01',
reason: 'policyholder requested a later date', // optional
},
];
// Cancel a scheduled payment.
return [
{
name: 'unschedule_payment',
scheduled_payment_id: scheduledPayment.id,
reason: 'policy_cancelled',
},
];reschedule_payment fields
reschedule_payment fields| Field | Required | Description |
|---|---|---|
name | Yes | Always "reschedule_payment". |
scheduled_payment_id | Yes | The id of the schedule to move. |
new_scheduled_for | Yes | The new due date. |
reason | No | A short note on why it moved. |
unschedule_payment fields
unschedule_payment fields| Field | Required | Description |
|---|---|---|
name | Yes | Always "unschedule_payment". |
scheduled_payment_id | Yes | The id of the schedule to cancel. |
reason | Yes | One of "manual_admin", "payment_method_revoked", or "policy_cancelled". |
The unschedule_payment reason must be one of the fixed values above; the reschedule_payment reason is free text.
A reschedule emits collection_rescheduled; an unschedule emits collection_unscheduled.
Next step
Submit scheduled payments if your collection module uses an API provider.