Skip to content

Confirm an invoice payment after customer authentication

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

Called with no request body after the browser has completed the 3-D Secure challenge of a requires_action answer. The server looks up the payment it recorded for this invoice itself and marks the invoice paid only if that payment succeeded for exactly this invoice and amount — the caller supplies nothing, so an invoice can never be pointed at some other payment.

Answers with the same 200 shapes as the pay endpoint (paid, requires_action, processing, canceled; the status set is open in the same way — treat a value you do not know as processing; a provider that does not answer is processing, not an error). An invoice that is already paid answers 200 paid. Unlike the pay endpoint it does not follow a re-issue: a void invoice answers 409 invoice_void, with the replacement’s id in error.invoice_id when it has one. Same role restriction as the pay endpoint. The collection switch does not stop a confirm — it only records what a payment already is.

orgID
required
string format: uuid

Organization UUID.

invoiceID
required
string format: uuid

Invoice UUID.

The state of the payment in flight (see status), with the 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 — e.g. an invalid path UUID or an unparseable JSON body.

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 payment in flight was declined; error.decline_code carries the decline code and error.invoice_id the invoice.

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"
}
}

payment_not_in_flight — the invoice has no payment in flight, so there is nothing to confirm (error.invoice_id is the invoice). invoice_void — the invoice was voided; when it was re-issued error.invoice_id is the replacement (replaced_by_invoice_id), and it is absent when nothing replaced it. invoice_not_issued — the invoice is not in the issued state (error.invoice_id is the invoice).

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 — no payment engine is available on this platform (the switch itself does not stop a confirm, see above). payment_provider_unavailable — no payment provider is configured, or it rejected the platform’s credentials; the payment’s state could not be read, so nothing is known about it (not that money did not move). 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"
}
}