cloudflare-api-token-permissions
Skill0 starsChanged 5 days agoMITView on GitHub
Map a Cloudflare CI deploy auth error (code 10000 / 7403 / 9106) to the missing API token permission, and show how to extend the token in-place without regenerating its value (so the GitHub Secret stays untouched). Use when `wrangler deploy` or `wrangler d1 migrations apply` fails in GitHub Actions with `Authentication error`, or when adding a new binding (`r2_buckets` / `kv_namespaces` / `queues` / `vectorize` / `hyperdrive`) to `wrangler.jsonc` and the deploy starts failing in CI even though it works locally. Covers the permission matrix per binding, the "Edit Cloudflare Workers" template's silent omission of D1 / Queues / Vectorize, and the diagnostic flow when multiple permissions are missing.
In this repository23 other files it gives its agents.
- skills/vercel-react-best-practices/AGENTS.mdAGENTS.md
- agent-status-hubSkill
- claude-code-docker-sandboxSkill
- cloudflare-cron-to-discordSkill
- cloudflare-d1-drizzle-migrationSkill
- cloudflare-d1-keyless-host-backupSkill
- cloudflare-d1-weekly-backup-via-prSkill
- cloudflare-mcp-claude-toolingSkill
- cloudflare-r2-private-image-uploadSkill
- cloudflare-workers-bot-scan-defenseSkill
- cloudflare-workers-builds-keyless-deploySkill
- cloudflare-workers-deploy-skeletonSkill
- cloudflare-workers-e2e-playwrightSkill
- cloudflare-workers-host-synthetic-checkSkill
- cloudflare-workers-passkey-authSkill
- cloudflare-workers-pat-bearer-authSkill
- cloudflare-workers-space-membership-inviteSkill
- cloudflare-workflows-for-long-tasksSkill
- playwright-e2e-in-docker-sandboxSkill
- sandboxed-agent-github-token-via-1passwordSkill
- sandboxed-agent-git-relaySkill
- snr-writingSkill
- vercel-react-best-practicesSkill
The file
We split this skill into the rules it gives an agent
11 rules. About 0 are common, 4 are distinctive, and 7 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈4
Distinctive · estimate
This author's own choices. Worth a look.
7
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Very specific: line 13
When wrangler deploy (or wrangler d1 migrations apply) fails in CI with code: 10000, code: 7403, or code: 9106, the cause is almost always: …
- Distinctive: line 99
Add the missing permission row → Continue to summary → Update Token
- Very specific: line 105
If you have multiple tokens in your account and aren't sure which value lives in CLOUDFLARE_API_TOKEN:
- Very specific: line 110
If you can't tell, rotate: create a fresh token with the full matrix, gh secret set CLOUDFLARE_API_TOKEN, delete the old one.
- Distinctive: line 115
Look up the code in the *Error code → missing permission* table above
- Very specific: line 118
If a new code: <different number> appears, repeat from step 2 (cumulative permissions across the deploy stages — e.g.
- Very specific: line 119
If the same code persists, check that the token you edited is actually the one in CLOUDFLARE_API_TOKEN (see *Identifying which token is the …
- Distinctive: line 130
Create a new token with the full permission matrix above (Custom Token, not the template)
- Very specific: line 132
Update the GitHub Secret: gh secret set CLOUDFLARE_API_TOKEN -R <owner>/<repo> and paste the value
- Distinctive: line 133
Delete the old token in the Cloudflare dashboard (avoids accumulation of stale tokens)
- Very specific: line 140
Deploying via
wrangler deploy --account-id <id>: instead of via secret: same permission requirements, just authenticated differently.
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Cloudflare API Token Permissions
When wrangler deploy (or wrangler d1 migrations apply) fails in CI with code: 10000, code: 7403, or code: 9106, the cause is almost always: the GitHub Secret CLOUDFLARE_API_TOKEN is missing a permission for a binding or operation that wrangler.jsonc references.
Adding a binding to wrangler.jsonc doesn't fail locally (your wrangler login session has full account access), but CI's narrow API token does. The error surfaces only when CI runs — typically after the PR is already merged.
When to use this skill
- A CI
wrangler deployjob that previously succeeded now fails withAuthentication error [code: 10000]/[code: 7403]/[code: 9106] - You're about to add a binding (
r2_buckets,kv_namespaces,queues,vectorize,hyperdrive,ai,browser) towrangler.jsoncand want to predict whether the token needs an extension - Token rotation: the secret value leaked or expired and needs replacement
- You used the
Edit Cloudflare Workerstemplate to create a token and need to verify what's actually included (it omits D1 / Queues / Vectorize — see below)
Error code → missing permission
| First error in deploy log | Cloudflare API path that failed | Missing permission |
Authentication error [code: 10000] | /accounts/<id>/r2/buckets/<name> | Account / Workers R2 Storage / Edit |
not authorized [code: 7403] | /accounts/<id>/d1/database/<id>/query | Account / D1 / Edit |
Authentication failed [code: 9106] | /memberships | Not a missing permission — see the 9106 note below this table |
Authentication error [code: 10000] | /accounts/<id>/workers/scripts/<name> | Account / Workers Scripts / Edit |
Authentication error [code: 10000] | /accounts/<id>/storage/kv/namespaces/<id> | Account / Workers KV Storage / Edit |
Authentication error [code: 10000] | /accounts/<id>/queues/<id> | Account / Queues / Edit |
The first error in the log is the actionable one; subsequent errors are usually consequences of the first failed auth call. Fix the first one and the rest disappear.
The 9106-on-/memberships special case (verified 2026-08-08): 9106 is not a permission-missing code. It means a user-level endpoint was called without a user identity — either the token is an Account owned token (which structurally cannot call /memberships; wrangler ≥ 4.90 auto-falls back to /accounts), or the credential env var itself is broken. A user token that merely lacks the scope fails with code: 10000 instead, and that one is fixed by User / Memberships / Read (User scope — Account Settings / Read covers /accounts, not /memberships). The durable fix either way: set account_id in wrangler.jsonc (or CLOUDFLARE_ACCOUNT_ID in CI) so wrangler skips the account-resolution endpoints entirely.
Permission matrix (binding / command → required permission)
| Binding or wrangler command | Required token permission |
wrangler deploy core (Worker upload) | Account / Workers Scripts / Edit |
wrangler d1 migrations apply --remote | Account / D1 / Edit |
wrangler d1 export --remote (backup) | Account / D1 / Edit (Read may suffice; Edit is the safe pick) |
r2_buckets binding (existence check on deploy) | Account / Workers R2 Storage / Edit |
kv_namespaces binding | Account / Workers KV Storage / Edit |
queues.producers / queues.consumers binding | Account / Queues / Edit |
vectorize binding | Account / Vectorize / Edit |
hyperdrive binding | Account / Hyperdrive / Edit |
ai (Workers AI) binding | Account / Workers AI / Read |
browser (Browser Rendering) binding | Account / Browser Rendering / Edit |
Memberships lookup (only fires when account_id is not set) | User / Memberships / Read (User scope) — or set account_id so the call never happens |
For the typical small-app stack (SPA + API + D1 + R2 served from one Worker), the minimum token permission set is:
Account / Workers Scripts / EditAccount / D1 / EditAccount / Workers R2 Storage / Edit- Account Resources:
<your specific account>(don't grant All accounts unless you have a reason)
…plus account_id set in wrangler.jsonc (or CLOUDFLARE_ACCOUNT_ID in CI). With it, wrangler never calls the account-resolution endpoints (/accounts, /memberships) that would otherwise drag in Account Settings / Read / User / Memberships / Read.
The "Edit Cloudflare Workers" template trap
Cloudflare offers a one-click template named Edit Cloudflare Workers. It looks complete but silently omits D1, Queues, and Vectorize — products added after the template was originally set. (The template does get occasional refreshes — R2 is included nowadays — so re-verify against the template reference when in doubt.)
What the template includes (verified 2026-08-07):
Workers Scripts / EditWorkers KV Storage / EditWorkers R2 Storage / EditWorkers Tail / ReadWorkers Routes / EditAccount Settings / ReadUser Details / Read(User scope)Memberships / Read(User scope)
What you must add manually if your wrangler.jsonc uses them:
D1 / EditQueues / EditVectorize / EditHyperdrive / Edit
Recommendation: skip the template. Use Create Custom Token and pick exactly what you need from the matrix above. Faster than auditing the template's gaps.
In-place permission edit (keeps the token value)
A common misconception: editing a Cloudflare API Token's permissions regenerates its value, requiring a GitHub Secret update.
It doesn't. Cloudflare keeps the same token value when you edit permissions; only rotating (deleting + recreating) generates a new value.
Workflow:
- https://dash.cloudflare.com/profile/api-tokens
- Locate the token used by GitHub Actions (sort by Last Used — CI tokens are touched on every
mainpush) - Click Edit (not "Roll")
- Add the missing permission row → Continue to summary → Update Token
- Token value unchanged → no GitHub Secret update needed
- Re-run the failed CI:
gh run rerun <run-id> --failed
Identifying which token is the CI token
If you have multiple tokens in your account and aren't sure which value lives in CLOUDFLARE_API_TOKEN:
- The GitHub Secret value is one-way encrypted — not retrievable
gh secret list -R <owner>/<repo>only shows theupdatedAttimestamp; correlate with the Cloudflare token's Created / Last Used timestamps- Cloudflare dashboard sorts tokens by Last Used. The most-recently-used should be the CI token (Actions runs on every
mainpush) - If you can't tell, rotate: create a fresh token with the full matrix,
gh secret set CLOUDFLARE_API_TOKEN, delete the old one. Avoids a long bisect.
Diagnostic flow when CI deploy fails
gh run view <run-id> --log-failed→ find the first[ERROR] code: <number>line- Look up the code in the Error code → missing permission table above
- Edit the token's permissions in-place (value stays the same)
gh run rerun <run-id> --failed- If a new
code: <different number>appears, repeat from step 2 (cumulative permissions across the deploy stages — e.g. R2 binding check fails first → fix R2 → D1 migrations apply fails next → fix D1) - If the same code persists, check that the token you edited is actually the one in
CLOUDFLARE_API_TOKEN(see Identifying which token is the CI token above) — most common cause of "I added the permission but it still fails"
When you do need to rotate (regenerate)
- The secret value leaked publicly
- Cloudflare flagged the token as compromised
- The token expired (if you set a TTL when creating)
- You can't tell which token the CI uses (rotate to break the ambiguity)
In that case:
- Create a new token with the full permission matrix above (Custom Token, not the template)
- Copy the new value (shown once)
- Update the GitHub Secret:
gh secret set CLOUDFLARE_API_TOKEN -R <owner>/<repo>and paste the value - Delete the old token in the Cloudflare dashboard (avoids accumulation of stale tokens)
- Re-run the failed CI
What this skill does NOT cover
- Local
wrangler loginsession permissions —wrangler loginuses OAuth and grants full account access; it never has the narrow-permission problem. Only CI tokens hit this. - R2 bucket-scoped object access tokens (S3-compatible API): those are a different token type (created under R2 → Manage R2 API Tokens) and unrelated to CI deploy. Workers R2 Storage binding uses the account API token, not the bucket-scoped one.
- Deploying via
wrangler deploy --account-id <id>instead of via secret: same permission requirements, just authenticated differently.
Related skills
cloudflare-workers-deploy-skeleton— initial project setup. The token created during that skill should already follow this matrix from day 1; consult this skill when adding new bindings later.cloudflare-d1-drizzle-migration— D1 migration pitfalls (PRAGMA foreign_keys=OFFignored). NeedsD1 / Edit.cloudflare-d1-weekly-backup-via-pr— weekly D1 backup workflow. Reuses the sameCLOUDFLARE_API_TOKEN(no new token needed if the matrix is met).cloudflare-workers-builds-keyless-deploy— the alternative that removes this whole problem class: deploy via Workers Builds with no Cloudflare token in GitHub at all. Its default build token has the same flavor of trap (D1 Edit missing).
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

