Pay an issued invoice
const url = 'https://api.aetherplatform.cloud/api/v1/organizations/2489E9AD-2EE2-8E00-8EC9-32D5F69181C0/billing/invoices/2489E9AD-2EE2-8E00-8EC9-32D5F69181C0/pay';const options = { method: 'POST', headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'}, body: '{"discount_code":"example"}'};
try { const response = await fetch(url, options); const data = await response.json(); console.log(data);} catch (error) { console.error(error);}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 withclient_secret, then call…/pay/confirm. A later call to this endpoint while that challenge is still open returns the sameclient_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.
Authorizations
Section titled “Authorizations”Parameters
Section titled “Parameters”Path Parameters
Section titled “Path Parameters”Organization UUID.
Invoice UUID.
Request Body
Section titled “Request Body”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
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).
Examplegenerated
{ "discount_code": "example"}Responses
Section titled “Responses”The outcome of the payment attempt (see status), with the live invoice.
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
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.
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
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.
The tax rate applied, in basis points (1900 = 19 %); 0 when no tax is charged.
The statement printed on the invoice explaining the tax treatment (for example why no VAT is charged); empty when there is none.
The amount owed, and exactly the amount a payment charges — there is no separate charged amount.
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.
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
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.
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
The address of a hosted PDF, when one exists; omitted otherwise. No invoice carries one today.
When the invoice was paid; omitted until it is.
How many payment attempts have been started for this invoice (automatic and manual together). Copied onto a replacement when the invoice is re-issued.
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.
When the most recent payment attempt was made; omitted when there has been none.
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.
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.
When this document was issued. A replacement carries its own, later issued_at.
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.
When the invoice was voided; omitted unless status is void.
On a replacement: the id of the voided invoice it replaces. Omitted on an invoice issued at month close.
On a voided invoice: the id of the invoice that replaced it — the one to show and to pay. Omitted otherwise.
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.
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.
object
object
A stable, machine-readable error code.
object
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.
The payment provider’s decline code. Present only on 402 payment_declined; it is its own field and is never folded into message.
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.
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.
object
object
A stable, machine-readable error code.
object
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.
The payment provider’s decline code. Present only on 402 payment_declined; it is its own field and is never folded into message.
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.
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.
object
object
A stable, machine-readable error code.
object
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.
The payment provider’s decline code. Present only on 402 payment_declined; it is its own field and is never folded into message.
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.
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).
object
object
A stable, machine-readable error code.
object
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.
The payment provider’s decline code. Present only on 402 payment_declined; it is its own field and is never folded into message.
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.
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.
object
object
A stable, machine-readable error code.
object
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.
The payment provider’s decline code. Present only on 402 payment_declined; it is its own field and is never folded into message.
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.
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.
object
object
A stable, machine-readable error code.
object
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.
The payment provider’s decline code. Present only on 402 payment_declined; it is its own field and is never folded into message.
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.
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.
object
object
A stable, machine-readable error code.
object
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.
The payment provider’s decline code. Present only on 402 payment_declined; it is its own field and is never folded into message.
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.
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" }}