Skip to content

API reference

Device authorization API

The RFC 8628 device grant that vy login uses to receive an API key.

On this page

Request a device code

POST /v1/auth/device/code

  • Authentication: none on this route.
  • Who: No authentication.

Request body (application/json, DeviceCodeRequest)

FieldTypeRequiredDescription
client_namestring | nullNoShown to the approving user, such as vy on build-host; 1–64 characters. Defaults to vy CLI.
scopenull | ApiKeyPresetDtoNoThe preset the CLI asks for; the approving user may choose another. Defaults to create_and_use.

Responses

StatusDescriptionBody
200Show user_code and verification_uri, then poll /v1/auth/device/token every interval secondsDeviceCodeResponse
422Invalid request (invalid_request); field_errors names the fields.ProblemDetails
429Too many requests; wait for Retry-After.ProblemDetails
Request
curl -X POST "$VMYARD_API_URL/v1/auth/device/code" \
  -H "Content-Type: application/json" \
  -d '{
  "client_name": "vy on build-host",
  "scope": "use_existing"
}'
Response 200
{
  "device_code": "string",
  "expires_in": 0,
  "interval": 0,
  "user_code": "BCDF-GHJK",
  "verification_uri": "https://console.example.com/device",
  "verification_uri_complete": "https://console.example.com/device?user_code=BCDF-GHJK"
}

Poll for the API key

POST /v1/auth/device/token

Polls for the API key. Errors use the RFC 8628 shape, not problem details.

  • Authentication: none on this route.
  • Who: No authentication.

Request body (application/json, DeviceTokenRequest)

FieldTypeRequiredDescription
device_codestringYes
grant_typestringYesurn:ietf:params:oauth:grant-type:device_code

Responses

StatusDescriptionBody
200Approved: the new API key, returned exactly onceDeviceTokenResponse
400authorization_pending, slow_down (add 5 s to the interval), access_denied, expired_token, invalid_grant, invalid_request, or unsupported_grant_typeDeviceTokenError
Request
curl -X POST "$VMYARD_API_URL/v1/auth/device/token" \
  -H "Content-Type: application/json" \
  -d '{
  "grant_type": "urn:ietf:params:oauth:grant-type:device_code",
  "device_code": "<device_code>"
}'
Response 200
{
  "access_token": "vmy_…",
  "api_key": {
    "created_at": "2026-10-12T05:51:01Z",
    "created_by": "6f1c2d3e-4b5a-4c7d-8e9f-0a1b2c3d4e5f",
    "expires_at": "2026-10-12T05:51:01Z",
    "id": "6f1c2d3e-4b5a-4c7d-8e9f-0a1b2c3d4e5f",
    "last_four": "x9Q2",
    "last_used_at": "2026-10-12T05:51:01Z",
    "name": "ci-runner",
    "organization_id": "6f1c2d3e-4b5a-4c7d-8e9f-0a1b2c3d4e5f",
    "prefix": "vmy_AbCdEfGh",
    "preset": "read_only",
    "revoked_at": "2026-10-12T05:51:01Z"
  },
  "expires_in": 0,
  "scope": "read_only",
  "token_type": "Bearer"
}

Look up a device request

GET /v1/auth/device

Shows a pending request so the user can check the device name and requested scope before approving.

  • API key: not accepted (403 session_required).
  • Browser session: the vmyard_session cookie.
  • Who: Any signed-in user.

Parameters

NameInTypeRequiredDescription
user_codequerystringYesThe code the user typed; case, spaces, and dashes are ignored.

Responses

StatusDescriptionBody
200—DeviceAuthorizationDto
401Not signed in, or the API key is malformed, expired, or revoked (unauthenticated, invalid_api_key).ProblemDetails
403session_required for API keysProblemDetails
404Unknown, expired, or already decided codeProblemDetails
429Too many requests; wait for Retry-After.ProblemDetails
Request
curl "$VMYARD_API_URL/v1/auth/device?user_code=BCDF-GHJK" \
  -b "vmyard_session=$VMYARD_SESSION"
