PaymentSchedule API

Schedule payments against existing payment agreements

The Payment Schedule API turns an authorised NPP PayTo agreement into a set-and-forget recurring payment. Instead of calling the payment initiation API for every run, you tell us the schedule details once and Azupay initiates payments on the agreement's cadence.

This guide covers the full lifecycle: create a schedule, retrieve it, and pause or resume it. Every endpoint shown extends the existing Azupay REST API and reuses the same SecretKey authentication and error model conventions you already know.

📘

What you'll need before you start

A Payment Agreement that is in ACTIVE state and has been authorised by the payer at their bank.


Endpoints at a glance

MethodPathPurpose
POST/v1/paymentAgreement/{paymentAgreementId}/schedulerCreate a schedule against an existing payment agreement. Amends an existing schedule.
GET/v1/paymentAgreement/{paymentAgreementId}/schedulerRetrieve a schedule by payment agreement ID.

To search for payment initiations, refer to our existing paymentInitiation API.

Create a Payment Schedule

Create a schedule by POSTing the agreement ID with the schedule details:

POST https://api.azupay.com.au/v1/paymentAgreement/{paymentAgreementId}/scheduler
Authorization: SECR_MYBUSINESSID_nR2duCGXlqWSuYJF
Content-Type: application/json
{
  "PaymentScheduler": {
    "status": "ACTIVE",
    "frequency": "MONTHLY",
    "amount": "89.95",
    "startDate": "2026-08-01",
    "endDate": "2027-06-01",
    "timezone": "Australia/Sydney"
  }
}

Request fields

FieldRequiredDescription
statusyesStatus of this schedule. Must be ACTIVE when creating a schedule for the first time.
frequencyyesFrequency of the scheduled runs (ONCE, WEEKLY, FORTNIGHTLY, MONTHLY, ANNUAL).
amountyesRun amount in AUD as a string, e.g. "89.95". Must fit inside the agreement's per-payment and per-period caps.
startDateyesFirst run date, YYYY-MM-DD. Must be strictly after today in the schedule's timezone.
endDatenoLast permitted run date, YYYY-MM-DD. Omit for an open-ended schedule.
timezoneyesIANA name (e.g. Australia/Sydney, Australia/Perth, UTC). No implicit default.
📘

What is not on this call

No retry parameters, the scheduler does not retry for hard failures. No additionalDetails block - schedule creation is a back-office action, not a payer-initiated payment. No callback URL on the schedule resource - run outcomes ride on your existing Payment Initiation webhook.

Successful response - 201 Created

{
  "PaymentScheduler": {
    "paymentAgreementId": "aa340b79a04571f989bed2f8d0f771b4",
    "status": "ACTIVE",
    "frequency": "MONTHLY",
    "amount": "89.95",
    "startDate": "2026-08-01",
    "endDate": "2027-06-01",
    "timezone": "Australia/Sydney"
  },
  "PaymentSchedulerStatus": {
    "version": 1,
    "createdDatetime": "2026-05-18T04:33:12.045Z",
    "updatedDatetime": "2026-06-23T02:42:32.753Z"
  }
}

Validation Errors - 4xx

See down below for a full list of 4xx errors and their corresponding messages.

{
  "message": "version does not match the current scheduler version",
  "details": {
    "failureCode": "AZP3.8",
    "failureReason": "version does not match the current scheduler version"
  }
}
failureCodeHTTPfailureReason
ERR0.03400Request validation failed. Message can vary.
AZP3.1400Payment Agreement is not active or does not exist
AZP3.2400Start date must be future-dated - at least one day after today in the scheduler's timezone (recurring only; ONCE may run today)
AZP3.3400End date must be equal to or after start date
AZP3.4400Amount exceeds the agreement's per-payment cap
AZP3.5400Frequency mismatch between agreement and scheduler
AZP3.6400Timezone is not a valid IANA name
AZP3.9400Schedule date falls beyond the payment agreement's end date
AZP4.2409A non-terminal scheduler is already attached to this agreement
AZP4.3400The target status you provide must be ACTIVE
AZP4.14404Payment agreement was not found (also returned for a missing agreement id or a cross-client ownership mismatch)
❗️

One active schedule per agreement

Only one schedule can be created against one agreement. By calling POST against the same payment agreement, it will be amended instead.

How cadence is anchored

