Skip to content

Pay an issued invoice

POST
/api/v1/organizations/{orgID}/billing/invoices/{invoiceID}/pay
curl --request POST \
--url https://api.aetherplatform.cloud/api/v1/organizations/2489E9AD-2EE2-8E00-8EC9-32D5F69181C0/billing/invoices/2489E9AD-2EE2-8E00-8EC9-32D5F69181C0/pay \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '{ "discount_code": "example" }'

Charges the invoice’s total_cents to the organization’s default payment method — the caller cannot name another card. Restricted to members with the owner or admin role; API tokens are refused.

The invoice that is charged may not be the one in the URL. If the invoice in the URL is void because it was re-issued, the call follows replaced_by_invoice_id and pays the live invoice. With a discount_code, the code is first redeemed for the organization and the invoice is voided and re-issued under the next invoice number at the discounted total, and the replacement is what is charged. In both cases the invoice in the response — and error.invoice_id in an error — is the live invoice; read the id and number from there, not from the request.

A discount_code is only consumed once the call can proceed: with no default payment method the answer is 402 payment_method_required and nothing is redeemed, voided or re-issued. A code that is already this organization’s own active discount is accepted (so a retry after a decline with the code still in the form is not blocked). When the invoice already carries that discount, or the code discounts none of its lines, the existing invoice is charged and no re-issue happens. A re-issue whose total is 0 is paid at once, with no charge.

The answer is always 200 when no error occurred, and status says what happened:

  • paid — the invoice is paid.
  • requires_action — the card needs the customer (3-D Secure). Complete it in the browser with client_secret, then call …/pay/confirm. A later call to this endpoint while that challenge is still open returns the same client_secret.
  • processing — a payment is in flight and there is nothing the caller can act on (the payment provider is still processing it, an automatic charge is under way, or another payment call for this organization is still running). Nothing new was started; check again later. It is also the answer when the payment provider did not answer after the attempt was created (a timeout, a provider-side 5xx, a rate limit): the attempt may still succeed, so it is reported as in progress rather than as an error — do not start another payment beside it; check the invoice, or call …/pay/confirm.
  • canceled — the payment provider reports the attempt called off for a reason that is not a decline (the intent was canceled). Nothing was recorded against the invoice and it can be paid again.

Two concurrent calls for the same invoice produce one charge; the second answers with what it observes, which can be requires_action or processing rather than paid. Treat the status set as open: additional values may be added, and a client must treat a value it does not know as processing.

A declined card is 402 payment_declined with the decline code in error.decline_code.

orgID
required
string format: uuid

Organization UUID.

invoiceID
required
string format: uuid

Invoice UUID.

Media typeapplication/json

The body of the pay endpoint. It may be omitted or empty. There is no way to name a card: the organization’s default payment method is charged.

object
discount_code

Optional. A discount code to apply to this invoice before paying it: the code is redeemed for the organization, the invoice is voided and re-issued at the discounted total, and the replacement is charged. Surrounding whitespace is ignored; a blank value is the same as omitting it (a plain payment, not an error).

string
Examplegenerated
{
"discount_code": "example"
}

The outcome of the payment attempt (see status), with the live invoice.

Media typeapplication/json

The 200 answer of the pay and pay/confirm endpoints. invoice is the live invoice the answer is about — the one that was charged; its id and number can differ from the invoice in the request URL (a re-issue, or a voided invoice that was followed to its replacement).

object
status
required

What the payment attempt came to. paid: the invoice is paid. requires_action: the customer must authenticate (3-D Secure) with client_secret, then call pay/confirm. processing: a payment is in flight and the caller has nothing to act on; check again later; it is also what a payment whose outcome the payment provider has not reported yet (it did not answer) is called. canceled: the attempt was called off without a decline; nothing was recorded and the invoice can be paid again. This set is open — additional values may be added, and a concurrent call can answer with any of the non-final values; a client must treat a value it does not know as processing. A declined card is not a status here: it is 402 payment_declined.

string
Allowed values: paid requires_action processing canceled
invoice
required

