A valid request URL is required to generate request examples{
"id": "reg-pilot-1-printer",
"label": "Imprimante ticket",
"kind": "printer",
"store": "Boutique Rivoli",
"storeId": "store-rivoli",
"register": "Caisse 1",
"status": "disconnected",
"lastSeen": "2026-07-13T09:00:00.000Z"
}{
"error": {
"code": "VALIDATION_FAILED",
"message": "<string>",
"statusCode": 123,
"fieldErrors": [
{
"field": "<string>",
"message": "<string>"
}
]
}
}{
"error": {
"code": "VALIDATION_FAILED",
"message": "<string>",
"statusCode": 123
}
}{
"error": {
"code": "VALIDATION_FAILED",
"message": "<string>",
"statusCode": 123
}
}{
"error": {
"code": "VALIDATION_FAILED",
"message": "<string>",
"statusCode": 123
}
}{
"error": {
"code": "VALIDATION_FAILED",
"message": "<string>",
"statusCode": 123
}
}Pair a device onto a register
Registers a new peripheral against a store’s register. This is an ONBOARDING operation performed while someone is physically installing hardware — it records the pairing, it does not connect to the device, so the paired row is bookkeeping until the till itself reports a link. The id is caller-supplied and permanent (use a stable slug such as reg-pilot-1-printer); 409s when a device already carries it. status defaults to disconnected, which is correct for a device that is not online yet — do not claim ready you have not observed. Do NOT send lastSeen: the server stamps it from its own clock and ignores yours. SEND storeId: it is the id of the store from GET /v1/stores and it is what makes the device joinable to its shop. store and register are free-text display labels — set store to that store’s name and copy the register string the other devices on that register already use, so the listing groups them together. Omitting storeId pairs a device no screen can attribute to a shop except by matching its typed label, which is how a healthy register ends up counted nowhere.
A valid request URL is required to generate request examples{
"id": "reg-pilot-1-printer",
"label": "Imprimante ticket",
"kind": "printer",
"store": "Boutique Rivoli",
"storeId": "store-rivoli",
"register": "Caisse 1",
"status": "disconnected",
"lastSeen": "2026-07-13T09:00:00.000Z"
}{
"error": {
"code": "VALIDATION_FAILED",
"message": "<string>",
"statusCode": 123,
"fieldErrors": [
{
"field": "<string>",
"message": "<string>"
}
]
}
}{
"error": {
"code": "VALIDATION_FAILED",
"message": "<string>",
"statusCode": 123
}
}{
"error": {
"code": "VALIDATION_FAILED",
"message": "<string>",
"statusCode": 123
}
}{
"error": {
"code": "VALIDATION_FAILED",
"message": "<string>",
"statusCode": 123
}
}{
"error": {
"code": "VALIDATION_FAILED",
"message": "<string>",
"statusCode": 123
}
}Authorizations
Authorization: Bearer <token>. Accepts EITHER a Keycloak access token (scopes-in-token) OR an opaque POS session token; both resolve to the same pos.* scope vocabulary the route guards enforce.
Body
1111111Response
The paired device, with the server-stamped lastSeen.
The paired device, with the server-stamped lastSeen.
111111^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z))$1
