Skip to content

Pair a terminal

POST
/v1/terminal_pairings
curl --request POST \
--url https://terminal.api.example.com/v1/terminal_pairings \
--header 'Content-Type: application/json' \
--data '{ "serial": "TX00000001", "pairing_code": "ABCDE-FGH12", "csr": "example", "attestation_chain": [ "example" ] }'

Called by the terminal itself, with no API key or Access Token, to exchange its Pairing Code for its client certificate. The key must be generated in the terminal’s hardware-backed Android Keystore with the code’s digest as its attestation challenge: see Pairing a terminal for how.

Every refusal is the same 401 pairing_refused, whichever check failed. Requests are limited per address. A retry with the same code and key, after a lost answer, returns the same certificate.

Media typeapplication/json
object
serial
required

The terminal’s serial number, as the device reports it.

string
pairing_code
required

The Pairing Code the operations team issued for this terminal. Case, dashes and spaces make no difference.

string
csr
required

A PKCS#10 certificate signing request in PEM, signed by the attested key. Its subject is ignored.

string
attestation_chain
required

The key’s attestation certificate chain from the Android Keystore, leaf first, each certificate as standard base64 of its DER. The root may be left off.

Array<string>
>= 1 items

The terminal’s certificate. Sent with Cache-Control: no-store.

Media typeapplication/json
object
object
required
string
Allowed value: terminal_pairing
terminal_id
required

The id of the terminal the certificate is for.

string
certificate
required

The terminal’s client certificate, in PEM, for its key. Presented over mutual TLS on the terminal’s later calls. Valid for up to 7 days: expires_at says when it ends.

string
chain
required

The certificates above it, in PEM, its issuer first.

string
expires_at
required

When the certificate expires, as a Unix timestamp.

integer
livemode
required

Always true. A terminal is a live device; there are no test terminals.

boolean
Example
{
"object": "terminal_pairing",
"terminal_id": "3f6c2a9e-8b41-4d7a-9e25-0c1b7d4f8a63",
"expires_at": 1790999400,
"livemode": true
}

validation_failure with invalid_parameter: a field is missing or not readable. param names a missing field, or is request when the CSR is not PEM or a chain entry is not base64. Also the codes any /v1 call can get for a body that is not JSON.

Media typeapplication/json
object
error
required
object
type
required
string
Allowed values: validation_failure authentication_failure authorization_failure not_found conflict timeout_or_in_progress policy_rejection invariant_violation rate_limited dependency_failure
code
required

A stable name for this refusal. Branch on this, never on message.

string
message
required

For the developer reading a log. May be reworded.

string
param
required

The field or parameter at fault, when there is one.

string | null
request_id
required
string | null
Example
{
"error": {
"type": "validation_failure"
}
}

authentication_failure with pairing_refused: the same for every refusal, whichever check failed. See Pairing a terminal for what is checked.

Media typeapplication/json
object
error
required
object
type
required
string
Allowed values: validation_failure authentication_failure authorization_failure not_found conflict timeout_or_in_progress policy_rejection invariant_violation rate_limited dependency_failure
code
required

A stable name for this refusal. Branch on this, never on message.

string
message
required

For the developer reading a log. May be reworded.

string
param
required

The field or parameter at fault, when there is one.

string | null
request_id
required
string | null
Example
{
"error": {
"type": "authentication_failure",
"code": "pairing_refused",
"message": "The pairing was refused. Ask for a new Pairing Code if this one may be spent or expired.",
"param": null,
"request_id": "req_4f1c0b2e9a7d"
}
}

rate_limited: too many pairing requests from this address. Wait for Retry-After seconds.

Media typeapplication/json
object
error
required
object
type
required
string
Allowed values: validation_failure authentication_failure authorization_failure not_found conflict timeout_or_in_progress policy_rejection invariant_violation rate_limited dependency_failure
code
required

A stable name for this refusal. Branch on this, never on message.

string
message
required

For the developer reading a log. May be reworded.

string
param
required

The field or parameter at fault, when there is one.

string | null
request_id
required
string | null
Example
{
"error": {
"type": "validation_failure"
}
}

dependency_failure with service_unavailable: the platform did not answer, or failed part-way. Whether the terminal was paired is not known. Retry the same request: the same code and key return the same certificate if it was issued, so never make a new key for the retry.

Media typeapplication/json
object
error
required
object
type
required
string
Allowed values: validation_failure authentication_failure authorization_failure not_found conflict timeout_or_in_progress policy_rejection invariant_violation rate_limited dependency_failure
code
required

A stable name for this refusal. Branch on this, never on message.

string
message
required

For the developer reading a log. May be reworded.

string
param
required

The field or parameter at fault, when there is one.

string | null
request_id
required
string | null
Example
{
"error": {
"type": "validation_failure"
}
}