Skip to content

API tokens

An API token is how a script authenticates to the public API. It belongs to you, it is pinned to the team you were in when you minted it, and it carries the scopes you asked for.

Minting and revoking need your dagweave session, not a token. A token can never mint another token, so widening what your automation can reach always costs a sign-in.

POST /api/tokens, signed in:

Terminal window
curl -X POST https://api.dagweave.com/api/tokens \
-H "Content-Type: application/json" \
-H "X-Requested-With: dagweave" \
-b "__Host-dagweave_session=$DAGWEAVE_SESSION" \
-d '{"name":"run dashboard","scopes":["runs:read"],"expiresInDays":90}'

name is a label to help you tell your tokens apart. scopes is required and takes one or more of the scope names; a name that is not one of them is a 400, and so is an empty list. Scope names are lowercased and de-duplicated before they are stored. expiresInDays is optional and runs from 1 to 730. Leave it out and the token lives until you revoke it.

The write scopes are the ones to be careful with: runs:write starts a run on a real cluster, workflows:write saves over a stored workflow, and workflows:delete removes one. Put them only on a token that has to do that.

The X-Requested-With header is required on the cookie-authenticated routes that change something, which is what minting and revoking are. Its value does not matter, only its presence. Nothing under /api/v1 needs it: those routes read a bearer token and never a cookie, so a cross-site form post carries no credential to spend.

The 201 response is the only place the plaintext ever appears:

{
"id": "...",
"name": "run dashboard",
"teamId": "...",
"scopes": ["runs:read"],
"createdAt": "2026-07-25T09:00:00Z",
"expiresAt": "2026-10-23T09:00:00Z",
"token": "dwk_..."
}

Copy token into your secret store now. dagweave keeps only its SHA-256, the same way it stores session and invite tokens, so there is no way to show it to you again. Listing your tokens later returns neither the plaintext nor the stored hash.

A token is 256 random bits in base64url behind a dwk_ prefix. The prefix is there so a secret scanner, or a person reading a support ticket, recognises a dagweave key on sight.

GET /api/tokens returns your tokens, newest first, revoked ones included. Each row carries id, name, teamId, scopes, and createdAt, plus expiresAt, lastUsedAt, and revokedAt once those apply.

lastUsedAt is rewritten at most once an hour. It tells you whether a token is still in use, not the minute of its last call.

DELETE /api/tokens/{id} revokes a token and answers 204. Revocation bites on the very next request, because every call re-reads the token’s row. You can only revoke your own tokens; someone else’s id comes back 404.

Both minting and revoking are written to the audit log, so a token appearing out of nowhere is visible. The plaintext is never in the audit detail.

GET /api/tokens/usage, signed in, reports what the public API would allow you right now:

{
"windowSeconds": 60,
"user": {"limit": 300, "remaining": 288, "reset": "2026-07-25T09:01:00Z"},
"team": {"limit": 1200, "remaining": 1102, "reset": "2026-07-25T09:01:00Z"}
}

It peeks the very counters /api/v1 charges, so asking never spends a request of its own.

A token resolves to the team you were in when you minted it, and it stays there. Leave that team and the token stops working. Point one at a team you are not a member of and every call is a 403.

To rotate, mint the replacement, move your script onto it, then revoke the old one. Both work at once, so nothing goes dark in between.