Skip to main content

Devbox agent tokens

Devbox agent tokens let an agent running inside a devbox manage that same devbox without holding a full account-wide MORPH_API_KEY.

Use an agent token when you want the agent to do narrow self-management tasks such as:

  • reading the current devbox summary,
  • exposing or hiding an HTTP service,
  • saving a snapshot,
  • reading or writing secrets when the token has the matching scope,
  • writing notifications when you mint the explicit notifications scope.

Use a full MORPH_API_KEY instead when you need account-wide or multi-devbox access, or when you need workflows outside the usual self-management surface.

What the token is scoped to

When you mint a devbox agent token through POST /api/devboxes/{devbox_id}/agent-token, the service binds it to:

  • the authenticated user,
  • a single devbox_id,
  • the effective organization context,
  • an explicit set of scopes,
  • an optional purpose string for audit/debug context.

The mint endpoint only succeeds for a devbox that is already claimed and owned by the authenticated user. Later requests made with the token are rejected if you try to use that token against a different devbox id.

Tokens minted by this route are currently non-expiring. The request schema still accepts ttl_seconds for backwards compatibility, but the service does not currently encode an expiry for newly minted tokens and returns expires_at: null.

Default and supported scopes

If you omit scopes when minting, the service currently grants this default self-management set:

  • devbox:summary:read
  • devbox:http:write
  • devbox:snapshot:write
  • devbox:secrets:read
  • devbox:secrets:write

Additional supported scopes include:

  • devbox:notifications:write
  • devbox:metadata:write (reserved; not part of the normal default workflow)

Best practice: mint only the scopes you actually need, and inspect the returned scopes array instead of assuming every agent token has the same capabilities.

Mint a least-privilege token

There is not currently a repository-backed SDK or CLI helper documented for minting this token, so the canonical workflow today is a raw HTTP request to the devbox service.

export MORPH_API_KEY="<your-user-api-key>"
export DEVBOX_ID="<your-devbox-id>"
export DEVBOX_SERVICE_BASE_URL="${DEVBOX_SERVICE_BASE_URL:-https://devbox.svc.cloud.morph.so}"

MINT_RESPONSE="$(curl -sS \
-H "Authorization: Bearer $MORPH_API_KEY" \
-H "Content-Type: application/json" \
-X POST "$DEVBOX_SERVICE_BASE_URL/api/devboxes/$DEVBOX_ID/agent-token" \
-d '{
"scopes": [
"devbox:summary:read",
"devbox:snapshot:write"
],
"purpose": "codex session: docs update"
}')"

export DEVBOX_AGENT_TOKEN="$(echo "$MINT_RESPONSE" | jq -r '.token')"
echo "$MINT_RESPONSE" | jq '{devbox_id, user_id, scopes, expires_at}'

The response includes the final granted scope list. For newly minted tokens, expires_at should currently be null.

Use an already-injected DEVBOX_AGENT_TOKEN

Some agent environments inject a pre-scoped DEVBOX_AGENT_TOKEN for you. In that case, use it directly and assume it may be narrower than the service default scope set.

export DEVBOX_SERVICE_BASE_URL="${DEVBOX_SERVICE_BASE_URL:-https://devbox.svc.cloud.morph.so}"
: "${DEVBOX_ID:?missing DEVBOX_ID}"
: "${DEVBOX_AGENT_TOKEN:?missing DEVBOX_AGENT_TOKEN}"

curl -sS \
-H "Authorization: Bearer $DEVBOX_AGENT_TOKEN" \
"$DEVBOX_SERVICE_BASE_URL/api/devboxes/$DEVBOX_ID/summary" | jq

If the session tooling manages the token for you, prefer refreshing or restarting that session rather than replacing it ad hoc.

Common self-management calls

# 1) Read the lightweight devbox summary
curl -sS \
-H "Authorization: Bearer $DEVBOX_AGENT_TOKEN" \
"$DEVBOX_SERVICE_BASE_URL/api/devboxes/$DEVBOX_ID/summary" | jq

