Getting 401 Unauthorized
Symptom: a request to VectorStep — a webhook POST, a curl to the API,
the UI itself — comes back 401 Unauthorized, even though you’re sure
you’re sending a token. The two most common causes are: the wrong token
for that specific route, or the wrong token for the job entirely (VectorStep
ships three separately-purposed tokens whose names all start with
VECTORSTEP_).
First: which token is this route asking for?
Section titled “First: which token is this route asking for?”Every route needs a token from auth.tokens in config/vectorstep.yaml,
and each configured token carries exactly one role:
| Role | Can do |
|---|---|
webhook |
POST /webhook only — nothing else |
viewer |
Read-only: browse the UI, list runs/pipelines, GET endpoints |
operator |
Everything viewer can, plus Run now, rerun, replay, feedback, approvals |
admin |
Everything operator can, plus editing pipelines/steps and POST /reload |
The one genuinely surprising part: admin/operator/viewer form a
hierarchy — an admin token can do anything a viewer token can — but
webhook is not part of that ladder. POST /webhook accepts a webhook-role
token only. An admin token, which can do literally everything else in
the system, still 401s on /webhook — that’s not a bug, it’s the role
boundary working as designed. If a webhook call is 401ing and you’re
confident the token is valid, check its role first, not whether the token
itself is right.
auth: tokens: - name: admin token: ${VECTORSTEP_ADMIN_TOKEN} role: admin - name: webhook token: ${VECTORSTEP_WEBHOOK_TOKEN} role: webhookEvery tutorial in this docs series uses exactly this shape: $ADMIN_TOKEN
for /reload and anything in the UI, $WEBHOOK_TOKEN for /webhook. If
you’re getting 401s while following along, that pairing — right token for
this specific call — is the first thing to check.
Second: are you reaching for the right token at all?
Section titled “Second: are you reaching for the right token at all?”VectorStep’s default install seeds three environment variables that all
start with VECTORSTEP_ and are easy to mix up, because they solve three
unrelated problems:
| Variable | What it actually is |
|---|---|
VECTORSTEP_ADMIN_TOKEN |
An auth.tokens entry — proves your identity to VectorStep. Used for /reload, editing pipelines, logging into the UI. |
VECTORSTEP_WEBHOOK_TOKEN |
Also an auth.tokens entry — proves an external caller’s identity to VectorStep, for /webhook specifically. |
VECTORSTEP_GATEWAY_TOKEN |
Not an auth.tokens entry at all. This is executors.gateway.token — the credential VectorStep itself presents outbound, to authenticate to the Gateway when running a step. It has nothing to do with who’s allowed to call VectorStep. |
The first two authenticate incoming requests to VectorStep; the third
authenticates an outgoing request VectorStep itself makes. A 401 from a
step failing to reach the Gateway is a VECTORSTEP_GATEWAY_TOKEN problem
(wrong value, or it doesn’t match what’s configured on the Gateway side —
see Gateway authentication); a 401 on
/webhook or /reload is an auth.tokens problem. If you’re not sure
which one you’re looking at, check which service returned the 401 and
which URL you called — VectorStep’s own routes want an auth.tokens
value; a step failing mid-run wants the Gateway token.
If you’re not sure what’s actually configured
Section titled “If you’re not sure what’s actually configured”auth.tokens lives in ~/.vectorstep/config/vectorstep.yaml, and every
${ENV_VAR} in it resolves from ~/.vectorstep/.env. Read both together —
a token name existing in one file with no matching value in the other is a
common cause, especially after copying a config from elsewhere.
MCP servers (Claude Code / Claude Desktop) need their own admin token
Section titled “MCP servers (Claude Code / Claude Desktop) need their own admin token”If you’re getting 401s from the VectorStep Service MCP or Gateway
MCP rather than from VectorStep directly, the
same principle applies one level up: VECTORSTEP_TOKEN (Service MCP) and
GATEWAY_OPERATOR_TOKEN (Gateway MCP) each need to be an admin-role
credential from the respective service — a lower-role token will 401 on
most of what these servers do, since authoring pipelines/steps/agents is
inherently a write operation. See each server’s own Configuration section
on the MCP servers page for exactly where that
token comes from.
Where next
Section titled “Where next”- Service configuration reference —
the full
auth:block, includingallow_unauthenticatedfor a trusted local machine. - Team attribution — the
teamfield on awebhook-role token, for attributing spend/notifications per team. - MCP servers — token configuration for both standalone MCP servers.