The cadence anchor is derived from startDate in combination with the agreement's frequency:

  • Weekly - same day-of-week as startDate, every week.
  • Fortnightly - startDate, then every 14 days.
  • Monthly - startDate.day every month. If startDate.day is 29, 30, or 31, the schedule executes on the last day of every month instead - so February is never silently skipped.
  • Annually - same month and day as startDate, every year. Feb 29 resolves to the last day of February each year.
📘

No backfill - schedule resumes from next occurrence

If an execution is missed for any reason, the schedule resumes from its next scheduled occurrence; missed executions are not replayed. Azupay will never initiate two payments for the same scheduled run.


Retrieve a Payment Schedule

GET https://api.azupay.com.au/v1/paymentAgreement/{paymentAgreementId}/scheduler
Authorization: SECR_MYBUSINESSID_nR2duCGXlqWSuYJF

Returns the request payload alongside the current state (status, cadence, last run).

Successful response - 200 OK

{
  "PaymentScheduler": {
    "paymentAgreementId": "aa340b79a04571f989bed2f8d0f771b4",
    "status": "ACTIVE",
    "frequency": "MONTHLY",
    "amount": "89.95",
    "startDate": "2026-08-01",
    "endDate": "2027-06-01",
    "timezone": "Australia/Sydney"
  },
  "PaymentSchedulerStatus": {
    "version": 2,
    "lastRunDatetime": "2026-06-01T04:33:12.045Z",
    "updatedDatetime": "2026-07-03T01:12:58.089Z",
    "createdDatetime": "2026-05-18T04:33:12.045Z"
  }
}

Amend an existing Payment Schedule

Amending an existing Schedule

To amend an existing schedule, use the original POST /paymentAgreement/{paymentAgreementId}/scheduler

Semantics

  • If no schedule exists for the paymentAgreementId, one will be created.
  • If a schedule already exists, the full payload is required to amend a schedule
  • There is only ever one schedule per payment agreement.
  • Changes apply to future scheduled payments only.
  • When amending, version is required and must match the current version returned by the most recent GET or POST. If the version does not match, the amendment is rejected with 409 Conflict - this prevents concurrent calls from silently overwriting each other.

Amendable fields

You must provide the version you are amending

Version must be provided when amending a schedule. It should be the same version from the most current GET response. Once updated, the version will then be incremented.

FieldNotes
statusSwitch between ACTIVE and INACTIVE.
amountNew run amount in AUD. Must still fit inside the agreement's caps.
startDateReschedules the cadence anchor. Must be strictly after today in the schedule's timezone and cannot be before the next pending run date.
endDateExtend, shorten, or remove the end boundary.
frequencyMust match the frequency permitted by the payment agreement.
timezoneValid IANA timezone name.
versionThe version of the schedule you are currently amending.

📘

The full payload is required for amendments

To clear an optional field (e.g. endDate), send it explicitly as null.

Example - amending amount and end date

The full payload is required to amend a schedule.

POST https://api.azupay.com.au/v1/paymentAgreement/{paymentAgreementId}/scheduler
Authorization: SECR_MYBUSINESSID_nR2duCGXlqWSuYJF
Content-Type: application/json
{
  "PaymentScheduler": {
    "version": 2,
    "status": "ACTIVE",
    "frequency": "MONTHLY",
    "amount": "99.95",
    "startDate": "2026-08-01",
    "endDate": "2029-06-01",
    "timezone": "Australia/Sydney"
  }
}

Successful response - 200 OK

{
  "PaymentScheduler": {
    "paymentAgreementId": "aa340b79a04571f989bed2f8d0f771b4",
    "status": "ACTIVE",
    "frequency": "MONTHLY",
    "amount": "99.95",
    "startDate": "2026-08-01",
    "endDate": "2029-06-01",
    "timezone": "Australia/Sydney"
  },
  "PaymentSchedulerStatus": {
    "version": 3,
    "lastRunDatetime": "2026-06-01T04:33:12.045Z",
    "createdDatetime": "2026-05-18T04:33:12.045Z",
    "updatedDatetime": "2026-05-28T01:12:44.901Z"
  }
}

Example - pausing a schedule

The full payload is required to amend a schedule.

