Cloudflare's enriched API 403s hand agents a 2 MB HTML page, and the .md route died in August 2026

The documentation_url in a Cloudflare 403 resolves to a 2 MB HTML page whose markdown route serves HTML.

Read time
13 min
Word count
2K
Sections
12
FAQs
8
Share
Cloudflare API 403 documentation_url resolves to a 2,012,713-byte HTML page, measured 23 August 2026
Cloudflare enriched 403 responses, measured against the live docs site on 23 August 2026
On this page · 12 sections
  1. What changed, and on what date
  2. The size problem, measured
  3. The .md convention is gone site-wide
  4. Roles versus permission groups
  5. Who this affects, and how to tell if that is you
  6. What to do instead
  7. India-specific considerations
  8. What is still unknown
  9. Comparison: the three doc-fetch routes on developers.cloudflare.com
  10. FAQ
  11. How eCorpIT can help
  12. References

Summary. On 21 August 2026 Cloudflare began returning a documentation_url field inside API 403 responses, and told developers that "Agents can use the documentation_url field to immediately fetch the endpoint's documentation from the 403 error response." Measured against the live site on 23 August 2026, the URL in Cloudflare's own worked example resolves to a 2,012,713-byte HTML document. The .md suffix that used to expose a clean markdown copy of any Cloudflare docs page now returns HTTP 404 across the whole of developers.cloudflare.com. The replacement route, /index.md, does return text/markdown on product pages, but on every /api/** endpoint page it returns content-type: text/html and a payload byte-identical to the HTML page. Two further endpoints measured the same way came back at 1,891,470 and 2,020,583 bytes. And the block the page does surface is headed "Accepted Permissions (at least one required)" and lists API token permission groups, not the account roles the announcement points readers at.

The feature is real and useful for a human sitting in a terminal. The agent workflow it advertises is the part that does not hold up yet.

What changed, and on what date

Cloudflare published "Enriched 403 responses for the Cloudflare API" to its developer changelog with a pubDate of Friday, 21 August 2026, at the path /changelog/post/2026-08-20-contextual-403s/. The change adds a documentation_url field to each entry in the errors array of a 403 Forbidden response. Cloudflare's example body is:


            {
  "success": false,
  "errors": [
    {
      "code": 10000,
      "message": "Forbidden",
      "documentation_url": "https://developers.cloudflare.com/api/resources/workers/subresources/beta/subresources/workers/methods/list"
    }
  ],
  "messages": [],
  "result": null
}
          

Cloudflare gives three reasons for the change. The first is troubleshooting speed: "The linked API docs surface the roles required for each endpoint, making it easier to self-serve access issues." The second is coverage: "Contextual 403 responses are now available across nearly all Cloudflare product APIs." The third is the agent case quoted above. The changelog does not publish a list of which product APIs sit inside "nearly all" and which do not, so there is no way to test coverage other than by hitting endpoints and reading what comes back.

Error code 10000 is the generic Cloudflare authentication error, which is exactly why the extra field matters. A bare {"code": 10000, "message": "Forbidden"} tells an operator nothing about which of roughly 150 account-scoped roles or several hundred token permission groups was missing.

The size problem, measured

The URL in Cloudflare's own example is the "List Workers" endpoint page. Fetched on 23 August 2026 with a browser user agent, it behaves like this:

Request HTTP status Bytes returned Content-Type
.../methods/list (bare, follows a 301) 200 2,012,713 text/html
.../methods/list.md 404 9 text/plain
.../methods/list/index.md 200 2,012,713 text/html
/api/resources/r2/subresources/buckets/methods/list/index.md 200 1,891,470 text/html
/api/resources/zones/methods/list/index.md 200 2,020,583 text/html

Stripping script and style tags from the List Workers page leaves 107,720 characters of visible text. The overwhelming majority of that is the global API navigation tree, which is rendered into every endpoint page: the extracted text opens with hundreds of unrelated entries such as "Abuse Reports", "Submit an abuse report", "List Accounts", "Create an account", "List Roles", "Role Details" and "List Permission Groups" before it reaches anything about Workers. The endpoint's own content sits inside a <main> element well below all of that.

