News

The Claude API Beta Headers That Just Went GA: What Removing Them Changes

The Claude Platform release notes for August 19, 2026 carry three general-availability entries in a row: the Files API, Agent Skills together with the Skills API, and the Admin API's user-management endpoints for Claude Enterprise organizations. Each entry ends the same way — the beta header is no longer required, and requests that still send it keep working. Nothing is switched off, so the change reads as paperwork. On one of the three endpoint families it is not. There, the header you delete is the thing that decides which response body comes back.

August 20, 2026

A dark machine room under cyan light: a low document sorting machine with its glass hood raised. One pale envelope with a small gold seal rests in a lit tray at the front, and a second envelope lies in a separate tray to the right under warm amber light.

Three Headers, One Date, Three Different Risks

Because the three entries share a date and a sentence structure, they invite a single cleanup ticket: find every anthropic-beta value that is now redundant and delete it. The per-feature documentation does not support that reading. Each page says something different about what a request looks like once the header is gone.

Beta name Where it applies What changes when you remove it
files-api-2025-04-14 /v1/files, and Messages requests that reference an uploaded file The list endpoint switches pagination scheme, and file objects start carrying expires_at
skills-2025-10-02 /v1/skills, and container.skills in Messages requests Nothing. Both headers "remain valid opt-ins", and the guide's own examples still send them
ce-user-management-2026-07-13 Group and custom-role requests on the Admin API Nothing. Requests that still send it "are accepted and behave identically"

Two of the three really are free. The Skills guide states that Skills are generally available and require no anthropic-beta header, for either the Skills API or container.skills in Messages requests, and that its examples keep sending skills-2025-10-02 only because it still works. The user-management page is flatter still: the header is no longer required on group and custom-role requests, and the group and custom-role examples on that page continue to send it. Neither documents any difference in what comes back. The Files API is the one that does.

What a Files Response Actually Does

The Files API documentation spells the difference out in two places, and both are about reads rather than writes. With the beta header, the list endpoint "paginates with before_id and after_id, returns has_more, first_id, and last_id instead of next_page, and rejects the page and ids[] parameters as unknown fields." Without it, GET /v1/files takes page and returns next_page, with limit defaulting to 20 and capped at 1,000. The second difference is smaller and harder to see: file objects returned under the header "omit expires_at instead of returning null when no expiration is set."

Neither of those is an error. A pagination loop written against the beta shape does not throw when the shape changes underneath it. It stops early instead.

// Written against the beta list format. Dropping the header does not
// fail this loop - it makes it return page one and call that the answer.
const all = [];
let params = { limit: 100 };
for (;;) {
  const page = await listFiles(params);   // GET /v1/files
  all.push(...page.data);
  if (!page.has_more) break;              // GA: has_more absent -> falsy -> break
  params.after_id = page.last_id;         // GA: last_id absent  -> undefined
}

A strict deserializer would at least raise on the missing fields. A permissive one — a hand-rolled fetch wrapper, a dynamic language, a JSON blob passed straight into business logic — returns the first 100 files and reports success. The same asymmetry runs the other way for the field: code that decides whether a file is temporary by testing for the presence of the expires_at key now sees that key on every file, holding null for the files that never expire. A presence check that was reliable under the beta format is wrong under the GA format, and nothing in the request tells you so.

Uploaded files are scoped to the workspace, not to your user

This is unrelated to the header change, but the GA notice is what will send you back to this documentation page, so read it in the same sitting. Uploaded files "are accessible to your entire workspace, not scoped to an end user, conversation, or session", any API key in that workspace can read any file in it, and the guidance is explicit that you should never accept file_id values from end users. For a multi-tenant product the recommended isolation boundary is one workspace per tenant, with up to 100 workspaces per organization. If the cleanup touches the code that maps your users to their files, that is the moment to check this. The tenancy and data-handling side is covered in the AI data privacy chapter.

In an SDK, the Header Is Not a String You Can Grep

Here is the part that turns a ten-minute ticket into an incident. The documentation states that the SDK beta.files methods and the CLI's ant beta:files commands add the header automatically, and the cURL examples on the Files page include it by hand. So in a typical Python or TypeScript service, nobody ever typed files-api-2025-04-14 anywhere. Grep your repository for that string and you get zero hits while every list call you make is still receiving the beta response format.

Which means the migration is not an edit to a header map. It is a move off the beta namespace: client.beta.files.list() becomes the non-beta call, and the day it does, the response shape changes with it. Inventory by call site instead — beta.files usage, ant beta:files in scripts, an explicit betas=[...] argument, a raw header on a hand-built request — and treat each one as its own switch. The general discipline for changing an API contract without breaking the clients on the other side is in the AI API contract change plan; this is that same problem seen from the client's chair, where you do not get to choose the timing but you do get to choose the order.

One relief: the Messages side is genuinely inert. Requests that use an uploaded file as a document or image source, or in a container_upload block for the code execution tool, "work with or without the header". The blast radius is confined to what /v1/files hands back.

What the GA Format Gives You Back

There is a reason to do this beyond tidiness, and it is the expiration and lookup behaviour that exists only on the header-free path.

An Order That Stays Reversible

  1. Do Skills and user management first. Both are documented as behaving identically with and without the header. Removing them clears two thirds of the ticket and leaves exactly one real change to think about.
  2. Inventory the Files call sites, not the string. The SDK adds the header for you, so the audit runs over beta.files usage rather than over a grep result.
  3. Split reads from writes. Upload, retrieve-metadata, download, and delete look the same either way. The endpoint whose shape moves is GET /v1/files. That is where the review time goes.
  4. Fix the pagination loop before you flip anything. Make it handle both shapes: page on next_page when the field is present, on has_more plus last_id when it is not. Once the loop is shape-agnostic, the switch itself becomes inert.
  5. Make the header a runtime flag, not a deleted line. One environment variable decides whether the call goes through the beta namespace. Flip it in staging, watch the list path, and if a downstream consumer chokes, flip it back in one deploy instead of reverting a code change under pressure.
  6. Adopt the new fields afterwards, deliberately. expires_in_seconds and ids[] are the payoff, not part of the cutover. Shipping them in the same change removes your ability to say which edit caused the behaviour you are looking at.

One testing note, because it is the assertion most likely to be missing. A test that checks data[0].id passes under both formats and proves nothing. Assert on the pagination envelope itself — that a second page was fetched at all, and that the total count matches a known fixture — because the envelope is the only part that changed.

Not a Shutdown, and That Is Exactly the Problem

It is worth being precise about how this differs from a deprecation. When a provider removes an endpoint, as with the Assistants API shutdown on August 26, you get a date, a documented replacement, and eventually an error that forces the issue whether or not anyone scheduled the work. Here there is no published end date, no error, and no forcing function at all. The old behaviour continues for anyone who does nothing under the documentation checked today. The only event that changes your response format is an edit you make yourself, in whatever week someone decides to tidy up headers — which is also the week nobody is watching the file-listing code path.

That said, a beta name is not a permanent parking spot either. An invalid beta name, or one your organization cannot access, returns a 400 whose message reads "Unexpected value(s) ... for the anthropic-beta header." No removal date has been published for any of these three names, and today's documentation says requests that still send them keep working. But "keeps working" is a present-tense statement about three specific strings, not a guarantee about next year. Treat the header as a rollback lever with a shelf life: worth keeping for the length of one cutover, not worth leaving in the code indefinitely. If you are wiring any of this up for the first time rather than migrating it, the groundwork is in the Claude API chapter.

Sources checked August 20, 2026