Recurring invoices

Bill a client on a schedule with a V3 RecurringInvoice, then pause, resume, generate early, preview, or cancel the series.

A recurring invoice bills the same client on a schedule, such as a monthly retainer, without you
creating each invoice by hand. This page shows how to set one up and manage the series.

In V1 you checked Repeat this invoice automatically on an invoice, and the API used invoice
templates. In V3 the series is its own resource, RecurringInvoice. Each period it creates an
ordinary invoice that you track like any other.

How it works

A RecurringInvoice has three parts:

  • A schedule (schedule) that says when each invoice is created.
  • An invoice template (invoiceTemplate) with the line items and settings copied onto each new
    invoice.
  • Switches for what happens to each new invoice: shouldAutoSend opens and emails it, and
    automaticCollectionMode controls whether Wingspan collects it automatically. See
    Collect each invoice automatically.

Generation only moves forward. A start date in the past sets the rhythm of the schedule, but the
first invoice is created at the next future date. Resuming a paused series doesn't create the
invoices it skipped.

Statuses

StatusMeaningHow it gets there
DraftSaved but not running.Create with "status": "Draft"
ActiveCreating invoices on schedule.Create (the default), resume, or a PATCH from Draft
PausedNot creating invoices. The schedule is kept.POST .../pause
CompletedThe end condition was reached.Wingspan, automatically
CancelledStopped for good. Can't be resumed.POST .../cancel

Completed only happens for a series with an end date or a number of occurrences.

Create a recurring invoice

Call POST /v3/payments/recurring-invoices.
payerId, currency, schedule, and invoiceTemplate are required, and the template needs
paymentTermsDays. This example bills Northwind Staffing a 2,500.00 USD retainer on the first of
each month for 12 months, due 15 days after each invoice is issued.

curl -X POST https://api.wingspan.app/v3/payments/recurring-invoices \
  -H "Authorization: Bearer $WINGSPAN_TOKEN" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
    "payerId": "Pq7Lm2Xv9RtK4sWb8NcY3d",
    "externalId": "RETAINER-NW-2026",
    "currency": "USD",
    "schedule": {
      "frequency": "Monthly",
      "interval": 1,
      "dayOfMonth": 1,
      "anchorDate": "2026-11-01",
      "timezone": "America/Chicago",
      "endCondition": { "type": "AfterOccurrences", "maxOccurrences": 12 }
    },
    "shouldAutoSend": true,
    "invoiceTemplate": {
      "paymentTermsDays": 15,
      "invoiceNumberPrefix": "NW-",
      "lineItems": [
        { "description": "Monthly design retainer", "type": "Services", "totalCost": 2500.00 }
      ],
      "acceptedPaymentMethods": ["Ach"],
      "notificationPreferences": { "shouldSendInvoice": true, "shouldSendReminders": true }
    }
  }'
// 201 Created (trimmed)
{
  "id": "Nr2Kx6Lq9Vz3Wt7Bm1Pc5d",
  "externalId": "RETAINER-NW-2026",
  "payerId": "Pq7Lm2Xv9RtK4sWb8NcY3d",
  "status": "Active",
  "currency": "USD",
  "nextOccurrenceDate": "2026-11-01",
  "occurrencesGenerated": 0,
  "isAutoSendEnabled": true,
  "isAutoPayEnabled": false,
  "events": { "createdAt": "2026-09-24T16:04:55Z" }
}

The response carries an ETag. Send it as If-Match on later updates and actions. See
Concurrency and ETags.

To bill against one engagement rather than the Payer's default, add payerEngagementId. Wingspan
returns 404 if the Payer or engagement isn't yours.

The schedule

