invoices

Stable. Describes behaviour that is built and covered by tests in this repository.

API reference

GET /v1/invoices #

List invoices (ordered by id)

Parameters

NameInTypeDescription
customerquerystringCustomer external id
periodquerystring
statusquerydraft | open | paid | uncollectible | void
page_sizequeryinteger
page_tokenquerystring

Responses

StatusMeaningBody
200OKListInvoicesResponse
400Error envelopeError
404Error envelopeError
429Error envelopeError
Example response
{
  "invoices": [
    {
      "id": "string",
      "customer_id": "string",
      "customer_external_id": "string",
      "period_id": "string",
      "kind": "REGULAR",
      "number": "INV-2026-000123",
      "currency": "string",
      "subtotal_minor": "string",
      "total_minor": "string",
      "status": "draft",
      "saga_state": "string",
      "create_time": "2026-11-03T10:15:00Z",
      "finalize_time": "2026-11-03T10:15:00Z",
      "lines": [
        {
          "line_no": 1,
          "kind": "BASE_FEE",
          "meter_code": "string",
          "origin_period_id": "string",
          "quantity": "1.250",
          "amount_minor": "string",
          "description": "string",
          "service_from": "2026-11-03T10:15:00Z",
          "service_to": "2026-11-03T10:15:00Z"
        }
      ],
      "payments": [
        {
          "id": "string",
          "invoice_id": "string",
          "method": "bank_transfer",
          "amount_minor": "string",
          "currency": "string",
          "received_at": "2026-11-03T10:15:00Z",
          "reference": "string",
          "note": "string",
          "recorded_by": "string",
          "recorded_at": "2026-11-03T10:15:00Z"
        }
      ],
      "plan_version_id": "string",
      "pdf_object_key": "string",
      "credit_surplus_minor": "string"
    }
  ],
  "next_page_token": "string"
}
curl
curl -X GET https://YOUR-API-ADDRESS/v1/invoices \
  -H "Authorization: Bearer $PAYRUDDER_API_KEY"

GET /v1/invoices/{id} #

One invoice with its lines

Parameters

NameInTypeDescription
id requiredpathstring · uuid

Responses

StatusMeaningBody
200OKInvoice
404Error envelopeError
429Error envelopeError
Example response
{
  "id": "string",
  "customer_id": "string",
  "customer_external_id": "string",
  "period_id": "string",
  "kind": "REGULAR",
  "number": "INV-2026-000123",
  "currency": "string",
  "subtotal_minor": "string",
  "total_minor": "string",
  "status": "draft",
  "saga_state": "string",
  "create_time": "2026-11-03T10:15:00Z",
  "finalize_time": "2026-11-03T10:15:00Z",
  "lines": [
    {
      "line_no": 1,
      "kind": "BASE_FEE",
      "meter_code": "string",
      "origin_period_id": "string",
      "quantity": "1.250",
      "amount_minor": "string",
      "description": "string",
      "service_from": "2026-11-03T10:15:00Z",
      "service_to": "2026-11-03T10:15:00Z"
    }
  ],
  "payments": [
    {
      "id": "string",
      "invoice_id": "string",
      "method": "bank_transfer",
      "amount_minor": "string",
      "currency": "string",
      "received_at": "2026-11-03T10:15:00Z",
      "reference": "string",
      "note": "string",
      "recorded_by": "string",
      "recorded_at": "2026-11-03T10:15:00Z"
    }
  ],
  "plan_version_id": "string",
  "pdf_object_key": "string",
  "credit_surplus_minor": "string"
}
curl
curl -X GET https://YOUR-API-ADDRESS/v1/invoices/<id> \
  -H "Authorization: Bearer $PAYRUDDER_API_KEY"

GET /v1/invoices/{id}/pdf #

A presigned link to the invoice PDF (valid for 5 minutes)

Parameters

NameInTypeDescription
id requiredpathstring · uuid

Responses

StatusMeaningBody
200OKSignedUrl
404Error envelopeError
409Error envelopeError
Example response
{
  "url": "string",
  "expires_at": "2026-11-03T10:15:00Z"
}
curl
curl -X GET https://YOUR-API-ADDRESS/v1/invoices/<id>/pdf \
  -H "Authorization: Bearer $PAYRUDDER_API_KEY"

GET /v1/invoices/{id}/payment-wait #

What an invoice's payment is waiting for (a first charge that is held, a customer who has to confirm it)

Only state is always set. PAYMENT_HELD: the first charge was not sent because the payment gate said no (see hold_reason); since_time is when it began to be held and next_check_time when Payrudder looks again (it charges the invoice then if nothing holds it any more). PAYMENT_ACTION_REQUIRED: the card needs the customer to confirm the payment with their bank (3-D Secure); the customer was mailed a link to the invoice page at since_time (the mail log says whether it arrived), Payrudder asks the provider again at next_check_time, and if the customer has not confirmed by window_ends_time the payment counts as declined and dunning applies. Any other state answers the state alone.

