Invoice lifecycle
Every V3 invoice status, the action calls that move an invoice between them, when you can edit, and which webhooks you can subscribe to.
This page explains each status an invoice can be in, which call or event moves it there, and what
you can still change at each step.
Why status changes are action calls
You never change an invoice's status with PATCH. Each change is its own POST
(/send, /void, /pay, and so on). That way the API knows what happened, not only the result,
and records who did it and when in events and actors. PATCH only edits fields.
Statuses
Created corresponds to V1's Draft, and Opened to V1's Open.
| Status | Meaning | How it gets there |
|---|---|---|
Created | The invoice exists but hasn't been sent. You can edit or delete it. | POST /v3/payments/invoices |
Opened | Sent to the payer and waiting for payment. | POST .../send |
Overdue | The dueDate passed without payment. | Wingspan, automatically |
Pending | Payment can't go ahead yet because a condition isn't met, such as outstanding eligibility requirements on the engagement. | Wingspan |
Disputed | The invoice is under dispute by the payee or the payer. | Wingspan, when a dispute is recorded |
PaymentInTransit | A payment has been started and is being processed. | POST .../pay, or a payer payment |
PartiallyPaid | Part of the amount has been paid. | Wingspan, as payments post |
Paid | Wingspan's system has marked the invoice paid. | Wingspan |
PaidOffPlatform | Payment happened outside Wingspan and was recorded. | POST .../pay-off-platform |
Returned | The payment rail returned the payment. | Wingspan, from the bank or provider |
PartiallyRefunded | Part of the payment was refunded. | POST .../refund with an amount |
Refunded | The full payment was refunded. | POST .../refund |
Cancelled | The invoice was voided. | POST .../void |
stateDiagram-v2
[*] --> Created
Created --> Opened: send
Created --> [*]: DELETE
Opened --> Overdue: due date passes
Opened --> PaymentInTransit: pay
Overdue --> PaymentInTransit: pay
Opened --> PaidOffPlatform: pay-off-platform
Overdue --> PaidOffPlatform: pay-off-platform
Opened --> Cancelled: void
Overdue --> Cancelled: void
PaymentInTransit --> Paid
PaymentInTransit --> PartiallyPaid
PaymentInTransit --> Returned
Paid --> Refunded: refund
Paid --> PartiallyRefunded: refund (partial)
The diagram shows the common paths. Wingspan can also move an invoice between states on its own,
for example when a payment fails and can be retried.
Actions
| Call | Who uses it | Allowed when | Notes |
|---|---|---|---|
PATCH /v3/payments/invoices/{invoiceId} | Payee | Created or Opened | Omitted fields stay the same. Send null to clear a nullable field. Splits can't be changed after sending. |
DELETE /v3/payments/invoices/{invoiceId} | Payee | Created only | Returns 204. On any other status it returns 409. Use void instead. |
POST .../send | Payee | Created | Moves to Opened and emails the payer. |
POST .../remind | Payee | Opened or Overdue | Emails a payment reminder. |
POST .../void | Payee | After sending | Cancels a sent invoice. A reason field isn't supported yet and returns 422. |
POST .../accept | Payee | Opened or Overdue | For an obligation the payer created: confirms you agree with the amount and terms before payment starts. Sets payeeReviewStatus: Accepted. |
POST .../dispute | Payee | Opened or Overdue | Records your dispute and sets payeeReviewStatus: Disputed. Requires a non-empty reason. Retrying with the same reason returns the existing dispute unchanged. A different reason on an existing dispute returns 409. |
POST .../pay | Payee or linked payer | Opened or Overdue | Collects payment. See Collect payment. |
POST .../pay-off-platform | Payee | Unpaid | Records a full payment made outside Wingspan and marks the invoice PaidOffPlatform. |
POST .../refund | Payee | Paid and deposited | Full refund, or partial with amount. See Refunds and returns. |
All paths are under /v3/payments/invoices/{invoiceId}.
Errors you'll see on transitions
Status and code | Cause |
|---|---|
409 InvalidStateTransition | The invoice is in the wrong status for this action, for example accept on a disputed invoice. |
409 EligibilityBlocked | send, remind, or pay-off-platform on an engagement whose payment eligibility requirements aren't complete. The detailCode is payments.EngagementNotPaymentsEligible. See Requirements and eligibility. |
409 ResourceConflict | A duplicate externalId on create. |
409 (other) | DELETE on an invoice that isn't Created, or a pay-off-platform retry with different payment details. |
412 PreconditionFailed | pay with an If-Match value that no longer matches the invoice. Read the invoice again and retry with the new ETag. |
Branch on code. detailCode is there to help you debug and may change. See
Errors.
Review and approval run separately from status
Two more fields track workflow that sits beside status:
payerApprovalStatus(Pending,PreApproved,Approved,Declined) is the payer's approval
of the obligation. The payer manages it from its side as a payable. See
Payable lifecycle and statuses.payeeReviewStatus(Pending,Accepted,Disputed,Resubmitted) is your review as the
payee, set byacceptanddispute.
Neither one moves money by itself.
Webhooks
These invoice events are published and you can subscribe to them:
| Event | Fires when |
|---|---|
Invoice.PaymentInTransit | A payment has been started. |
Invoice.Paid | Wingspan's system marked the invoice paid. |
Invoice.DepositConfirmed | Wingspan's originating bank or provider reported its final processed status for the payment. |
Invoice.PaymentFailed | A payment attempt failed. The invoice can be paid again. |
Invoice.Returned | The payment was returned by the rail. |
InvoicePayment.Initiated, .Processing, .Completed, .Failed, .Returned, .Cancelled | Each collection attempt moves through its own status. |
Invoice.Paid and Invoice.DepositConfirmed are different signals. Paid is Wingspan's internal
state. DepositConfirmed means Wingspan's originating bank or provider reported its terminal
processed state. It doesn't mean the recipient's bank received the money, that funds are available,
or that the payment can't be returned, so an Invoice.Returned can still follow. See
Paid vs DepositConfirmed.
Events for Created, Opened, Overdue, Cancelled, Disputed, Refunded, and
PaidOffPlatform aren't available to subscribe to yet. To track those changes, read the invoice
with GET /v3/payments/invoices/{invoiceId}, or list invoices with a status filter such as
GET /v3/payments/invoices?filter[status][eq]=Overdue.
Webhook delivery is best effort and can send duplicates. Dedupe on the event id, and recover
anything you missed from the event log. See Webhooks overview
and Recover missed events.
Related pages
Updated 10 days ago