Mode: programmatic Last validated: 2026-05-01 UTC Validation method: read-only-docs Average duration: 5m Required role: superadmin
Applies to (new taxonomy names — #754):
- CF_PAGES_DEPLOY (was CLOUDFLARE_RAXX_AUTOMATION_API_TOKEN)
- CF_ACCESS_MGMT (was CLOUDFLARE_ACCESS_MGMT_TOKEN) — the management token itself; see cloudflare-access-service-token.md for Access service tokens
- CF_PAGES_READ (was CLOUDFLARE_PAGES_READ_TOKEN)
Legacy names (CLOUDFLARE_RAXX_AUTOMATION_API_TOKEN, CLOUDFLARE_ACCESS_MGMT_TOKEN, CLOUDFLARE_PAGES_READ_TOKEN) continue to work until removed by the cleanup card (see docs/secrets/cf-token-taxonomy.md).
Important correction to earlier verbal summary: Cloudflare User API Tokens can be rolled programmatically via
PUT /user/tokens/{id}/value. The dashboard "Roll" button is one path, but it is not the only path. The Global API Key cannot be rolled (and we do not store it). This SOP defaults to the programmatic path; the dashboard path is documented as a fallback for cases where the calling token has lost permission to roll itself.
token_id from Cloudflare (visible at https://dash.cloudflare.com/profile/api-tokens or via GET /user/tokens)User:API Tokens:Edit permission, OR Global API Key access for the dashboard fallback path. (A token cannot always roll itself — see Known gotchas.)CLOUDFLARE_ACCESS_MGMT_TOKEN since it gates console health probes# Confirm the token is currently working (sanity check before rolling)
curl -sS -H "Authorization: Bearer $CURRENT_TOKEN" \
https://api.cloudflare.com/client/v4/user/tokens/verify | jq .
# Expect: {"success":true,"result":{"status":"active",...}}
Identify the token_id to roll:
curl -sS -H "Authorization: Bearer $ROLLING_TOKEN" \
https://api.cloudflare.com/client/v4/user/tokens | jq '.result[] | {id, name, status}'
Programmatic (preferred):
NEW_VALUE=$(curl -sS -X PUT \
-H "Authorization: Bearer $ROLLING_TOKEN" \
-H "Content-Type: application/json" \
https://api.cloudflare.com/client/v4/user/tokens/$TOKEN_ID/value \
--data '{}' | jq -r '.result')
# $NEW_VALUE is the new token secret. Capture once; not retrievable again.
PUT /user/tokens/{token_id}/value rolls the secret in place — the token_id, name, scopes, and metadata are preserved. The old secret is invalidated atomically.
Dashboard fallback (use only if the programmatic path is unavailable):
1. Navigate to https://dash.cloudflare.com/profile/api-tokens.
2. Locate the token by name.
3. Click the menu icon → Roll.
4. Confirm. Copy the new value (shown once).
curl -sS -H "Authorization: Bearer $NEW_VALUE" \
https://api.cloudflare.com/client/v4/user/tokens/verify | jq .
# Expect: {"success":true,"result":{"status":"active",...}}
For CLOUDFLARE_PAGES_READ_TOKEN, additionally:
curl -sS -H "Authorization: Bearer $NEW_VALUE" \
https://api.cloudflare.com/client/v4/accounts/$CF_ACCOUNT_ID/pages/projects | jq '.result | length'
# Expect a non-zero project count.
Update the Infisical secret in the appropriate project (Raptor backend / console / GitHub Actions). Use Infisical's web UI or CLI:
# Write new taxonomy name (primary) + legacy name (for backward compat during migration)
infisical secrets set CF_PAGES_DEPLOY="$NEW_VALUE" \
--path /MooseQuest/cloudflare/ --env=prod
infisical secrets set CLOUDFLARE_RAXX_AUTOMATION_API_TOKEN="$NEW_VALUE" \
--path /MooseQuest/cloudflare/ --env=prod # legacy alias — remove after cleanup card
Infisical retains version history automatically — the previous value is recoverable for the rollback window.
| Consumer | How |
|---|---|
| Heroku Raptor (prod + staging) | heroku config:set CF_PAGES_DEPLOY="$NEW_VALUE" -a raxx-api-prod (and -staging). Also set legacy: heroku config:set CLOUDFLARE_RAXX_AUTOMATION_API_TOKEN="$NEW_VALUE" -a raxx-api-prod. Triggers dyno restart. |
| GitHub Actions | gh secret set CF_PAGES_DEPLOY -a actions -b "$NEW_VALUE" (and set legacy CLOUDFLARE_RAXX_AUTOMATION_API_TOKEN until workflows are updated) |
| Operator local env | Update manually after Infisical sync — DM via Slack D0AJ7K184TV |
Console (raxx-console-prod) |
heroku config:set CF_ACCESS_MGMT="$NEW_VALUE" -a raxx-console-prod (legacy: CLOUDFLARE_ACCESS_MGMT_TOKEN) |
After Heroku dyno restart completes:
# Raptor: confirm the new token is in use by hitting a CF-dependent endpoint
curl -sS https://api.raxx.app/health/dependencies | jq '.cloudflare'
# Expect: {"ok": true, ...}
# Console: confirm CF Pages probes still pass
curl -sS https://console.raxx.app/api/status/sites \
-H "Cookie: <ops session>" | jq '.sites[] | select(.provider=="cloudflare_pages")'
# Expect: liveness.ok = true for all CF Pages sites.
The PUT /user/tokens/{id}/value call already invalidated the old secret — no separate revocation step needed. To confirm:
curl -sS -H "Authorization: Bearer $OLD_VALUE" \
https://api.cloudflare.com/client/v4/user/tokens/verify
# Expect: HTTP 401 / {"success":false,"errors":[{"code":1000,"message":"Invalid API token"}]}
Write to console_audit_log via the rotation pipeline callback (#253). Manual operator entry if rotated outside the console UI:
action: secret.rotate.completed
actor: <admin_id>
context: {"secret_name": "CF_PAGES_DEPLOY", "method": "programmatic", "rotated_at": "2026-05-01T12:00:00Z"}
The previous token value is invalidated at the moment the new one is generated — there is no "revert to old token" path. Rollback options:
PUT call, repeat until validated). Each call invalidates the previous attempt.PUT /client/v4/user/tokens/{token_id}/value)User:API Tokens:Edit permission is required to call the roll endpoint. If the token being rolled lacks that permission (most do, intentionally — least-privilege), use a separate "rotation" token, the Global API Key (we do not store this), or the dashboard.heroku config:set the restart is automatic; do not skip the verification in step 6.expires_at was set at creation, the roll preserves the original expires_at. Use the dashboard to extend expires_at if needed; that path is the "Refresh" button in CF's UI, not the "Roll" button.CLOUDFLARE_ACCESS_MGMT_TOKEN rotation does not roll the service tokens it issued. Service token rotation is a separate SOP — see cloudflare-access-service-token.md.