Parameters

NameInTypeDescription
id requiredpathstring · uuid

Responses

StatusMeaningBody
200OKPaymentWait
404Error envelopeError
Example response
{
  "state": "PAYMENT_HELD"
}
curl
curl -X GET https://YOUR-API-ADDRESS/v1/invoices/<id>/payment-wait \
  -H "Authorization: Bearer $PAYRUDDER_API_KEY"

POST /v1/invoices/{id}/release #

Ask again for an invoice whose first charge is held, and charge it if nothing holds it any more

Payrudder asks the payment gate again, as it does every hour. When the gate lets the invoice through the charge is sent now (answer PAYMENT_REQUESTED); when the customer is still blocked for fraud or a chargeback, or the environment is on hold, the invoice stays held (answer PAYMENT_HELD, see hold_reason of the payment-wait read): to charge a blocked customer, lift the block first. 409 billing.invoice.not_held when the invoice's first charge is not held. Recorded in the audit log.

Parameters

NameInTypeDescription
id requiredpathstring · uuid

Responses

StatusMeaningBody
200OKInvoiceSaga
404Error envelopeError
409Error envelopeError
Example response
{
  "id": "string",
  "invoice_id": "string",
  "state": "string",
  "attempt": 1,
  "dunning_attempt": 1,
  "last_error": "string",
  "version": 1
}
curl
curl -X POST https://YOUR-API-ADDRESS/v1/invoices/<id>/release \
  -H "Authorization: Bearer $PAYRUDDER_API_KEY"

POST /v1/invoices/{id}/credit-notes #

Correct one of your paid invoices with a numbered credit note, added to the customer's credit balance or refunded to the card

An issued invoice is never edited. The note's amount goes to the customer's credit balance in the invoice's currency and pays their next invoice, or, with refund_to_card, back to the card through the payment provider that took the payment (the note's refund_state says whether it went through; a refund nobody answers is sent again, and a failed one can be retried with POST /v1/credit-notes/{id}/retry-refund). The notes of an invoice never add up to more than the invoice. Only a paid invoice can be credited: 409 billing.credit_note.not_paid for any other, 409 billing.credit_note.fully_credited when nothing is left of it, 409 billing.credit_note.nothing_to_refund when refund_to_card is asked of a payment no provider can return. Works on the invoices of the key's (or the signed-in person's) own environment only. Needs invoices:write. Recorded in the audit log (credit_note.issued).

Parameters

NameInTypeDescription
id requiredpathstring · uuid

Request body

FieldTypeDescription
amount_minorstring · int64int64 minor units; 0 or absent = what is left of the invoice
reasonstring
refund_to_cardbooleanRefund the amount to the card instead of adding it to the credit balance
Example
{}

Responses

StatusMeaningBody
201CreatedCreditNote
400Error envelopeError
403Error envelopeError
404Error envelopeError
409Error envelopeError
Example response
{
  "id": "0192f0b4-6f6e-7c3a-9a3e-6a1f2b3c4d5e",
  "number": "string",
  "invoice_id": "0192f0b4-6f6e-7c3a-9a3e-6a1f2b3c4d5e",
  "invoice_number": "string",
  "customer_id": "0192f0b4-6f6e-7c3a-9a3e-6a1f2b3c4d5e",
  "customer_external_id": "string",
  "currency": "string",
  "total_minor": "string",
  "tax_minor": "string",
  "reason": "string",
  "create_time": "2026-11-03T10:15:00Z",
  "disposition": "CREDIT_NOTE_DISPOSITION_CREDIT",
  "refund_state": "REFUND_STATE_REQUESTED",
  "refund_error": "string"
}
curl
curl -X POST https://YOUR-API-ADDRESS/v1/invoices/<id>/credit-notes \
  -H "Authorization: Bearer $PAYRUDDER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{}'

POST /v1/invoices/{id}/void #

Void an invoice whose first charge is held

Nothing was charged, so nothing is refunded; the invoice becomes void (answer VOIDED) and the invoice.voided webhook event is sent. Only an invoice whose first charge is held can be voided here: 409 billing.invoice.not_held for any other, a paid one included. Recorded in the audit log.

Parameters

NameInTypeDescription
id requiredpathstring · uuid

Responses

StatusMeaningBody
200OKInvoiceSaga
404Error envelopeError
409Error envelopeError
Example response
{
  "id": "string",
  "invoice_id": "string",
  "state": "string",
  "attempt": 1,
  "dunning_attempt": 1,
  "last_error": "string",
  "version": 1
}
curl
curl -X POST https://YOUR-API-ADDRESS/v1/invoices/<id>/void \
  -H "Authorization: Bearer $PAYRUDDER_API_KEY"

Models

CreateCreditNoteRequest

