Skip to content

Export a merchant account's stored cards for the hotel's next processor

GET
/v1/merchant-accounts/{merchant_account_id}/card-export
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.

merchant_account_id
required
string format: uuid

The property’s merchant account.

shape
string
default: stripe_copy
Allowed values: stripe_copy manifest

stripe_copy (default): Stripe’s partial-copy upload. manifest: a row per card.

The export, as CSV.

Media typetext/csv
string
Example
cus_P1uBZ8nE2a7qXk
cus_P1uC4Rk0xTq3Lm
Aerandil-Customers-Withheld
integer

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.

Aerandil-Export-Cards
integer

How many cards the export carries.

Aerandil-Retired-Cards-Attached
integer

Cards Stripe still has on an exported Customer that the file does not list, which a copy of the Customer carries too.

Cache-Control
string

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

Content-Disposition
string

attachment, named for the account and the shape.

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

shape is not stripe_copy or manifest.

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 card rail is not configured on this deployment, so no Customer can be proven at Stripe (stripe_not_configured).

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