An invoice as issued. The document is this record: issuer, buyer and tax rule are frozen at issue time and an issued, numbered invoice is never edited — a change (a discount code applied to it) voids it and issues a replacement under the next number, the two linked by replaces_invoice_id / replaced_by_invoice_id. A billing period has exactly one invoice that is not void.

object
id
required
string format: uuid
invoice_number
required

The customer-facing identity, AE-YYYY-NNNN, from a gapless series. Assigned once at issue and never reused; a re-issue gets the next number and the voided invoice keeps its own.

string
org_id
required
string format: uuid
period_start
required
string format: date-time
period_end
required
string format: date-time
subtotal_cents
required
integer format: int64
tax_cents
required
integer format: int64
tax_rate_bp
required

The tax rate applied, in basis points (1900 = 19 %); 0 when no tax is charged.

integer
tax_note
required

The statement printed on the invoice explaining the tax treatment (for example why no VAT is charged); empty when there is none.

string
total_cents
required

The amount owed, and exactly the amount a payment charges — there is no separate charged amount.

integer format: int64
status
required

issued until paid, then paid; void for an invoice that was re-issued (see replaced_by_invoice_id). A failed payment is not a status: the invoice stays issued and the failure is recorded in last_payment_error. An invoice whose total is 0 is paid from the moment it is issued. Clients must tolerate a value they do not know.

string
issuer
required

The selling entity as it was when the invoice was issued. A historical record: every field is always present, as an empty string when it has no value.

object
legal_name
required
string
address_line1
required
string
address_line2
required
string
city
required
string
postal_code
required
string
country_code
required
string
company_number
required
string
vat_registered
required

Whether the issuer was VAT-registered at issue time. It does not by itself mean VAT was charged — see the invoice’s tax_rate_bp.

boolean
vat_id
required
string
tax_note
required
string
buyer
required

The customer as they were when the invoice was issued. A historical record: every field is always present, as an empty string when it has no value. Contains personal data (names, addresses, email).

object
org_id
required
string format: uuid
name
required
string
customer_type
required
string
legal_name
required
string
first_name
required
string
last_name
required
string
billing_address
required
string
primary_contact
required
string
billing_email
required
string
vat_id
required
string
country_code
required
string
pdf_url

The address of a hosted PDF, when one exists; omitted otherwise. No invoice carries one today.

string | null
created_at
required
string format: date-time
paid_at

When the invoice was paid; omitted until it is.

string format: date-time
payment_attempts
required

How many payment attempts have been started for this invoice (automatic and manual together). Copied onto a replacement when the invoice is re-issued.

integer
last_payment_error

A sanitised decline code or short message from the most recent failed attempt — never a raw payment-provider error. Omitted when no attempt has failed; cleared when the invoice is paid.

string
last_payment_at

When the most recent payment attempt was made; omitted when there has been none.

string format: date-time
payment_in_flight
required

True while a payment for this invoice has been started and has not reached a final outcome (for example while a 3-D Secure challenge is open). No second payment is started while this is true; the pay endpoint answers requires_action or processing instead.

boolean
discount_id

The organization’s discount (OrgDiscount.id) that was active when the invoice was issued, whether or not its scope matched any line; omitted when there was none. Provenance only — what the discount did is in each line’s discount_cents.

string format: uuid
issued_at
required

When this document was issued. A replacement carries its own, later issued_at.

string format: date-time
due_clock_start_at
required

Where the payment period counts from. Equal to issued_at for an invoice issued at month close, and copied unchanged onto a replacement, so re-issuing an invoice never extends the time to pay.

string format: date-time
voided_at

When the invoice was voided; omitted unless status is void.

string format: date-time
replaces_invoice_id

On a replacement: the id of the voided invoice it replaces. Omitted on an invoice issued at month close.

string format: uuid
replaced_by_invoice_id

On a voided invoice: the id of the invoice that replaced it — the one to show and to pay. Omitted otherwise.

string format: uuid
client_secret

Present only with status: requires_action: the payment provider’s client secret, with which the browser completes the 3-D Secure challenge. A credential for that one payment; do not log or store it.

string
Example
{
"status": "paid"
}