POST https://api.azupay.com.au/v1/paymentAgreement/{paymentAgreementId}/scheduler
Authorization: SECR_MYBUSINESSID_nR2duCGXlqWSuYJF
Content-Type: application/json
{
  "PaymentScheduler": {
    "version": 3,
    "status": "INACTIVE",
    "frequency": "MONTHLY",
    "amount": "99.95",
    "startDate": "2026-08-01",
    "endDate": "2029-06-01",
    "timezone": "Australia/Sydney"
  }
}

Successful response - 200 OK

{
  "PaymentScheduler": {
    "paymentAgreementId": "aa340b79a04571f989bed2f8d0f771b4",
    "status": "INACTIVE",
    "frequency": "MONTHLY",
    "amount": "99.95",
    "startDate": "2026-08-01",
    "endDate": "2029-06-01",
    "timezone": "Australia/Sydney"
  },
  "PaymentSchedulerStatus": {
    "version": 4,
    "lastRunDatetime": "2026-06-01T04:33:12.045Z",
    "createdDatetime": "2026-05-18T04:33:12.045Z",
    "updatedDatetime": "2026-05-28T01:12:44.901Z"
  }
}		

Schedule Status & Agreement Status

The Payment Agreement status will be synced with the Payment Schedule status automatically.

Allowed transitions

FromToEffect
ACTIVEINACTIVESuspends further runs. The underlying agreement is untouched.
INACTIVEACTIVEResumes the schedule from its next scheduled run. Runs missed during the pause are not back-filled.

Validation Errors - 4xx

See down below for a full list of 4xx errors and their corresponding messages.

{
  "message": "version does not match the current scheduler version",
  "details": {
    "failureCode": "AZP3.8",
    "failureReason": "version does not match the current scheduler version"
  }
}
failureCodeHTTPfailureReason
ERR0.03400Request validation failed. Message can vary.
AZP3.1400Payment Agreement is not active or does not exist
AZP3.2400Start date must be future-dated - at least one day after today in the scheduler's timezone (recurring only; ONCE may run today)
AZP3.3400End date must be equal to or after start date
AZP3.4400Amount exceeds the agreement's per-payment cap
AZP3.5400Frequency mismatch between agreement and scheduler
AZP3.6400Timezone is not a valid IANA name
AZP3.7400version is required when amending an existing scheduler
AZP3.8409version does not match the current scheduler version
AZP3.9400Schedule date falls beyond the payment agreement's end date
AZP4.14404Payment agreement was not found (also returned for a cross-client ownership mismatch)

Schedule lifecycle

The schedule has two states: ACTIVE & INACTIVE

Merchant-driven transitions

Merchants can change the status of the schedule via POST /paymentAgreement/{paymentAgreementId}/scheduler

Agreement-driven transitions (automatic - no API call needed)

The schedule status is only affected when the underlying agreement is cancelled - regardless of how or by whom. Payments are attempted as normal while the agreement is paused or suspended by the payer or bank. When a cancellation is received, the schedule moves to INACTIVE only after the latest payment initiation attempt has completed.

TriggerResult
Payer cancels the agreementSchedule → INACTIVE
Merchant cancels the agreement via APISchedule → INACTIVE
Agreement expiresSchedule → INACTIVE
Bank cancels or revokes the agreementSchedule → INACTIVE

Completion

When endDate is reached, the schedule moves to INACTIVE.

📘

You don't get schedule-specific webhooks

By design, the schedule does not emit its own callbacks. Run outcomes flow through the existing Payment Initiation webhook. Agreement-driven status changes flow through the existing Payment Agreement webhook. The schedule resource simply mirrors what those events already tell you.


Webhook behaviour

The schedule does not introduce a new webhook channel. The two existing webhooks carry everything you need:

WebhookWhat it tells you
PaymentInitiationStatusEventOne event per run attempt - settlement reference, success or failure code. Same shape as any ad-hoc payment.
PaymentAgreementStatusEventTells you when the payer or bank cancels the agreement - which in turn sets the schedule to INACTIVE.

Things to keep in mind

  • No automatic retries. The schedule initiates each run once. To chase a failed collection, drive it from your own systems - for example by creating a fresh ad-hoc payment under the same agreement.
  • No schedule-specific webhooks. Use the Payment Initiation and Payment Agreement webhooks you already consume.
  • Missed runs during a pause are not back-filled. Runs that fall during a paused window are skipped; the schedule picks up at the next scheduled date when resumed.
  • End-of-month handling. A monthly schedule starting on the 29th, 30th, or 31st runs on the last day of every month thereafter - by design, so shorter months like February are never silently skipped.