---
title: Agent token management
description: Issue, list, and revoke app-scoped agent tokens using the app owner's web session.
---

# Agent token management {#agent-token-management}

Manage tokens in **App information and SDK → Coding agent connection**. This API reference is for web clients implementing the same functions.

All token endpoints require **the app owner's web session**. Agent tokens cannot create, list, or revoke tokens. Any `Authorization` header is rejected with `401`. Mutations also require an allowed web `Origin`.

## Issue a token {#issue-a-token}

```http
POST /api/v1/apps/:appId/automation/tokens
Content-Type: application/json
```

| Field | Required | Constraint |
| --- | --- | --- |
| `name` | Yes | 1–100 characters after trimming |
| `expiresInDays` | No | Integer 1–90; default 30 |
| `scopes` | No | 1–3 distinct supported scopes; default all three |

Supported scopes are `feedback:read`, `work-items:read`, and `work-items:write`.

Run this web client example on the same origin as the signed-in service, not on the documentation domain. The browser sends the session cookie and Origin:

```js
const appId = "aaaaaaaa-aaaa-4aaa-8aaa-aaaaaaaaaaaa"; // Your actual app ID
const response = await fetch(`/api/v1/apps/${appId}/automation/tokens`, {
  method: "POST",
  credentials: "same-origin",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({
    name: "Development machine MCP",
    expiresInDays: 30,
    scopes: ["feedback:read", "work-items:read", "work-items:write"],
  }),
});
const result = await response.json();
if (!response.ok) throw new Error(result.message);
// result.token is only returned here. Never log or commit it.
```

Example `201` response (`token` is a placeholder):

```json
{
  "token": "ot_agent_REDACTED",
  "tokenInfo": {
    "id": "ffffffff-ffff-4fff-8fff-ffffffffffff",
    "name": "Development machine MCP",
    "scopes": ["feedback:read", "work-items:read", "work-items:write"],
    "expiresAt": "2026-11-27T02:00:00.000Z",
    "createdAt": "2026-10-28T02:00:00.000Z",
    "lastUsedAt": null,
    "revokedAt": null
  }
}
```

Only a hash is stored on the server; the raw token is shown only in this response. Issuance has no `clientRequestId`. Repeating a request after losing its response creates another token. List and revoke any unneeded tokens.

## List tokens {#list-tokens}

```http
GET /api/v1/apps/:appId/automation/tokens
```

The `200` response is `{ "tokens": [...] }`, with items shaped like `tokenInfo` above. Raw tokens and hashes are never returned. Results are newest first, include expired/revoked tokens, and are not paginated.

| Field | Meaning |
| --- | --- |
| `id` | UUID for revocation |
| `name` | Name assigned at issuance |
| `scopes` | Granted scopes |
| `expiresAt` | Expiration time |
| `createdAt` | Issuance time |
| `lastUsedAt` | Last token authentication time; initially `null` |
| `revokedAt` | Revocation time; `null` until revoked |

Times are UTC ISO 8601 strings. `lastUsedAt` indicates authentication, not whether an individual API operation succeeded.

## Revoke a token {#revoke-a-token}

```http
DELETE /api/v1/apps/:appId/automation/tokens/:tokenId
```

Successful `200` response:

```json
{ "ok": true }
```

Repeating revocation succeeds. A token ID not found in the target app returns `404`. Revoked tokens cannot be restored; issue a new one. Expiration and scopes cannot be edited through an API.

If a token may have leaked, revoke it first and connect a replacement. See [MCP setup](/mcp/) for passing it to the execution environment.
