Claude's API Can Now Ban One Customer Instead of Your Whole Org. Its Webhook Isn't Live

Claude's API Can Now Ban One Customer Instead of Your Whole Org. Its Webhook Isn't Live

Book: Event-Driven Architecture Pocket Guide The series: Pocket Guides for Developers — 8 short books on agents, RAG, prompts, observability, events, databases and system design — the whole series My project: Hermes IDE | GitHub — an IDE for developers who ship with Claude Code and other AI coding tools Me: xgabriel.com | GitHub You run a SaaS app, a gateway or a reseller on the Claude API. Every request you send for your customers arrives under one organization: yours. Anthropic's own docs spell out what that has meant until now: "one end customer can trigger an enforcement action that applies to your whole organization" (Anthropic, user profiles). On October 20, Anthropic shipped the fix: user profiles, so you can tell it which end customer each API request is for. On October 21 it added enforcement_status and enforcement_actions fields, so you can see whether Anthropic has restricted or banned one of your customers (release notes). MIXED News summed it up as resellers being able to get "one customer banned instead of their whole account." There's a catch in the fine print. The webhook that's supposed to tell you when a customer gets banned exists, takes registrations, and sends nothing. Anthropic's docs say so in a note: "Event delivery isn't live" (organization webhooks). What a user profile is A profile is a record you create once per end customer with POST /v1/user_profiles. It returns an ID that starts with uprof_, and you send that ID in the anthropic-user-profile-id header on every request you make for that customer (docs). When a safety action hits that profile, "the rest of your traffic is unaffected," per the same page. Two details make it easy to adopt: Profiles "aren't billable and don't affect pricing" (docs). A profile doesn't contain conversation content, and Anthropic doesn't verify the details you send (docs). Who gets a profile depends on what you sell. A signed-up user of your app gets an application profile, the default. A company you resell API access to gets one passthrough profile for the whole company, not one per employee (docs). A request with no profile ID counts as your own usage, and Anthropic recommends keeping that usage in a separate organization, because profiles belong to an organization, not a workspace (docs). The SDKs take the ID as an argument. This is the Python call from Anthropic's page, with the import and the stored ID added and the print removed: import anthropic profile_id = "uprof_..." # the ID you stored at signup client = anthropic.Anthropic() message = client.messages.create( model="claude-opus-5-5", max_tokens=1024, messages=[{"role": "user", "content": "Hello, Claude"}], user_profile_id=profile_id, ) Enter fullscreen mode Exit fullscreen mode The same header works on token counting, Message Batches and the OpenAI SDK compatibility endpoint (docs). With a profile on each request, a restriction can land on one customer's window instead of the whole building. Five traps before you ship it 1. Creating a profile isn't idempotent. "Every call creates a new profile, even when reference_id matches an existing one" (docs). Create it in your onboarding flow, store the uprof_ ID on the customer record, and have every service read it from there. A worker that calls create on startup will mint a new profile per boot. 2. An old beta header hides the ban fields. Responses leave out enforcement_status and enforcement_actions when your anthropic-beta header carries user-profiles-2026-09-04 or an earlier value, and the enforcement_status[] list filter returns a 400 (webhooks docs). If you adopted profiles during the beta, remove that value. 3. Bedrock and Google Cloud don't look the profile up. Profiles also work on Claude in Amazon Bedrock and on Claude Models on Google Cloud (Google managed), but you create them on the Claude API or Claude Platform on AWS. Those two platforms check only the ID's format. "They accept any well-formed ID, so a successful response doesn't confirm that the ID matches a profile" (docs). On Bedrock, the ID goes in a user_profile_id field in the request body, not a header, and the older InvokeModel and Converse integration doesn't support it at all. 4. On Claude Platform on AWS, the fields always say unrestricted. There, enforcement_status is always unrestricted and the actions list is always empty, and Anthropic doesn't refuse requests there because of an open action (docs). 5. You can't clear a field. You can replace a value in external_user_details, but the API rejects null with a 400 (docs). If you don't know a customer's country, leave it out rather than sending a placeholder you can never remove. Reading the ban status Each profile now reports enforcement_status as unrestricted, restricted or banned (the API reference also lists pending, while an identity check is under review). "While an action is open, Anthropic refuses some or all inference requests that carry the profile's ID" (docs). Each entry in enforcement_actions has a type (restriction or ban), a reason such as fraud_or_abuse or account_compromise, an action the customer can take or null, and an expires_at time or null. The docs also tell you how to handle values they haven't invented yet: treat an unknown enforcement_status as restricted and an unknown reason as unspecified (docs). In code, that's one line with a fallback: KNOWN = {"unrestricted", "restricted", "banned"} def effective_status(raw: str) -> str: # Anthropic can add values; fail toward caution. return raw if raw in KNOWN else "restricted" Enter fullscreen mode Exit fullscreen mode Knowing a customer is restricted lets your app show them a real message instead of a stream of failed requests, and lets your support team see the reason before the customer opens a ticket. The webhook takes registrations and sends nothing Anthropic also added organization webhooks in beta, behind the webhooks-2026-10-07 header. They define two events, user_profile.enforcement_action_opened and user_profile.enforcement_action_resolved. To get access, you contact your Anthropic account team (docs). Then comes the note: "Anthropic doesn't send the two events, so an endpoint you register receives nothing" (docs). You can build the handler today. You can't rely on it today. The enforcement webhook is wired to nothing for now. A scheduled check is the doorbell that works. Even once delivery goes live, the docs describe a webhook you can't treat as the source of truth (docs): Events carry only IDs. You fetch the profile to see what changed. The same event can arrive more than once, with the same id. Deduplicate on it. Events "might arrive late or out of order." Use the profile's current state. Failed deliveries are retried "a few times over the next few minutes," then dropped. An action opened before you registered never produces an event. Deleting a profile closes its actions without a resolved event. Deliveries follow the Standard Webhooks spec, so verify the webhook-signature header with your endpoint's whsec_ secret, and return a 2xx within 5 seconds before doing slow work (docs). Poll now, keep polling later Every path above ends at the same call. Anthropic's own catch-up advice is to list your profiles filtered by status (docs): curl -g "https://api.anthropic.com/v1/user_profiles\ ?enforcement_status[]=restricted&enforcement_status[]=banned" \ -H "x-api-key: $ANTHROPIC_API_KEY" \ -H "anthropic-version: 2023-06-01" Enter fullscreen mode Exit fullscreen mode The -g flag stops curl from treating the brackets as a glob. A page can come back with fewer profiles than limit, or none, before the list ends, so keep requesting pages until next_page is null (docs). Also, a status change "doesn't reliably move updated_at," so compare the two enforcement fields with the values you last stored, not the timestamp (docs). Run that on a schedule now. When event delivery goes live, keep the job as the reconciliation pass behind the webhook. The webhook tells you sooner; the scheduled list call catches whatever it repeats, reorders or drops. Profiles are optional, so a platform that skips them keeps the old risk (MIXED News). If you send traffic for other people, creating a profile per customer and sending one header is a cheap way to keep one bad customer's ban from landing on everyone else. Are you creating profiles at signup, or backfilling an existing customer base first? If this was useful A ban webhook that can repeat, reorder and drop events is the textbook case for idempotent consumers and a reconciliation job. If you're wiring one up, my Event-Driven Architecture Pocket Guide walks through the event-driven patterns around it: sagas, CQRS, the outbox, and the traps nobody warns you about. It's one of 8 short books in the Pocket Guides for Developers series, covering agents, RAG, prompts, observability, events, databases and system design. I also build Hermes IDE, a free desktop app for running Claude Code, Codex and other coding agents side by side, each on its own branch: download it or star it on GitHub.

Original Source

Read the full article at Dev →

KhanList aggregates and links to publicly available news content. We do not host full articles from third-party sources. Always verify important information with original sources.