# Kiosk (clock in/out tablets)

> How the TimeTracking kiosk works: device tokens, 6-digit PINs, the identify and punch endpoints, automatic breaks, lockout rules, and how punches become kiosk claims.

URL: https://timetracking.tech/developers/data-in/kiosk
Last updated: 2026-10-24

A kiosk is a shared tablet on a work site. Workers type their **6-digit PIN** and tap their placement to clock in or out. Clocking out turns the punch into a `kiosk` claim, which is matched like any other source.

**Status: Live.** Office setup steps are in [Kiosk setup](https://timetracking.tech/developers/help/office/kiosk-setup).

## 1. Register a device (admin)

In the dashboard: "Regels & koppelingen" → "Kiosk (tablet op locatie)". Over the API this needs an **admin key** or an admin user token (write keys get 403):

```sh
curl -s -X POST "$TT/kiosk-devices" -H "Authorization: Bearer $ADMIN_KEY" -H "content-type: application/json" \
  -d '{"name":"Hal 2 ingang","clientId":"00000000-0000-4000-8000-0000000000c1"}'
# → {"data":{"token":"ttk_…"}}      shown once
```

- `clientId` is optional. When set, only placements at that client can be used on this tablet.
- `GET /kiosk-devices` (write key) lists devices: `id`, `name`, `last_seen_at`, `revoked_at`, `client{name}`. Revoking is done in the dashboard ("Ontkoppelen").

Open the kiosk on the tablet with the token in the URL **fragment** (the fragment is never sent to servers or logs):

```
https://app.timetracking.tech/kiosk#device=ttk_…
```

The page stores the token in the browser's local storage and removes it from the address bar.

## 2. Set PINs

```sh
curl -s -X POST "$TT/people/$PERSON_ID/pin" -H "Authorization: Bearer $KEY" -H "content-type: application/json" -d '{"pin":"482913"}'
# → {"data":{"saved":true}}
```

Rules:

- Exactly **6 digits**. A shorter PIN returns 422 `PIN must be 6 digits`.
- No repeated digit (`111111`) and no ascending or descending run (`123456`, `987654`): 422 `PIN is too easy to guess`.
- Unique within the org: 409 `This PIN is already used by someone else`.
- PINs are stored as a peppered HMAC. Lists only show `has_kiosk_pin`.
- Older 4–5 digit PINs keep working until they are changed.

## 3. Kiosk endpoints

These are used by the kiosk page. They are authenticated with the device token in the `x-kiosk-token` header, not with an API key.

**Identify**

```sh
curl -s -X POST https://app.timetracking.tech/api/kiosk/identify \
  -H "x-kiosk-token: ttk_…" -H "content-type: application/json" -d '{"pin":"482913"}'
```

```json
{ "data": { "person": { "id": "00000000-0000-4000-8000-0000000000d1", "firstName": "Jan" },
            "placements": [ { "id": "00000000-0000-4000-8000-0000000000e1", "clientName": "VDL Montage", "roleTitle": "Monteur", "openSince": null } ] } }
```

Only active placements valid today are returned, limited to the device's client if one is set. `openSince` is the clock-in time of an open punch.

**Punch** (toggles in/out)

```sh
curl -s -X POST https://app.timetracking.tech/api/kiosk/punch \
  -H "x-kiosk-token: ttk_…" -H "content-type: application/json" \
  -d '{"pin":"482913","placementId":"00000000-0000-4000-8000-0000000000e1"}'
```

- Clock in: `{"data":{"action":"in","at":"2026-10-20T05:58:12.000Z","firstName":"Jan"}}`
- Clock out: `{"data":{"action":"out","at":"…","firstName":"Jan","minutes":480,"dayStatus":"draft"}}`

(Shapes from the source code. These endpoints were not called during the doc capture.)

## 4. How a punch becomes hours

On clock-out:

1. The span between in and out is computed. Spans over **16 hours** are refused with 409 `Open punch is older than 16 hours; ask the office to correct it`.
2. An automatic break per the Dutch ATW minimum is deducted: span > 10 h → 45 min; span > 5.5 h → 30 min; otherwise 0.
3. Start and end are converted to the org's timezone (default `Europe/Amsterdam`). The claim belongs to the **clock-in date**.
4. A claim with `source: "kiosk"`, `startMin`, `endMin`, `breakMin`, `sourceRef = <device id>` and `deviceTime` is recorded. A `claim.created` webhook fires. If the employee also entered hours in the app, normal [conflict detection](https://timetracking.tech/developers/concepts/sources-and-conflicts) applies.

## 5. Security and lockout

| Rule | Value |
|---|---|
| Wrong PINs before lockout | 5 within 15 minutes, per device |
| Lockout duration | 15 minutes (403 `Too many wrong PINs, try again in a few minutes`) |
| Counter reset | Only by time. A correct PIN does not reset it |
| Unknown or revoked device | 401 `Unknown kiosk` |
| Unknown PIN | 404 `Unknown PIN` |

## Errors

| Status | Message | Fix |
|---|---|---|
| 401 | `Unknown kiosk` | Re-open the kiosk URL with a valid `#device=` token |
| 403 | `Too many wrong PINs, try again in a few minutes` | Wait 15 minutes |
| 403 | `Not your placement at this kiosk` | The placement is inactive, not valid today, or at another client |
| 404 | `Unknown PIN` | Check the PIN, or set one under "Medewerkers" |
| 409 | `Open punch is older than 16 hours…` | The office records the correct hours with `POST /claims` |
| 423 | `This day is exported or locked; book a correction in an open period` | The clock-in date is already exported; nothing was stored |