Response 200
{
  "client_name": "vy on build-host",
  "expires_at": "2026-10-12T05:51:01Z",
  "requested_scope": "read_only",
  "user_code": "BCDF-GHJK"
}

Approve a device

POST /v1/auth/device/approve

Approves a pending device for the X-Organization-ID organization. The key is minted for this user when the device next polls, so it is bound by the user's role there like any other key.

  • API key: not accepted (403 session_required).
  • Browser session: the vmyard_session cookie and the x-csrf-token header.
  • Who: Any member of the chosen organization.

Parameters

NameInTypeRequiredDescription
X-Organization-IDheaderstring (uuid)NoOrganization the key will belong to. Required with a browser session. Optional with an API key, which fixes the organization; when sent it must match.

Request body (application/json, DeviceApproveRequest)

FieldTypeRequiredDescription
user_codestringYes
expires_in_daysinteger | nullNoKey lifetime, 1–90 days; defaults to 90. 1–90. default 90.
namestring | nullNoKey name, 1–64 characters; defaults to vy: <client_name>.
presetnull | ApiKeyPresetDtoNoThe key's preset; defaults to the requested scope.

Responses

StatusDescriptionBody
200—DeviceApproved
401Not signed in, or the API key is malformed, expired, or revoked (unauthenticated, invalid_api_key).ProblemDetails
403CSRF failure or an API key (session_required)ProblemDetails
404Unknown, expired, or already decided code, or not a member of the organizationProblemDetails
422Invalid request (invalid_request); field_errors names the fields.ProblemDetails
429Too many requests; wait for Retry-After.ProblemDetails
Request
curl -X POST "$VMYARD_API_URL/v1/auth/device/approve" \
  -b "vmyard_session=$VMYARD_SESSION" \
  -H "x-csrf-token: $VMYARD_CSRF" \
  -H "X-Organization-ID: $VMYARD_ORGANIZATION_ID" \
  -H "Content-Type: application/json" \
  -d '{
  "user_code": "BCDF-GHJK",
  "preset": "use_existing",
  "expires_in_days": 30,
  "name": "vy: build-host"
}'
Response 200
{
  "expires_in_days": 0,
  "organization_id": "6f1c2d3e-4b5a-4c7d-8e9f-0a1b2c3d4e5f",
  "preset": "read_only",
  "user_code": "BCDF-GHJK"
}

Deny a device

POST /v1/auth/device/deny

  • API key: not accepted (403 session_required).
  • Browser session: the vmyard_session cookie and the x-csrf-token header.
  • Who: Any signed-in user.

Parameters

NameInTypeRequiredDescription
X-Organization-IDheaderstring (uuid)NoOrganization that records the audit event. Required with a browser session. Optional with an API key, which fixes the organization; when sent it must match.

Request body (application/json, DeviceDecisionRequest)

FieldTypeRequiredDescription
user_codestringYes

Responses

StatusDescriptionBody
204Denied; the device's next poll gets access_denied—
401Not signed in, or the API key is malformed, expired, or revoked (unauthenticated, invalid_api_key).ProblemDetails
403Refused for this caller: role, key preset, CSRF token, or organization (forbidden, insufficient_scope, csrf_invalid, organization_mismatch, session_required).ProblemDetails
404Not found in this organization (resource_not_found).ProblemDetails
429Too many requests; wait for Retry-After.ProblemDetails
Request
curl -X POST "$VMYARD_API_URL/v1/auth/device/deny" \
  -b "vmyard_session=$VMYARD_SESSION" \
  -H "x-csrf-token: $VMYARD_CSRF" \
  -H "X-Organization-ID: $VMYARD_ORGANIZATION_ID" \
  -H "Content-Type: application/json" \
  -d '{
  "user_code": "BCDF-GHJK"
}'