API reference
API keys and login
Keys#
Keys are managed by signed-in members in the dashboard. A key cannot create or revoke keys.
List keys#
GET /v1/keys
Session: member.
| Query | Type | Meaning |
|---|---|---|
organization |
slug | Required. The organization. |
Response 200:
{
"keys": [
{
"id": "uuid",
"organization_id": "uuid",
"prefix": "wb_a1b2c3d4",
"label": "CI",
"scopes": ["runs:read", "runs:write"],
"created_by": "uuid",
"expires_at": "2027-01-26T12:00:00Z",
"last_used_at": null,
"revoked_at": null,
"created_at": "2026-10-28T12:00:00Z"
}
]
}prefix is the first 11 characters of the token, to identify a key. Only a hash of the token is stored.
Create a key#
POST /v1/keys
Session: manager.
| Field | Type | Meaning |
|---|---|---|
organization_id |
UUID | Required. |
label |
string, 1 to 120 characters | Required. |
scopes |
list of catalog:read, packages:write, runs:read, runs:write, org:admin |
Required. |
expires_in_days |
integer, 1 to 365 | Default 90. |
Response 201: the key, as in the list, plus token, wb_ and 64 hex characters. The token is returned once.
Revoke a key#
DELETE /v1/keys/:id
Session: the key's creator, or a manager of its organization. Response 200 { "deleted": true }.
Profile#
GET /v1/profile
Any key. Sessions are refused. Returns who the key is:
{
"organization": {
"id": "uuid",
"slug": "acme",
"name": "Acme",
"description": null,
"website": null,
"logo_url": null,
"accent": null,
"verified": false,
"personal": false,
"created_at": "...",
"updated_at": "..."
},
"user": { "id": "uuid", "email": "ada@example.com" },
"scopes": ["catalog:read", "packages:write", "runs:read", "runs:write"],
"key": { "id": "uuid", "label": "wb CLI", "expires_at": "..." }
}user is null for a key with no owning user. wb whoami shows the same.
MCP connections#
Keys created by an MCP client's OAuth sign-in, for the signed-in user.
List connections#
GET /v1/connections
Session only.
| Query | Type | Meaning |
|---|---|---|
organization |
slug | Required. |
Response 200 { "connections": [ { "id", "organization_id", "client_name", "connected_at", "last_used_at", "expires_at" } ] }.
Disconnect#
DELETE /v1/connections/:id
Session only: the user who approved it. Revokes the key. Response 200 { "deleted": true }. 404 for an ID that is not an MCP connection.
CLI login#
The device flow wb login uses to get a key. A login lasts 10 minutes, and the client polls at the returned interval.
Start a login#
POST /v1/logins
Open.
| Field | Type | Meaning |
|---|---|---|
label |
string, 1 to 120 characters | Required. The key's label. |
Response 201:
| Field | Meaning |
|---|---|
id |
The login. |
code |
The user code, such as ABCD-1234. |
label |
The label. |
verification_url |
Where the user approves. |
expires_at |
When the login expires. |
interval |
Seconds between polls, 1 to 30. |
approved |
false. |
secret |
A one-time secret, 64 hex characters. Keep it; it proves the exchange. |
Read a login#
GET /v1/logins/:id
Open. The same fields without secret.
Approve a login#
PATCH /v1/logins/:id with { "organization_id": "uuid" }
Session only. The user approves the login for one of their organizations. Response 200 { "approved": true }. The dashboard's approval page does this for you.
Exchange for a key#
POST /v1/tokens
Open; the request proves the secret.
| Field | Type |
|---|---|
login_id |
UUID |
secret |
64 hex characters |
Response 200: { "status": "pending" } until approved, then:
{
"status": "complete",
"token": "wb_...",
"key_id": "uuid",
"organization": {
"id": "uuid",
"slug": "acme",
"name": "Acme",
"...": "..."
},
"scopes": ["catalog:read", "packages:write", "runs:read", "runs:write"],
"expires_at": "..."
}The key holds every scope except org:admin and lasts 90 days.