---
title: "Exchange a CLI credential"
method: POST
path: "/v2/cli.exchangeToken"
tags: ["cli"]
---

# Exchange a CLI credential

`POST /v2/cli.exchangeToken`

## Request

- Base URL: `https://api.unkey.com`
- URL: `https://api.unkey.com/v2/cli.exchangeToken`
- Auth: none required

Supply exactly one of `loginId` or `refreshToken`. No authentication header is required.
A `loginId` polls a device login started by cli.startDeviceLogin. While the user has not approved it,
the API returns HTTP 202 with a Retry-After header in seconds. Approval returns HTTP 200 with access and refresh tokens exactly once.
Denial returns HTTP 403. An expired login returns HTTP 410. Stop polling on either response.
Device polling is rate limited per IP address.
A `refreshToken` rotates an existing session and returns HTTP 200 with new tokens.
The user's current membership and permissions are checked again. Reuse of the previous token
after the retry grace period revokes the session.
Access tokens last 15 minutes. A session expires after 7 days without a refresh,
and no later than 30 days after login.

## Request body

- union: Supply exactly one credential. A loginId polls for approval; a refreshToken rotates an existing session.
  - object
    - `loginId` string, required: loginId from cli.startDeviceLogin.
    - `refreshToken` string: Refresh token from the previous login or token exchange.
  - object
    - `loginId` string: loginId from cli.startDeviceLogin.
    - `refreshToken` string, required: Refresh token from the previous login or token exchange.

## Response `200`

Token exchange completed. Includes the access token and refresh token.

- V2CliExchangeTokenResponseBody
  - `meta` Meta, required: Metadata object included in every API response. This provides context about the request and is essential for debugging, audit trails, and support inquiries. The `requestId` is particularly important when troubleshooting issues with the Unkey support team.
    - `requestId` string, required: A unique id for this request. Always include this ID when contacting support about a specific API request. This identifier allows Unkey's support team to trace the exact request through logs and diagnostic systems to provide faster assistance.
  - `data` V2CliExchangeTokenResponseData, required: Session credentials returned by a successful token exchange.
    - `accessToken` string, required: Short-lived access token.
    - `refreshToken` string, required: Rotating refresh token. Store it. It is returned only once.
    - `expiresIn` integer, required: Seconds until the access token expires. Access tokens last 15 minutes.
    - `workspaceId` string, required: Workspace the session belongs to.
    - `sessionId` string, required: CLI session id.

## Other responses

- `202`: Awaiting approval. Wait for Retry-After seconds before polling again.
- `400`: Invalid credential selection or unknown login.
- `401`: The refresh token is invalid, expired, or reused outside the retry grace period.
- `403`: Login was denied, or the user was removed or no longer has the session's permissions.
- `410`: The login expired. Start a new device login.
- `429`: Too many requests from this IP address.
- `500`: Internal server error, including when CLI login is not configured on this API.

## Changes

- **2026-10-28** `5949ec68734a`: 1 info
  - endpoint added

[Operation history](https://skmtc.dev/unkeyed/apis/unkey-api/changes/v2/cli.exchangeToken/post.md)

---

[API](https://skmtc.dev/unkeyed/apis/unkey-api.md) - [All operations](https://skmtc.dev/unkeyed/apis/unkey-api/llms.txt) - [OpenAPI document](https://skmtc.dev/unkeyed/apis/unkey-api/revisions/6fbaa27f19e0?raw)
