Skip to content

Record the card a completed SetupIntent saved

POST
/v1/persons/{person_id}/payment-cards
curl --request POST \
--url https://api.aerandil.com/v1/persons/2489E9AD-2EE2-8E00-8EC9-32D5F69181C0/payment-cards \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '{ "capture_kind": "terminal", "reservation_id": "2489E9AD-2EE2-8E00-8EC9-32D5F69181C0", "retain_until": "2026-04-15", "setup_intent_id": "example" }'

Takes the SetupIntent id — never the card’s details — and reads the brand, last four, expiry, funding and wallet back off the processor, because a page that collected the card could otherwise claim any brand and last four it liked. What the caller does supply is the pair of facts the processor cannot know: how the card was captured (Stripe cannot tell a guest typing on their own phone from an agent typing at the counter, and the difference decides chargeback liability) and how long this jurisdiction says it may be kept.

person_id
required
string format: uuid

The guest the card binds to.

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
capture_kind
required

How the card reached the processor. The desk knows this and the processor does not, and the difference decides interchange, chargeback liability, and whether SCA was satisfied.

string
Allowed values: terminal keyed_by_staff guest_device imported
reservation_id

The booking this card also guarantees, when it does.

string format: uuid
retain_until
required

Storage-limitation clock. The desk’s jurisdiction decides it.

string format: date
setup_intent_id
required

The SetupIntent the browser just completed. Not the card’s details — those are read back from the processor.

string

The card is on file. The response carries the display triple and nothing else — the token is payment.token-marked and this route is not the way around that.

Media typeapplication/json
object
brand
required
string
Allowed values: visa mastercard amex discover diners jcb unionpay cartes_bancaires eftpos_au interac maestro unknown
expiry_month
required
integer
>= 1 <= 12
expiry_year
required
integer
last4
required

For a wallet card this is the device account number’s last four, not the funding card’s — the desk must say so rather than read it out as the guest’s card.

string
payment_card_id
required
string format: uuid
wallet
string | null
Allowed values: apple_pay google_pay samsung_pay link click_to_pay other
Example
{
"brand": "visa",
"wallet": "apple_pay"
}
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"
}
}

card_not_collected — the intent has saved no card yet, because the guest has not completed the form; invalid_setup_intent_id — the id is not shaped seti_ plus letters and digits, refused before the processor is asked; setup_intent_mismatch — the processor answered with another intent.

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 card rail is configured on this deployment.

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

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 card was not stored. Try again after Retry-After.

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"
}
}
Retry-After
integer

Seconds to wait before trying the same request again (RFC 9110 §10.2.3) — the queue’s first backoff.