# 2) Expose a local port as an HTTP service
curl -sS \
-X POST \
-H "Authorization: Bearer $DEVBOX_AGENT_TOKEN" \
-H "Content-Type: application/json" \
"$DEVBOX_SERVICE_BASE_URL/api/devboxes/$DEVBOX_ID/http" \
-d '{"name":"app","port":3000}' | jq '.networking.http_services'

# 3) Hide that HTTP service again
curl -sS \
-X DELETE \
-H "Authorization: Bearer $DEVBOX_AGENT_TOKEN" \
"$DEVBOX_SERVICE_BASE_URL/api/devboxes/$DEVBOX_ID/http/app" | jq '.networking.http_services'

# 4) Save a snapshot before a risky change
curl -sS \
-X POST \
-H "Authorization: Bearer $DEVBOX_AGENT_TOKEN" \
-H "Content-Type: application/json" \
"$DEVBOX_SERVICE_BASE_URL/api/devboxes/$DEVBOX_ID/save" \
-d '{"name":"pre-change: dependency upgrade"}' | jq

Using secret scopes safely

If your token includes devbox:secrets:read or devbox:secrets:write, you can use the devbox service secret endpoints with the same bearer token.

Example: list secret metadata without printing secret values:

curl -sS \
-H "Authorization: Bearer $DEVBOX_AGENT_TOKEN" \
"$DEVBOX_SERVICE_BASE_URL/api/user-secrets" | jq '.data[] | {name, metadata}'

Actual secret-value reads and writes use the same auth model, but be careful not to print values into shell history, logs, chat, or CI output.

If a secret-backed call fails with Agent token session not found (mint a new agent token), mint a fresh token with your user API key and retry. The service stores encrypted session data for agent-token secret resolution, and non-secret endpoints can continue to work even when that session state is unavailable.

Best practices

  • Mint one token per agent session or workflow, not one broad token that multiple tools share indefinitely.
  • Set purpose every time you mint. A short string like codex session: release docs makes logs and debugging much easier.
  • Request the smallest scope set that works. If you only need summary and save, do not also grant secrets or notifications.
  • Keep the token in process memory or short-lived environment state. Do not commit it, paste it into tickets, or write it to shared files.
  • Assume injected DEVBOX_AGENT_TOKEN values are intentionally constrained. Check your workflow against the actual granted scope set before widening credentials.
  • There is no dedicated revoke endpoint documented here today, so treat narrow scopes and one-token-per-session hygiene as your primary containment strategy.

Troubleshooting

401 or 403 with Token expired

  • Newly minted tokens from POST /agent-token are currently non-expiring.
  • If you still see Token expired, you are probably using an environment-managed token provided by a higher-level session tool. Refresh or restart that session and retry.

401 with Agent token session not found (mint a new agent token)

  • This usually appears on secret-backed requests.
  • Mint a fresh token with your user API key so the service can recreate the encrypted token-session record it uses to resolve your underlying user API key for secret operations.

403 with Insufficient scope

  • Remint the token with the exact missing scope instead of switching immediately to a full MORPH_API_KEY.

403 with Token is scoped to a different devbox

  • The token is bound to one devbox id. Use the matching DEVBOX_ID, or mint a new token for the correct devbox.

When to use MORPH_API_KEY instead

Prefer MORPH_API_KEY when you need:

  • account-wide or organization-wide operations,
  • workflows that touch multiple devboxes,
  • devbox create, delete, branch, or tmux automation flows that are outside the usual agent-token self-management surface,
  • a broader control-plane surface than self-management,
  • any automation where the agent token does not have the required scope,
  • tasks where your environment provides only a narrowed injected DEVBOX_AGENT_TOKEN.

For general Devboxes setup and API usage, start with Getting started and Development. For low-level API details, see the hosted devbox service docs.