# Device check-in > The device-side check-in contract, with examples. A commissioned device calls BuildTrace to say which firmware it runs. BuildTrace answers with the firmware it should run. This guide is the contract a device implements. You need a commissioned device and its token. See [Devices and firmware](https://buildtrace.eu/docs/concepts/devices-and-firmware.md). ## Before you start 1. Your workspace's plan must include the `ota` feature. Without it, every check-in fails with `403`. 2. Commission the device and copy its token. The token is shown once, and BuildTrace stores only a hash of it. If you lose it, rotate the token. 3. Create a component type for each part of the device that runs firmware, such as `main` or `radio`. The `componentTypeCode` the device reports is the code of that component type. 4. Assign released firmware to the device or its fleet. ## The endpoint `POST /api/device/checkin` The API base is your BuildTrace app address followed by `/api`. For `https://app.buildtrace.eu`, the check-in URL is `https://app.buildtrace.eu/api/device/checkin`. Devices should use `https://app.buildtrace.eu/api`. Devices already set up with an old address, `https://buildtrace.erbo.io/api` or `https://app.buildtrace.erbo.io/api`, keep working. This route is not in the generated API reference. It is a plain route with its own device authentication, so it is documented here instead. ## Authentication Send the device token in an `Authorization: Bearer` header: ``` Authorization: Bearer ``` This is the device token you got when you commissioned the device, or when you last rotated it. It is not a user token. BuildTrace hashes the token and looks it up. A missing, unknown or revoked token gets `401`. Rotating a token revokes the old one. ## Request Send a JSON body that lists the firmware the device runs now: ```json { "reported": [ { "componentTypeCode": "main", "version": "1.0.0" }, { "componentTypeCode": "radio", "version": "2.3.1" } ] } ``` - `componentTypeCode` is the code of a component type. - `version` is the version the component runs now. The body can be at most `64 KiB`. A larger body gets `413`. The list can have at most `64` components. A longer list gets `400`. ## Response A successful check-in returns `200`: ```json { "manifest": [ { "componentTypeId": "6b1f0f9e-3c1d-4e53-9a8e-2f0d5a9c7e11", "componentTypeCode": "main", "firmwareVersionId": "0c7a2d44-5b8e-4f6a-a1d2-9e3b7c4d8f20", "version": "1.1.0", "artifactUrl": "https://…", "checksumSha256": "9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08", "sizeBytes": 524288 } ], "compliant": false } ``` The `manifest` is the firmware the device should run. It has one item for each component type that has firmware assigned. Each item has these fields: - `componentTypeId`: the component type's ID. - `componentTypeCode`: the component type's code. - `firmwareVersionId`: the firmware version's ID. - `version`: the version to run. - `artifactUrl`: where to download the firmware. - `checksumSha256`: the SHA-256 checksum of the file. - `sizeBytes`: the size of the file in bytes. A device with nothing assigned gets an empty `manifest`. BuildTrace only offers released firmware that is compatible with the device's part revision. A deprecated release is still offered to devices already assigned to it. ## Compliance `compliant` is `true` only when every manifest item is reported at exactly its desired version. This is an exact match. BuildTrace does not check whether a different version would also work. A component that the device reports but that is not in the manifest does not make the device non-compliant. A component in the manifest that the device does not report does. ## What BuildTrace records - A reported version is written to the unit's timeline as `FIRMWARE_FLASHED` only when it differs from the last version recorded for that component. A device that checks in every few minutes at the same version adds nothing. - A component type that BuildTrace does not know is recorded on every check-in. It has no earlier version to compare with. - The device's connection status is updated on every check-in. It shows as online, and its compliance flag is updated. BuildTrace never rejects a report. An unknown or unexpected version is recorded, and the device is shown as non-compliant. ## Download and flash 1. For each manifest item whose `version` differs from what the device runs, download `artifactUrl`. 2. The URL is presigned and valid for `15 minutes`. Check in again to get a fresh one. 3. Verify the file against `checksumSha256` before you flash it. Discard the file if it does not match. 4. Flash the firmware with your own update agent. BuildTrace does not push firmware to a device. 5. On the next check-in, report the new version. That is when BuildTrace records `FIRMWARE_FLASHED` and updates compliance. ## Example with curl ```bash curl -X POST "https://app.buildtrace.eu/api/device/checkin" \ -H "Authorization: Bearer $DEVICE_TOKEN" \ -H "Content-Type: application/json" \ -d '{"reported":[{"componentTypeCode":"main","version":"1.0.0"}]}' ``` ## A device loop ``` every N minutes: reported = [{componentTypeCode: c.code, version: c.version} for c in components] response = POST /api/device/checkin with reported if response.status != 200: log the status and try again later continue for item in response.manifest: if item.version == running_version(item.componentTypeCode): continue file = download(item.artifactUrl) if sha256(file) != item.checksumSha256: discard file continue flash(item.componentTypeCode, file) # the next check-in reports the new versions ``` ## Errors | Status | Meaning | | --- | --- | | `200` | Checked in. The body has `manifest` and `compliant`. | | `400` | The body is not valid JSON, or it lists more than 64 components. | | `401` | The token is missing, unknown or revoked. | | `403` | Your workspace's plan does not include the `ota` feature. | | `413` | The body is larger than 64 KiB. | | `500` | A server error. Try again later. | Error responses carry a short plain-text message, not JSON.