For a person with a browser this is invisible. The sidebar is chrome and the eye goes to the middle of the page. For an HTTP client that fetches a URL and passes the body to a model, it is roughly 2 MB of markup to retrieve and parse in order to reach a three-line permissions list. That is the gap between "immediately fetch the endpoint's documentation" and what the endpoint actually returns.

The .md convention is gone site-wide

The more consequential finding is not specific to the 403 change. Until recently, appending .md to any developers.cloudflare.com docs URL returned a clean markdown rendering of that page, and a lot of tooling was built on that convention. As of 23 August 2026 it returns 404 everywhere we tested:

Page / (HTML) .md /index.md /index.md Content-Type
/fundamentals/manage-members/roles/ 200, 134,158 B 404 200, 73,300 B text/markdown
/fundamentals/api/reference/permissions/ 200, 253,265 B 404 200, 199,493 B text/markdown
/cloudflare-one/traffic-policies/http-policies/package-registry-security/ 200, 306,347 B 404 200, 17,477 B text/markdown
/r2/reference/data-location/ 200, 137,548 B 404 200, 8,038 B text/markdown
/api/resources/zones/methods/list/ 200, 2,020,583 B 404 200, 2,020,583 B text/html

Two things follow. On product documentation the migration is a clear improvement: the R2 data-location page drops from 137,548 bytes of HTML to 8,038 bytes of markdown, and the Gateway package registry page from 306,347 to 17,477. That is a 94 percent reduction on the second one. Under /api/**, the same route exists, returns 200, and gives you HTML anyway.

The 404 body itself is worth noting for anyone writing retry logic. On product paths the .md 404 returns the full styled docs 404 page — 83,293 bytes for the two /fundamentals/ URLs, 232,267 bytes for the Gateway page, 69,330 bytes for the R2 page. Under /api/** the .md 404 returns 9 bytes of text/plain reading Not Found. A client that only checks for a non-empty body, rather than the status code, will treat a 232 KB HTML 404 as a successful documentation fetch. This is the same class of failure we documented when Amazon S3 403 errors began naming the denying policy ARN: the new field is genuinely helpful, and the exclusions are where the automation breaks.

Roles versus permission groups

The changelog says the linked docs "surface the roles required for each endpoint," and one of its three "For more info" links is Cloudflare's Roles page at /fundamentals/manage-members/roles/. The List Workers endpoint page does carry a permissions block. It reads:

Accepted Permissions (at least one required) Workers Tail Read Workers Scripts Write Workers Scripts Read

Those three strings are API token permission groups. They come from the API token permissions reference at /fundamentals/api/reference/permissions/, a page last updated 17 June 2026 that segments its contents into User permissions, Account permissions and Zone permissions. They are not roles.

Roles are a different system. The Roles page lists account-scoped roles assigned to human members of an account — "Administrator", "Super Administrator - All Privileges", "Administrator Read Only", "Analytics", "Cloudflare R2 Admin", "Cloudflare Zero Trust", "Cache Purge", "Firewall", "Log Share" and so on, each described in prose. Grepping the markdown copy of that page for "endpoint", "documentation_url" and "permission group" returns zero matches. The Roles page never maps a role to an API endpoint, and it does not tell you which permission groups a given role carries.

So the self-service loop the changelog describes only closes for one of the two ways a Cloudflare 403 happens. If your API token is missing a permission group, the endpoint page names the exact groups that would work. If a member's account role is too narrow, the endpoint page names token permission groups that are not the thing you need to change, and the roles document it sends you to cannot answer the question. Naming both systems "permissions" in casual writing is what makes this easy to miss. Cloudflare's own consent surface has the same split, which we unpacked in Cloudflare optional OAuth scopes.

Who this affects, and how to tell if that is you

You are affected if any of the following is true.

You run an agent or a script that reads Cloudflare API error bodies and follows links in them. The new field will resolve, return 200, and hand back roughly 2 MB. Check whether your fetch layer has a response size cap, because a silent truncation at 256 KB or 1 MB will cut the page long before the <main> element.

You built tooling on the .md suffix. Anything doing curl "$URL.md" against Cloudflare docs is now getting a 404 body rather than markdown, and if it does not check the status code it is feeding a 404 page into a prompt. Cloudflare's own pages now advertise the replacement in a header line reading Last updated Aug 14, 2026|Copy as Markdown|View as Markdown|Agent setup, where "View as Markdown" points at the /index.md URL.

You are diagnosing a 403 for a human member rather than a token. Read the permissions block on the endpoint page as a statement about tokens, then go to the account member's role separately.

What to do instead

For agents, skip the documentation_url as a fetch target and use the index files Cloudflare publishes for exactly this purpose. https://developers.cloudflare.com/api/llms.txt returns HTTP 200, text/plain, 93,427 bytes. The site-wide https://developers.cloudflare.com/llms.txt is 15,906 bytes, and per-product indexes exist too — https://developers.cloudflare.com/cloudflare-one/llms.txt is 140,897 bytes. Cloudflare embeds a pointer to these directly in its markdown pages, in a blockquote that reads "Fetch the complete documentation index at" followed by the product's llms.txt URL. Cloudflare also now ships an Agent setup section at /agent-setup/, with per-agent guides for Claude Code, Codex and Cursor.

For product documentation, switch every .md call to /index.md and assert on content-type: text/markdown rather than on status code alone. That single assertion catches both failure modes described here: the 404 pages that return HTML bodies, and the /api/** routes that return 200 with HTML.

For 403 handling specifically, keep the documentation_url in your logs. It is a precise, stable identifier for which endpoint was denied, and that is genuinely new information. Treat it as a key, not as a document to download. The same discipline applies across an enterprise AI agent production stack, where every outbound fetch is a cost and a failure mode. Teams already scoping down what their agents can reach, the pattern we covered in Claude Managed Agents domain filters, should note that adding developers.cloudflare.com to an allowlist so an agent can follow these links has a real token cost attached.

The wider point holds beyond Cloudflare. An error message written for a machine has to be tested against a machine. The real cost here is not the missing markdown route; it is that nobody fetched the URL the feature emits and looked at what came back.

India-specific considerations

Indian teams running Cloudflare from a Gurugram or Bengaluru office feel the size issue more than the permissions one. Agent runs billed per input token turn a 2 MB fetch into a recurring line item. At the common rule of thumb of roughly four characters per token, the 2,012,713-byte List Workers page is on the order of 500,000 input tokens if a client passes the raw body to a model, and around 27,000 even after stripping to the 107,720 characters of visible text. Multiply either figure by a CI job that retries on every 403 and the arithmetic stops being trivial.

The roles-versus-permissions distinction also matters for anyone documenting access control under the Digital Personal Data Protection Act 2023. If your access-control register records "the Cloudflare docs say this endpoint needs Workers Scripts Read", that statement describes a token permission group, and it does not describe which staff members can reach the data. Those are separate entries in a register and an auditor will read them as separate controls. We cover the broader mapping problem in the DPDP Act engineering playbook.

What is still unknown

Three things are not answerable from the public record as of 23 August 2026. Cloudflare has not published which product APIs are excluded from "nearly all". It has not said whether the /api/** /index.md behaviour is a deliberate choice or an incomplete migration, and the byte-identical response makes an incomplete migration the more likely reading. And there is no published mapping from account roles to token permission groups, which is the document that would actually close the self-service loop the changelog describes.

Comparison: the three doc-fetch routes on developers.cloudflare.com

Route Product pages (/r2/, /cloudflare-one/, /fundamentals/) API reference pages (/api/**) Safe for an agent?
Bare URL 200, HTML, 134 KB–306 KB measured 200, HTML, 1.89 MB–2.02 MB measured No, size and markup overhead
.md suffix 404, HTML 404 page, 69 KB–232 KB 404, text/plain, 9 bytes No, retired August 2026
/index.md 200, text/markdown, 8 KB–199 KB 200, text/html, identical to bare URL Only on product pages
llms.txt index 200, text/plain, 15,906 B site-wide 200, text/plain, 93,427 B for /api/ Yes, this is the intended path

FAQ

How eCorpIT can help

We build and operate AI agents that call third-party APIs in production, where an unbounded fetch or an unchecked status code turns into a cost line or a silent failure. Our teams instrument the fetch layer, set response size caps, assert on content types rather than status codes, and keep a tested map of which documentation routes actually return machine-readable output. eCorpIT is CMMI Level 5 and ISO 27001:2022 certified, and we design access-control documentation aligned with DPDP requirements. Talk to our agent engineering team about auditing what your agents fetch and what it costs you.

References

  1. Enriched 403 responses for the Cloudflare API, Cloudflare developer changelog, 21 August 2026
  1. List Workers endpoint reference, Cloudflare API documentation
  1. Roles, Cloudflare Fundamentals documentation
  1. API token permissions, Cloudflare Fundamentals documentation, last updated 17 June 2026
  1. Cloudflare API documentation index, llms.txt for the API reference
  1. Cloudflare developer documentation index, site-wide llms.txt
  1. Cloudflare One documentation index, product-scoped llms.txt
  1. Agent setup, Cloudflare developer documentation
  1. List Zones endpoint reference, Cloudflare API documentation
  1. List R2 buckets endpoint reference, Cloudflare API documentation
  1. Data location, Cloudflare R2 documentation
  1. Cloudflare developer changelog RSS feed

Last updated: 23 August 2026. All HTTP status codes, byte counts and content types in this article were measured against the live Cloudflare documentation site on 23 August 2026.

Frequently asked

Quick answers.

01 What exactly did Cloudflare change on 21 August 2026?
Cloudflare added a documentation_url field to each entry in the errors array of API 403 Forbidden responses. The field points at the reference page for the specific endpoint that was denied. Cloudflare says the change is available across nearly all of its product APIs, without publishing a list of the exceptions.
02 Why is the linked documentation page a problem for agents?
The page Cloudflare uses in its own worked example returned 2,012,713 bytes of HTML when measured on 23 August 2026. Two other endpoint pages returned 1,891,470 and 2,020,583 bytes. Most of that payload is the global API navigation tree, which is rendered into every endpoint page rather than loaded separately.
03 Does appending .md to a Cloudflare docs URL still work?
No. As of 23 August 2026 the .md suffix returns HTTP 404 across developers.cloudflare.com. The replacement route is /index.md. On product documentation pages that route returns text/markdown and is much smaller than the HTML. On /api/** endpoint pages it returns text/html at the same byte size as the HTML page.
04 What should an agent fetch instead?
Use the llms.txt index files. The API-wide index at developers.cloudflare.com/api/llms.txt returned 93,427 bytes of text/plain, the site-wide index 15,906 bytes, and the Cloudflare One product index 140,897 bytes. Cloudflare links these from a blockquote at the top of each markdown page and also ships per-agent guides at its Agent setup section.
05 Does the endpoint page show the roles a 403 was caused by?
Not for account roles. The List Workers page shows a block headed "Accepted Permissions (at least one required)" listing Workers Tail Read, Workers Scripts Write and Workers Scripts Read. Those are API token permission groups from the token permissions reference, last updated 17 June 2026, and not the account-scoped roles assigned to members.
06 How are Cloudflare roles and permission groups different?
Roles are assigned to human members of an account and are described in prose on the Roles page, with entries such as Administrator, Cloudflare R2 Admin and Cache Purge. Permission groups are attached to API tokens and are segmented into User, Account and Zone categories. The Roles page contains no mapping from a role to an API endpoint.
07 What breaks in existing retry logic?
Clients that check for a non-empty response body rather than the status code. A .md request against a product page returns a 404 whose body is the full styled docs 404 page, measured at 83,293 bytes for two /fundamentals/ URLs and 232,267 bytes for the Gateway page. Only the /api/** 404 is a short 9-byte plain-text body.
08 Is the change still worth adopting?
Yes, as a logging improvement. The documentation_url is a precise and stable identifier for which endpoint was denied, which a bare error code 10000 with the message Forbidden never gave you. Record it as a key for triage rather than treating it as a document your tooling should download and parse.

About the author

Manu Shukla

Founder & Director

Founder of eCorpIT. Hands-on engineer leading senior-only delivery for AI apps, custom software, and cloud systems for global clients.

Subscribe

One engineering note a week. No fluff, no spam.

Senior-architect playbooks on AI agents, mobile apps, cloud, security, data, and marketing — delivered every Wednesday.

Past the reading

Read enough. Let's build something.

A senior architect responds in 24 working hours with scope, indicative cost, and a timeline. NDA before any technical conversation.