Skip to content

Exchange a code for a key

POST/api/v1/device-auth/token

Access
No key

Exchange a connection code for the app’s API key

No authentication: the code and the PKCE verifier are the proof. Once per code, within 5 minutes of approval, while the account keeps the access revision it had then. Issues a key with the client's scopes (windows: account:read and content:write) for 365 days, named «lacuna для Windows · {device}», and revokes the previous working key of the same client and device of the account. A second exchange of a used code is refused, and with the right verifier it also revokes the key that code issued (a lost answer: connect again). At most 20 exchanges per client address per 10 minutes.

Example request

curl -X POST "https://lac.pics/api/v1/device-auth/token" \
  -H "Content-Type: application/json" \
  -d '{
  "code": "Qm3xT7kP9wZ2vL5nR8cB1dF4gH6jK0sA3eY7uI2oM5q",
  "verifier": "dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk"
}'

Request body

application/jsonrequired

  • codestringrequiredrequest only

    The one-time code from the address back into the app.

    pattern ^[A-Za-z0-9_-]{43}$

  • verifierstringrequiredrequest only

    The PKCE verifier whose S256 challenge was approved.

    pattern ^[A-Za-z0-9_-]{43,128}$

Responses

  • 200Connected; store the tokenDeviceAuthToken
    • tokenstringrequiredresponse only

      A Bearer API key, returned once. Keep it like the key form’s tokens (Windows Credential Manager); never in a URL or a log.

      pattern ^lac_api_[A-Za-z0-9_-]{43}$

    • keyApiCredentialrequired
      ApiCredential 6 fields
      • idstringrequired

        The file or note id.

      • namestringrequired

        Keys of a connection are named «lacuna для Windows · {deviceName}».

        length 1–100

      • scopesarray of stringrequired

        values: "content:read" "content:write" "content:delete" "account:read"

        1–4 items · unique

      • expiresAtstring · date-timerequired

        After this moment every request answers 401 API_KEY_EXPIRED; connect again. A connection key lives 365 days and renews itself within 30 days of its end (POST /device-auth/renew).

      • clientstringrequiredor null

        App of a key issued by a connection; null for keys made in the key form.

        values: "windows"

      • deviceNamestringrequiredor null

        Null for keys made in the key form.

        length 1–128

    • userobjectrequired
      1 field
      • usernamestringrequired
  • 400DEVICE_AUTH_INVALID for every unusable request alike: unknown, used or expired code, wrong verifier, changed account, malformed or extra fields. INVALID_JSON for a body that is not JSON. Start connecting again.DEVICE_AUTH_INVALIDINVALID_JSON
  • 413JSON body exceeds 1 MB before authentication; upload and quota limits may also refuse this operation.
  • 429RATE_LIMITED: exchange limit of this address.RATE_LIMITED
  • 500Unexpected failure; the code may have been used. Connect again.
  • 503The required rate-limit service is unavailable; access fails closed.

Example response

200 · application/json
{
  "token": "lac_api_NEW_KEY_SHOWN_ONCE",
  "key": {
    "id": "cmf8a1x2k0001q7lh5b2n8r6t",
    "name": "Upload script",
    "scopes": [
      "content:write"
    ],
    "expiresAt": "2026-12-26T09:30:00.000Z",
    "client": null,
    "deviceName": null
  },
  "user": {
    "username": "alex"
  }
}

Error codes

CodeStatusMessage
DEVICE_AUTH_INVALID400The connection code is invalid or has expired. Start connecting again in the app.
INVALID_JSON400The request contains invalid JSON.
RATE_LIMITED429Too many requests. Try again later.
Exchange a code for a key: POST /api/v1/device-auth/token · lacuna API