Skip to content

Issue the invoice for a folio

POST
/v1/folios/{folio_id}/invoice
curl --request POST \
--url https://api.aerandil.com/v1/folios/2489E9AD-2EE2-8E00-8EC9-32D5F69181C0/invoice \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '{ "issued_on": "2026-04-15", "note": "example", "recipient_address_override": { "city": "example", "country": "example", "line1": "example", "line2": "example", "postal_code": "example", "region": "example" }, "recipient_kind": "person", "recipient_name_override": "example", "recipient_tax_identifier_override": "example" }'

Turns a sealed folio into the numbered document its recipient keeps, then renders and stores the PDF. Accepts a closed folio at zero balance (the settled stay) and an open folio already transferred to a company’s A/R account (the billed stay, which this seals as it issues). A failed upload is not a failed request. The number is allocated and the document exists the moment the action commits; pdf_stored reports whether the rendering also reached object storage, and GET /v1/invoices/{invoice_id}/pdf renders on demand when it did not. Retry-safe without an Idempotency-Key: the route re-reads first, and a folio that already carries an uncredited invoice answers with that document and already_issued: true rather than asking the series for a second number.

folio_id
required
string format: uuid

The folio to project into a document.

Idempotency-Key
string
>= 1 characters

Opaque idempotency token (Stripe-shape). Repeated POSTs with the same key replay the original response within the cache TTL rather than creating a duplicate object.

Media typeapplication/json
object
issued_on
required

The property’s business date of issue — the document’s date for tax purposes, never the wall clock.

string format: date
note

Printed in the document’s footer.

string
recipient_address_override

Print this address instead of the profile’s.

object
city
string
country
string
line1
string
line2
string
postal_code
string
region
string
recipient_kind

Defaults to the company when the folio was settled to A/R or the reservation carries one, else the guest.

string
Allowed values: person company
recipient_name_override
string
recipient_tax_identifier_override
string

Issued.

Media typeapplication/json
object
already_issued
required

True when the folio already carried an uncredited invoice and this call returned it rather than issuing a second one.

boolean
amount_due
object
amount
required

Exact decimal as a string. Money is never a float — binary floating point cannot hold 0.10.

string format: decimal
currency
required
string format: iso-4217
gross_total
object
amount
required

Exact decimal as a string. Money is never a float — binary floating point cannot hold 0.10.

string format: decimal
currency
required
string format: iso-4217
invoice_id
required
string format: uuid
issued_on
required
string format: date
kind
string
Allowed values: invoice credit_note
number
required

E.g. STAN-2026-000123.

string
pdf_stored
required

Whether the rendering reached object storage. False is not an error — the document exists either way, and the pdf route renders on demand.

boolean
Example
{
"kind": "invoice"
}
Cache-Control
string

Always no-store. Object and link state is live data and is never cacheable.

The bearer authenticated but does not hold the scope this operation requires.

Media typeapplication/json

Structured error wrapper. Every non-2xx response from any Aerandil endpoint serialises to this shape; the HTTP status carries the failure class (4xx/5xx), the body adds a stable code for programmatic dispatch plus a human-readable message.

object
error
required
object
code
required

Stable, programmatic error code (e.g. unauthorized, forbidden, not_found, invalid_body, invalid_url, precondition_failed, no_secondary_key). Clients dispatch on code, not on the message (which may be localised or contextualised in future versions).

string
instance_id

Request-id correlation handle echoed on the request-id response header — support teams correlate without scraping logs.

string
message
required

Human-readable explanation of the failure.

string
parameters

Per-error structured detail. The shape varies by code (e.g. forbidden_action carries {"action": "..."}; precondition_failed carries {"current_version": N}).

Examplegenerated
{
"error": {
"code": "example",
"instance_id": "example",
"message": "example",
"parameters": "example"
}
}

No such object in the caller’s tenant — which is also the answer when it exists in another tenant, by design.

Media typeapplication/json

Structured error wrapper. Every non-2xx response from any Aerandil endpoint serialises to this shape; the HTTP status carries the failure class (4xx/5xx), the body adds a stable code for programmatic dispatch plus a human-readable message.

object
error
required
object
code
required

Stable, programmatic error code (e.g. unauthorized, forbidden, not_found, invalid_body, invalid_url, precondition_failed, no_secondary_key). Clients dispatch on code, not on the message (which may be localised or contextualised in future versions).

string
instance_id

Request-id correlation handle echoed on the request-id response header — support teams correlate without scraping logs.

string
message
required

Human-readable explanation of the failure.

string
parameters

Per-error structured detail. The shape varies by code (e.g. forbidden_action carries {"action": "..."}; precondition_failed carries {"current_version": N}).

Examplegenerated
{
"error": {
"code": "example",
"instance_id": "example",
"message": "example",
"parameters": "example"
}
}

The folio cannot be invoiced. The reason is one of INVOICE_BLOCKERS: already_invoiced, folio_open, folio_not_invoiceable, recipient_address_missing, recipient_unknown, issuer_not_configured, folio_empty, rebate_not_a_credit. preview_invoice returns the same list without allocating anything.

Media typeapplication/json

Structured error wrapper. Every non-2xx response from any Aerandil endpoint serialises to this shape; the HTTP status carries the failure class (4xx/5xx), the body adds a stable code for programmatic dispatch plus a human-readable message.

object
error
required
object
code
required

Stable, programmatic error code (e.g. unauthorized, forbidden, not_found, invalid_body, invalid_url, precondition_failed, no_secondary_key). Clients dispatch on code, not on the message (which may be localised or contextualised in future versions).

string
instance_id

Request-id correlation handle echoed on the request-id response header — support teams correlate without scraping logs.

string
message
required

Human-readable explanation of the failure.

string
parameters

Per-error structured detail. The shape varies by code (e.g. forbidden_action carries {"action": "..."}; precondition_failed carries {"current_version": N}).

Examplegenerated
{
"error": {
"code": "example",
"instance_id": "example",
"message": "example",
"parameters": "example"
}
}