Skip to content

Read a room key's UID on a registered terminal, for enrollment

POST
/v1/key-enrollment/read
curl --request POST \
--url https://api.aerandil.com/v1/key-enrollment/read \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '{ "device_id": "2489E9AD-2EE2-8E00-8EC9-32D5F69181C0" }'

The desk’s enrollment tap on a terminal (terminal-integration spec §4): triggers one NFC UID read on a registered Adyen terminal at the caller’s house and answers the card’s canonical token — uppercase hex, 8, 14 or 20 characters. It never resolves the token and never binds it: the desk binds it by applying issue_access_credential to the stay, which refuses a key still live on another stay. Carries issue_access_credential’s apply scope, since the read exists to feed it. Writes nothing, so it takes no Idempotency-Key; a repeat is another read. The request is held while someone taps, up to AERANDIL_ADYEN_READ_TIMEOUT_SECS. 501 unless AERANDIL_ADYEN_ENABLED=1.

Media typeapplication/json
object
device_id
required

The registered hospitality.device to read on: a payment_terminal at the caller’s house, active, carrying the Adyen POIID in vendor_device_ref.

string format: uuid
Examplegenerated
{
"device_id": "2489E9AD-2EE2-8E00-8EC9-32D5F69181C0"
}

A room key was read.

Media typeapplication/json
object
data
required
object
nfc_variant

The terminal’s reading of which card technology answered, when it reports one.

string | null
token
required

The card’s canonical token — what issue_access_credential takes.

string
Examplegenerated
{
"data": {
"nfc_variant": "example",
"token": "example"
}
}
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"
}
}

terminal_busy — the terminal is taking something else (a payment) or is not logged in; nothing was read and the terminal was left alone.

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

device_not_terminal, device_not_active, device_missing_vendor_ref; not_a_key_card — a payment card was tapped; tap_timed_out — nothing was tapped within the read budget, and the read was cancelled on the terminal; terminal_read_failed — the read did not succeed (cancelled on the terminal, a card error); terminal_rejected — Adyen never delivered the message (e.g. an unknown POIID); token_malformed — the UID breaks the token contract.

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 Adyen bridge is not enabled 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"
}
}

terminal_unreachable — the read never reached Adyen; terminal_gateway_error — Adyen answered with no reading.

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