FieldNotes
frequencyDaily, Weekly, SemiMonthly, Monthly, or Annual.
intervalEvery N periods. Monthly with interval: 3 is quarterly, and Weekly with interval: 2 is every two weeks. Defaults to 1.
anchorDateThe first or reference date. Required with frequency.
dayOfWeekRequired for Weekly, for example Friday.
dayOfMonthFor Monthly and Annual. In short months it moves to the last day of the month.
daysOfMonthFor SemiMonthly only: exactly two days, such as [1, 15].
timezoneIANA timezone that dates are evaluated in. It can't change after the first invoice is created.
businessDayAdjustmentPreviousBusinessDay, NextBusinessDay, or None (default).
holidayCalendarUSFederalReserve or None (default).
prorationPolicyNone (default) or ByDays, for starting, pausing, or cancelling mid-period.
endCondition{ "type": "Never" }, { "type": "OnDate", "endDate": "2027-10-31" }, or { "type": "AfterOccurrences", "maxOccurrences": 12 }. maxOccurrences can be 1 to 30.
scheduleDatesAn explicit list of dates, for irregular schedules. You can use it instead of frequency.

The invoice template

invoiceTemplate takes most of the fields you'd put on a one-off invoice. See
Components of an invoice for what each does.

FieldNotes
paymentTermsDaysRequired. Each invoice's due date is its issue date plus this many days, from 0 to 180. 0 means due on receipt.
invoiceNumberPrefixUp to 20 characters, such as NW-. Wingspan numbers the invoices.
lineItemsThe charges. On a template, the line-item type field is type, not lineItemType.
splitsCollaborator splits copied onto each invoice.
acceptedPaymentMethods, lateFeeHandling, creditFeeHandling, notificationPreferences, notes, metadataThe same as on an invoice. Late fees are measured from each generated invoice's own due date.

A template has no fixed due date or invoice number, because each generated invoice gets its own.

Manage the series

All of these are under /v3/payments/recurring-invoices/{recurringInvoiceId} and accept If-Match
and Idempotency-Key.

CallWhat it does
PATCHChange the schedule, invoiceTemplate, shouldAutoSend, automaticCollectionMode, or metadata. Template fields you send are merged in, and lineItems replaces the whole list. Changes apply to future invoices only.
POST .../pauseStop creating invoices and keep the schedule.
POST .../resumeStart again from now. Skipped periods aren't billed.
POST .../cancelStop for good. An optional reason (up to 1,000 characters) is kept on the record.
POST .../generate-nowCreate the next scheduled invoice right away. Returns 201 with the new Invoice and moves the next date forward.
GET .../upcoming-occurrencesPreview upcoming dates, with occurrenceDate, resolvedDueDate, and sequenceNumber. Nothing is created. Use page[size] to choose how many.
DELETEDelete the series. Invoices it already created aren't affected.

To start a Draft series, PATCH it with { "status": "Active" }. That's the only status change
PATCH accepts. Use the action calls above for everything else.

A stale If-Match returns 412. An action that doesn't fit the current status, such as resuming a
cancelled series, returns 409.

Collect each invoice automatically

Set automaticCollectionMode on create or PATCH to decide whether Wingspan collects each
generated invoice without a pay call:

ValueEffect
EnabledCollect this series' invoices automatically.
DisabledDon't collect this series' invoices automatically, even if the engagement or Payer is set to.
InheritClear this series' setting and follow the engagement, then the Payer.

Leaving the field out of a PATCH keeps the current value. Changes apply to payments started after
the change. Collection uses the engagement's installed funding authorization, so nothing is collected
until the engagement has one. For bank debit, that's a bank account with an active Recurring
mandate. See
Automatic collection for how the Payer and engagement
settings work and how to get the authorization.

isAutoPayEnabled on the response is an older view of the same preference. It can't tell
Inherit apart from Disabled, so read automaticCollectionMode instead. The older shouldAutoPay
switch is still accepted, but new integrations should use automaticCollectionMode.

Find the invoices a series created

Each generated invoice carries recurringInvoiceId, so you can match it to its series. The series
shows occurrencesGenerated, lastOccurrenceDate, and nextOccurrenceDate. Once the series ends,
nextOccurrenceDate is null.

List series with
GET /v3/payments/recurring-invoices,
filtered by filter[payerId][eq], filter[externalId][eq], or filter[status][eq], and sorted with
sort[createdAt]=desc. Send the same sort on every page.

Webhooks

RecurringInvoice webhook events aren't available to subscribe to yet. Read the series with
GET /v3/payments/recurring-invoices/{recurringInvoiceId} to track it, and follow each generated
invoice through its own invoice events.

Related pages


Did this page help you?