FieldTypeDescription
amount_minorstring · int64int64 minor units; 0 or absent = what is left of the invoice
reasonstring
refund_to_cardbooleanRefund the amount to the card instead of adding it to the credit balance

CreateInvoiceLinkRequest

FieldTypeDescription
expires_in_daysinteger

CreateInvoiceLinkResponse

FieldTypeDescription
token requiredstring
url requiredstring
expires_at requiredstring · date-time

CreditNote

FieldTypeDescription
id requiredstring · uuid
number requiredstringCN-2026-000007 (live) or SBX-CN-2026-000007 (sandbox)
invoice_id requiredstring · uuid
invoice_number requiredstring
customer_id requiredstring · uuid
customer_external_id requiredstring
currency requiredstring
total_minor requiredstring · int64
tax_minor requiredstring · int64The part of total_minor that is the invoice's tax given back: the note's share of the invoice's total taken from the invoice's tax, so the notes of an invoice give back its tax exactly. 0 for an untaxed invoice and for a note made before this field existed
reason requiredstring
create_time requiredstring · date-time
disposition requiredCREDIT_NOTE_DISPOSITION_CREDIT | CREDIT_NOTE_DISPOSITION_REFUNDWhere the amount went: the credit balance, or back to the card
refund_state requiredREFUND_STATE_REQUESTED | REFUND_STATE_REFUNDED | REFUND_STATE_FAILEDOnly for a refund to the card
refund_error requiredstring

Error

FieldTypeDescription
error requiredobject

Invoice

FieldTypeDescription
id requiredstring
customer_id requiredstring
customer_external_id requiredstring
period_id requiredstring
kind requiredREGULAR | CORRECTION | FEE | LINKFEE: a fixed fee invoiced in advance for one period, or what a plan change moved. LINK: made from an ad-hoc payment link
number requiredstring
currency requiredstring
subtotal_minor requiredstring · int64
total_minor requiredstring · int64
status requireddraft | open | paid | uncollectible | void
saga_state requiredstring
create_time requiredstring · date-time
finalize_time requiredstring · date-time
lines requiredarray of InvoiceLine
payments requiredarray of InvoicePaymentPayments recorded by hand by the operator (they do not come through a payment provider), oldest first. Only the single-invoice reads fill it.
plan_version_id requiredstring
pdf_object_key requiredstring
credit_surplus_minor requiredstring · int64What the lines came to below zero (late-usage corrections larger than everything else on the invoice). The total is never negative, so this was added to the customer's credit balance instead, once per invoice, and their next invoices use it. 0 for every other invoice

InvoiceLine

FieldTypeDescription
line_no requiredinteger
kind requiredBASE_FEE | USAGE | LATE_USAGE | CREDIT | CORRECTION | DISCOUNT | TAX
meter_code requiredstring
origin_period_id requiredstring
quantity requiredstring
amount_minor requiredstring · int64
description requiredstring
service_from requiredstring · date-timeThe service period the line covers, nullable: true
service_to requiredstring · date-time

InvoicePayment

FieldTypeDescription
id requiredstring
invoice_id requiredstring
method requiredbank_transfer | cash | other
amount_minor requiredstring · int64
currency requiredstring
received_at requiredstring · date-time
reference requiredstring
note requiredstring
recorded_by requiredstring
recorded_at requiredstring · date-time

InvoiceSaga

FieldTypeDescription
id requiredstring
invoice_id requiredstring
state requiredstring
attempt requiredinteger
dunning_attempt requiredinteger
last_error requiredstring
version requiredinteger

ListInvoicesResponse

FieldTypeDescription
invoices requiredarray of Invoice
next_page_token requiredstring

PaymentWait

FieldTypeDescription
state requiredstringThe invoice's saga state (`Invoice.saga_state`). Everything below is set only for PAYMENT_HELD and PAYMENT_ACTION_REQUIRED
since_timestring · date-timeWhen the payment began to wait. For PAYMENT_ACTION_REQUIRED, the moment the mail asking the customer to confirm it was asked for (not proof that it was delivered)
next_check_timestring · date-timeWhen Payrudder looks again (the next pass over held charges, or the next question to the provider)
hold_reasoncustomer_blocked | tenant_on_hold | nonePAYMENT_HELD only, asked of the payment gate now. `customer_blocked`: the customer is blocked for fraud or a chargeback. `tenant_on_hold`: the operator has suspended, frozen or banned the environment. `none`: nothing holds it now, the next pass charges it. Absent when the gate could not be asked
window_ends_timestring · date-timePAYMENT_ACTION_REQUIRED only: when the payment counts as declined if the customer has not confirmed it. Absent when the payment service did not answer

RevokeInvoiceLinksResponse

FieldTypeDescription
revoked requiredinteger

SignedUrl

FieldTypeDescription
url requiredstring · uri
expires_at requiredstring · date-timeRFC 3339, UTC