Export a merchant account's stored cards for the hotel's next processor
const url = 'https://api.aerandil.com/v1/merchant-accounts/2489E9AD-2EE2-8E00-8EC9-32D5F69181C0/card-export?shape=stripe_copy';const options = {method: 'GET', headers: {Authorization: 'Bearer <token>'}};
try { const response = await fetch(url, options); const data = await response.json(); console.log(data);} catch (error) { console.error(error);}curl --request GET \ --url 'https://api.aerandil.com/v1/merchant-accounts/2489E9AD-2EE2-8E00-8EC9-32D5F69181C0/card-export?shape=stripe_copy' \ --header 'Authorization: Bearer <token>'The Leaving policy’s card export. The account’s stored cards are Stripe PaymentMethods on Customers kept on the platform’s account, so the platform files the move with Stripe: a partial copy to another Stripe account, or Stripe’s PCI export to a processor that is not Stripe (runbook products/payments/docs/leaving.md). shape=stripe_copy (the default) is exactly the file Stripe’s partial copy with CSV upload takes — “a single-column of Stripe customer IDs (standard format: cus_xxxx) in a CSV file without a header”, one Customer per line, ordered. shape=manifest is a row per card under a header (customer_id, payment_method_id, payment_card_id, person_id, reservation_id, brand, last4, expiry_month, expiry_year), ordered by Customer then PaymentMethod: the join against Stripe’s mapping file (customer_id_old, source_id_old, customer_id_new, source_id_new) and the list a PCI export request names. A card is a candidate when it names this account (merchant_account_ref), is active, is not past its expiry month on the house’s business date, is the guest’s own (physical), and has both of Stripe’s handles. Whose each Customer is is proven at Stripe, not read off the card row: before either file is written every candidate Customer is read back and kept only when it exists, is not deleted, carries the rail’s stamp naming this tenant and this account (metadata[aerandil_tenant], metadata[aerandil_merchant_account]) and holds no PaymentMethod this account has no card for; a Customer with no stamp is withheld, never assumed. Stripe copies a Customer whole, so Aerandil-Retired-Cards-Attached counts the cards Stripe still has on an exported Customer that the file does not list (detach them before filing), and a Customer that also holds another merchant’s or a channel’s card, or that Stripe does not prove this account’s, is left out and counted in Aerandil-Customers-Withheld. Every pull is recorded before the file is served — cards_exported_at and cards_exported_count on the account, and merchant_account.cards.exported with the counts and who asked. A closed account still exports. The platform operator’s (tenant_admin:write), with clearance to read payment.token (markings:payment.token:read): Stripe’s handles on a card are marked, and a bearer without the clearance is refused rather than handed a file of blanks.
Authorizations
Section titled “Authorizations”Parameters
Section titled “Parameters”Path Parameters
Section titled “Path Parameters”The property’s merchant account.
Query Parameters
Section titled “Query Parameters”stripe_copy (default): Stripe’s partial-copy upload. manifest: a row per card.
Responses
Section titled “Responses”The export, as CSV.
Example
cus_P1uBZ8nE2a7qXkcus_P1uC4Rk0xTq3LmHeaders
Section titled “Headers”Customers left out: they also hold a card another merchant, or a channel, may draw on, or Stripe does not prove them this account’s.
How many cards the export carries.
Cards Stripe still has on an exported Customer that the file does not list, which a copy of the Customer carries too.
Always no-store. Object and link state is live data and is never cacheable.
attachment, named for the account and the shape.
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" }}shape is not stripe_copy or manifest.
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" }}The card rail is not configured on this deployment, so no Customer can be proven at Stripe (stripe_not_configured).
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" }}