Malformed request (an invalid path UUID or an unparseable body), or discount_code_invalid — the discount_code does not exist or is not available (a disabled code answers exactly like an unknown one). Nothing was redeemed, re-issued or charged, and error.invoice_id is absent.

Media typeapplication/json
object
error
required
object
code
required

A stable, machine-readable error code.

string
message
required
string
details
Array<object>
object
field
required
string
code
required
string
message
required
string
status_url

The platform status page (https://status.aetherplatform.cloud). Present on 5xx answers written by the API’s error writer and on no other; absent, not null, on 4xx.

string format: uri
decline_code

The payment provider’s decline code. Present only on 402 payment_declined; it is its own field and is never folded into message.

string
invoice_id

Present on exactly these errors of the invoice pay and pay/confirm endpoints: 402 payment_declined, 402 payment_method_required, 409 invoice_already_paid, 409 invoice_not_issued, 409 invoice_below_minimum, 409 payment_not_in_flight, and 409 invoice_void when the void invoice was replaced. It is the id of the live invoice, which after a re-issue is the replacement and not the id in the request URL (on 409 invoice_void from pay/confirm it is the replacement’s id). Absent on every other error — 400 (including discount_code_invalid), 403, 404, the discount_* 409s, 409 invoice_void with no replacement (nothing live to name), every 5xx and 503 code — and never null.

string format: uuid
Examplegenerated
{
"error": {
"code": "example",
"message": "example",
"details": [
{
"field": "example",
"code": "example",
"message": "example"
}
],
"status_url": "https://example.com",
"decline_code": "example",
"invoice_id": "2489E9AD-2EE2-8E00-8EC9-32D5F69181C0"
}
}

Missing or invalid credentials.

Media typeapplication/json
object
error
required
object
code
required

A stable, machine-readable error code.

string
message
required
string
details
Array<object>
object
field
required
string
code
required
string
message
required
string
status_url

The platform status page (https://status.aetherplatform.cloud). Present on 5xx answers written by the API’s error writer and on no other; absent, not null, on 4xx.

string format: uri
decline_code

The payment provider’s decline code. Present only on 402 payment_declined; it is its own field and is never folded into message.

string
invoice_id

Present on exactly these errors of the invoice pay and pay/confirm endpoints: 402 payment_declined, 402 payment_method_required, 409 invoice_already_paid, 409 invoice_not_issued, 409 invoice_below_minimum, 409 payment_not_in_flight, and 409 invoice_void when the void invoice was replaced. It is the id of the live invoice, which after a re-issue is the replacement and not the id in the request URL (on 409 invoice_void from pay/confirm it is the replacement’s id). Absent on every other error — 400 (including discount_code_invalid), 403, 404, the discount_* 409s, 409 invoice_void with no replacement (nothing live to name), every 5xx and 503 code — and never null.

string format: uuid
Examplegenerated
{
"error": {
"code": "example",
"message": "example",
"details": [
{
"field": "example",
"code": "example",
"message": "example"
}
],
"status_url": "https://example.com",
"decline_code": "example",
"invoice_id": "2489E9AD-2EE2-8E00-8EC9-32D5F69181C0"
}
}

payment_declined — the card was declined; error.decline_code carries the decline code and error.invoice_id the invoice that was charged (the replacement, when a discount_code re-issued it before the decline). payment_method_required — the organization has no default payment method; error.invoice_id is the live invoice. This is answered before a discount_code is redeemed, so nothing was redeemed, voided or re-issued on that path.

Media typeapplication/json
object
error
required
object
code
required

A stable, machine-readable error code.

string
message
required
string
details
Array<object>
object
field
required
string
code
required
string
message
required
string
status_url

The platform status page (https://status.aetherplatform.cloud). Present on 5xx answers written by the API’s error writer and on no other; absent, not null, on 4xx.

string format: uri
decline_code

The payment provider’s decline code. Present only on 402 payment_declined; it is its own field and is never folded into message.

string
invoice_id

Present on exactly these errors of the invoice pay and pay/confirm endpoints: 402 payment_declined, 402 payment_method_required, 409 invoice_already_paid, 409 invoice_not_issued, 409 invoice_below_minimum, 409 payment_not_in_flight, and 409 invoice_void when the void invoice was replaced. It is the id of the live invoice, which after a re-issue is the replacement and not the id in the request URL (on 409 invoice_void from pay/confirm it is the replacement’s id). Absent on every other error — 400 (including discount_code_invalid), 403, 404, the discount_* 409s, 409 invoice_void with no replacement (nothing live to name), every 5xx and 503 code — and never null.

string format: uuid
Examplegenerated
{
"error": {
"code": "example",
"message": "example",
"details": [
{
"field": "example",
"code": "example",
"message": "example"
}
],
"status_url": "https://example.com",
"decline_code": "example",
"invoice_id": "2489E9AD-2EE2-8E00-8EC9-32D5F69181C0"
}
}

insufficient_role — the caller is a member or viewer. token_not_permitted — the request was authenticated with an API token. Also answered by the organization role policy (see Authorization): forbidden (not a member of the organization), insufficient_role (the caller’s organization role is below the operation’s minimum) or token_not_permitted (an API token on an operation tokens may not use).

Media typeapplication/json
object
error
required
object
code
required

A stable, machine-readable error code.

string
message
required
string
details
Array<object>
object
field
required
string
code
required
string
message
required
string
status_url

The platform status page (https://status.aetherplatform.cloud). Present on 5xx answers written by the API’s error writer and on no other; absent, not null, on 4xx.

string format: uri
decline_code

The payment provider’s decline code. Present only on 402 payment_declined; it is its own field and is never folded into message.

string
invoice_id

Present on exactly these errors of the invoice pay and pay/confirm endpoints: 402 payment_declined, 402 payment_method_required, 409 invoice_already_paid, 409 invoice_not_issued, 409 invoice_below_minimum, 409 payment_not_in_flight, and 409 invoice_void when the void invoice was replaced. It is the id of the live invoice, which after a re-issue is the replacement and not the id in the request URL (on 409 invoice_void from pay/confirm it is the replacement’s id). Absent on every other error — 400 (including discount_code_invalid), 403, 404, the discount_* 409s, 409 invoice_void with no replacement (nothing live to name), every 5xx and 503 code — and never null.

string format: uuid
Examplegenerated
{
"error": {
"code": "example",
"message": "example",
"details": [
{
"field": "example",
"code": "example",
"message": "example"
}
],
"status_url": "https://example.com",
"decline_code": "example",
"invoice_id": "2489E9AD-2EE2-8E00-8EC9-32D5F69181C0"
}
}

The requested resource was not found.

Media typeapplication/json
object
error
required
object
code
required

A stable, machine-readable error code.

string
message
required
string
details
Array<object>
object
field
required
string
code
required
string
message
required
string
status_url

The platform status page (https://status.aetherplatform.cloud). Present on 5xx answers written by the API’s error writer and on no other; absent, not null, on 4xx.

string format: uri
decline_code

The payment provider’s decline code. Present only on 402 payment_declined; it is its own field and is never folded into message.

string
invoice_id

Present on exactly these errors of the invoice pay and pay/confirm endpoints: 402 payment_declined, 402 payment_method_required, 409 invoice_already_paid, 409 invoice_not_issued, 409 invoice_below_minimum, 409 payment_not_in_flight, and 409 invoice_void when the void invoice was replaced. It is the id of the live invoice, which after a re-issue is the replacement and not the id in the request URL (on 409 invoice_void from pay/confirm it is the replacement’s id). Absent on every other error — 400 (including discount_code_invalid), 403, 404, the discount_* 409s, 409 invoice_void with no replacement (nothing live to name), every 5xx and 503 code — and never null.

string format: uuid
Examplegenerated
{
"error": {
"code": "example",
"message": "example",
"details": [
{
"field": "example",
"code": "example",
"message": "example"
}
],
"status_url": "https://example.com",
"decline_code": "example",
"invoice_id": "2489E9AD-2EE2-8E00-8EC9-32D5F69181C0"
}
}

The invoice cannot be paid. Four codes concern the invoice; the first three carry error.invoice_id (the live invoice): invoice_already_paid — it is already paid (also the answer when a payment completed while a discount_code was being applied; the discount stays redeemed for later invoices). invoice_not_issued — it is not in the issued state. invoice_below_minimum — its total is positive but below the payment provider’s minimum chargeable amount (€0.50); it is settled by the platform operator, not through this endpoint. invoice_void — the invoice is void and nothing replaced it (a void invoice that was replaced is followed to the replacement, not refused), so there is no live invoice to name and error.invoice_id is absent. With a discount_code also: discount_code_expired, discount_code_exhausted, discount_already_active (the organization holds a different active discount) and discount_already_redeemed — in each case nothing was re-issued or charged, and error.invoice_id is absent.

Media typeapplication/json
object
error
required
object
code
required

A stable, machine-readable error code.

string
message
required
string
details
Array<object>
object
field
required
string
code
required
string
message
required
string
status_url

The platform status page (https://status.aetherplatform.cloud). Present on 5xx answers written by the API’s error writer and on no other; absent, not null, on 4xx.

string format: uri
decline_code

The payment provider’s decline code. Present only on 402 payment_declined; it is its own field and is never folded into message.

string
invoice_id

Present on exactly these errors of the invoice pay and pay/confirm endpoints: 402 payment_declined, 402 payment_method_required, 409 invoice_already_paid, 409 invoice_not_issued, 409 invoice_below_minimum, 409 payment_not_in_flight, and 409 invoice_void when the void invoice was replaced. It is the id of the live invoice, which after a re-issue is the replacement and not the id in the request URL (on 409 invoice_void from pay/confirm it is the replacement’s id). Absent on every other error — 400 (including discount_code_invalid), 403, 404, the discount_* 409s, 409 invoice_void with no replacement (nothing live to name), every 5xx and 503 code — and never null.

string format: uuid
Examplegenerated
{
"error": {
"code": "example",
"message": "example",
"details": [
{
"field": "example",
"code": "example",
"message": "example"
}
],
"status_url": "https://example.com",
"decline_code": "example",
"invoice_id": "2489E9AD-2EE2-8E00-8EC9-32D5F69181C0"
}
}

collection_disabled — invoice collection is switched off on this platform; answered before anything else is read, so no code is redeemed and no invoice re-issued. payment_provider_unavailable — no payment provider is configured on this platform, or the provider rejected the platform’s credentials. Both platform-side: nothing about the invoice or the card. When it is known up front (nothing configured) it is answered before anything is read, so no code is redeemed; a rejection by the provider can surface during the charge itself, after a discount_code was redeemed and the invoice re-issued — the retry then pays the replacement. Neither carries error.invoice_id. The error carries status_url.

Media typeapplication/json
object
error
required
object
code
required

A stable, machine-readable error code.

string
message
required
string
details
Array<object>
object
field
required
string
code
required
string
message
required
string
status_url

The platform status page (https://status.aetherplatform.cloud). Present on 5xx answers written by the API’s error writer and on no other; absent, not null, on 4xx.

string format: uri
decline_code

The payment provider’s decline code. Present only on 402 payment_declined; it is its own field and is never folded into message.

string
invoice_id

Present on exactly these errors of the invoice pay and pay/confirm endpoints: 402 payment_declined, 402 payment_method_required, 409 invoice_already_paid, 409 invoice_not_issued, 409 invoice_below_minimum, 409 payment_not_in_flight, and 409 invoice_void when the void invoice was replaced. It is the id of the live invoice, which after a re-issue is the replacement and not the id in the request URL (on 409 invoice_void from pay/confirm it is the replacement’s id). Absent on every other error — 400 (including discount_code_invalid), 403, 404, the discount_* 409s, 409 invoice_void with no replacement (nothing live to name), every 5xx and 503 code — and never null.

string format: uuid
Examplegenerated
{
"error": {
"code": "example",
"message": "example",
"details": [
{
"field": "example",
"code": "example",
"message": "example"
}
],
"status_url": "https://example.com",
"decline_code": "example",
"invoice_id": "2489E9AD-2EE2-8E00-8EC9-32D5F69181C0"
}
}