Collect an assessed no-show fee off the stored card
const url = 'https://api.aerandil.com/v1/reservations/2489E9AD-2EE2-8E00-8EC9-32D5F69181C0/no-show-fee/collection';const options = { method: 'POST', headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'}, body: '{"payment_card_id":"2489E9AD-2EE2-8E00-8EC9-32D5F69181C0"}'};
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.aerandil.com/v1/reservations/2489E9AD-2EE2-8E00-8EC9-32D5F69181C0/no-show-fee/collection \ --header 'Authorization: Bearer <token>' \ --header 'Content-Type: application/json' \ --data '{ "payment_card_id": "2489E9AD-2EE2-8E00-8EC9-32D5F69181C0" }'The night audit assesses the fee and posts the charge; this is the leg that moves the money, off-session against the card the guest left. The booking’s own card hold pays first: a completed preauthorization’s pending hold is captured for the fee (or all of it, when it is smaller — Stripe releases the rest), and the card is charged only for what it could not cover; paid_by says which. Name a card with payment_card_id or let the route pick — the one bound to this reservation first, then any active consented card the guest has on file (but not a card a pay page kept for another booking: its terms covered that booking), which is what makes it callable in a loop straight after the audit. The verb that records the outcome (collect_no_show_fee) is the card rail’s own: this route is the only way to it, under actions:apply or its own grant actions:hospitality.action.collect_no_show_fee:apply. Two of the three outcomes are not errors, so all three answer 200 with an outcome rather than turning a card decision into an HTTP failure. authentication_required is the one to read carefully: the issuer demanded SCA on a charge with nobody present, which is not a decline — the fee stays on the books at full value and is collectable once the guest authenticates. A caller that treats it as a refusal writes off revenue it could still take.
Authorizations
Section titled “Authorizations”Parameters
Section titled “Parameters”Path Parameters
Section titled “Path Parameters”The no-showed reservation carrying the assessed fee.
Header Parameters
Section titled “Header Parameters”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.
Request Body
Section titled “Request Body”object
Charge this card specifically. Omit it and the route picks the card bound to the reservation, then any active consented card the guest has on file.
Examplegenerated
{ "payment_card_id": "2489E9AD-2EE2-8E00-8EC9-32D5F69181C0"}Responses
Section titled “Responses”The attempt was made and its outcome recorded — collected, authentication_required, or uncollectible.
object
The assessed fee, as a decimal string.
True when the outcome is authentication_required and the card rail recorded the pending authentication (payment.authentication_pending): a payment request with reason authentication_required carries the house’s deadline, and its page goes to the guest at a house with the chase on (set_sca_chase). Paid, it collects the fee.
Why it did not, in the processor’s own words.
What the booking’s hold has paid toward the fee, as a decimal string (0 when there was none).
authentication_required is not a decline — the issuer wants SCA, the fee is still owed at full value, and it is collectable once the guest authenticates.
Which path paid a collected fee — booking_hold, stored_card or booking_hold_and_stored_card: the booking’s own card hold (captured for the fee, the rest released), the stored card off-session, or the hold and then the card for what the hold could not cover.
The processor’s reference when money moved — the only handle that survives into a dispute.
Example
{ "outcome": "collected"}Headers
Section titled “Headers”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.
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
object
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).
Request-id correlation handle echoed on the request-id response header — support teams correlate without scraping logs.
Human-readable explanation of the failure.
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.
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
object
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).
Request-id correlation handle echoed on the request-id response header — support teams correlate without scraping logs.
Human-readable explanation of the failure.
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" }}merchant_account_not_ready — the property’s card payments settle to its own merchant account, which Stripe has not yet let take charges; or processor_key_reused — a retry of an attempt that failed before its outcome was recorded reached Stripe on different terms (the property’s account opened or closed, or its fee moved). Stripe refused it rather than charging twice; the first attempt may have taken the money, so check the Stripe dashboard first.
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
object
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).
Request-id correlation handle echoed on the request-id response header — support teams correlate without scraping logs.
Human-readable explanation of the failure.
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_show_fee_not_assessed — nothing was assessed for this reservation; no_show_fee_already_collected — the money moved, and returning it is a refund rather than another collection; or payment_card_not_chargeable — a card named through payment_card_id that is expired or removed, unconsented, or tokenless. A card the caller chose and cannot charge is a mistake to correct rather than an uncollectible fee to write down. A fee left authentication_required or uncollectible is not refused — both are retryable, which is the point of recording them separately from collected. Note that the fee’s own state is the at-most-once guard rather than Idempotency-Key: replaying a key after a successful collection answers no_show_fee_already_collected, not the original 200. The money still moves exactly once.
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
object
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).
Request-id correlation handle echoed on the request-id response header — support teams correlate without scraping logs.
Human-readable explanation of the failure.
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 card rail is configured on this deployment.
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
object
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).
Request-id correlation handle echoed on the request-id response header — support teams correlate without scraping logs.
Human-readable explanation of the failure.
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" }}rail_unavailable — the card processor could not be reached (a transport failure, a 5xx, a 429, or this request’s key still running at the processor). The fee is not queued: it stands, and the desk or the next night audit takes it again. Try again after Retry-After.
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
object
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).
Request-id correlation handle echoed on the request-id response header — support teams correlate without scraping logs.
Human-readable explanation of the failure.
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" }}Headers
Section titled “Headers”Seconds to wait before trying the same request again (RFC 9110 §10.2.3) — the queue’s first backoff.