---
title: "Request a device link code a human can approve in the console"
method: POST
path: "/api/link/device"
tags: ["link"]
---

# Request a device link code a human can approve in the console

`POST /api/link/device`

## Request

- Base URL: `https://app.nerdit.ai` (the document declares no server; this is the origin it was published from)
- URL: `https://app.nerdit.ai/api/link/device`
- Auth: none declared

Mint the two artifacts of one device request (D-X16-O1/O3/O5).

Unauthenticated by design — nobody is signed in on the machine being
linked, which is the whole point — so this grants nothing on its own. It
writes one ``pending`` row that names no node and no account: the node
does not exist until a human approves, and the approval runs the same
account-standing assertion the console mint runs (D-X16-O25). The most
this route can be made to produce in bulk is rows that expire, which is
what the budgets above bound.

Both plaintexts exist only in the response object. The row keeps their
digests; the audit row keeps neither, and passes both to
``forbidden_values`` so that any future edit which interpolates one into
the metadata raises here rather than shipping.

Two other things are captured, and the difference between them is the
whole of D-X16-O23. The three strings the daemon *reports* about itself
are stored because the approval screen has to show a human something
they can recognise their own laptop by — bounded and control-stripped on
the way in, and labelled unverified on the way out, because this route
is unauthenticated and a phisher writes whatever reads best. The address
the request came *from* is stored because it is the one requester fact
the requester cannot choose, and because it can only be observed here:
by the time the approval screen loads, the only address the cloud can
see is the approver's own.

## Request body

- DeviceLinkRequest: What a daemon says about itself when it asks to be linked. Only ``credential_reference`` is load-bearing: it is pinned to the row here and every later step — the fingerprint the console renders, the node the approval adopts, the poll's constant-time comparison — is bound to *that* value (D-X16-O22), never to whatever presents itself later. The other three are display strings and are treated as hostile (D-X16-O23). They are bounded above; nothing downstream may render them without saying, in the copy, that the requesting machine reported them and nobody verified them.
  - `credential_reference` string, required
  - `hostname_hint` string, nullable
  - `daemon_version` string, nullable
  - `os` string, nullable

## Response `201`

Successful Response

- DeviceLinkResponse: The two artifacts, where to take one of them, and how long they last. ``device_code`` is the daemon's poll credential and is never displayed; ``user_code`` is what the terminal prints for a human to carry. Both leave this process exactly once, in this response, and neither is stored: the row keeps only ``sha256_hex`` of each (D-X16-O3). ``verification_uri_complete`` carries the user code in a **fragment** (D-X16-O24), so the one-click link cannot deposit the secret in an access log, a CDN log, a ``Referer`` header or a shared-link history the way ``?user_code=`` would. ``credential_fingerprint`` is the same eight-hex prefix the console will show beside the approval, so an operator standing at the terminal can check that the screen is describing *this* machine (D-X16-O4 — an aid for the operator who has the terminal, never a defence against a phished stranger).
  - `device_code` string, required
  - `user_code` string, required
  - `verification_uri` string, required
  - `verification_uri_complete` string, required
  - `credential_fingerprint` string, required
  - `expires_in` integer, required
  - `interval` integer, required

## Other responses

- `400`: Bad Request
- `422`: Validation Error
- `429`: Too Many Requests

---

[API](https://skmtc.dev/nerdit/apis/nerdit-cloud-web.md) · [All operations](https://skmtc.dev/nerdit/apis/nerdit-cloud-web/llms.txt) · [OpenAPI document](https://skmtc.dev/nerdit/apis/nerdit-cloud-web/revisions/06e3b44b58be?raw)
