{"slug": "claude-s-api-can-now-ban-one-customer-instead-of-your-whole-org-its-webhook-isn", "title": "Claude's API Can Now Ban One Customer Instead of Your Whole Org. Its Webhook Isn't Live", "summary": "Anthropic released user profiles for the Claude API on October 20, letting SaaS apps, gateways and resellers tag each request with a per-customer ID so that an enforcement action against one end customer no longer applies to the whole organization. A day later the company added enforcement_status and enforcement_actions response fields, but the webhook meant to notify developers of a customer ban accepts registrations and does not yet deliver events, per Anthropic's documentation. Profiles are created via POST /v1/user_profiles and passed in the anthropic-user-profile-id header, and the call is not idempotent, so developers must store the uprof_ ID at onboarding.", "body_md": "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](https://platform.claude.com/docs/en/manage-claude/user-profiles)).\n\nOn October 20, Anthropic shipped the fix: [user profiles](https://platform.claude.com/docs/en/release-notes/overview), 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](https://platform.claude.com/docs/en/release-notes/overview)). [MIXED News](https://mixed-news.com/en/claude-api-user-profiles-ban-one-customer-not-whole-account/) summed it up as resellers being able to get \"one customer banned instead of their whole account.\"\n\nThere'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](https://platform.claude.com/docs/en/manage-claude/organization-webhooks)).\n\nA 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](https://platform.claude.com/docs/en/manage-claude/user-profiles)). When a safety action hits that profile, \"the rest of your traffic is unaffected,\" per the same page.\n\nTwo details make it easy to adopt:\n\nWho 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](https://platform.claude.com/docs/en/manage-claude/user-profiles)). 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](https://platform.claude.com/docs/en/manage-claude/user-profiles)).\n\nThe 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:\n\n``` python\nimport anthropic\n\nprofile_id = \"uprof_...\"  # the ID you stored at signup\nclient = anthropic.Anthropic()\n\nmessage = client.messages.create(\n    model=\"claude-opus-5-5\",\n    max_tokens=1024,\n    messages=[{\"role\": \"user\", \"content\": \"Hello, Claude\"}],\n    user_profile_id=profile_id,\n)\n```\n\nThe same header works on token counting, Message Batches and the OpenAI SDK compatibility endpoint ([docs](https://platform.claude.com/docs/en/manage-claude/user-profiles)).\n\n*With a profile on each request, a restriction can land on one customer's window instead of the whole building.*\n\n**1. Creating a profile isn't idempotent.** \"Every call creates a new profile, even when `reference_id` matches an existing one\" ([docs](https://platform.claude.com/docs/en/manage-claude/user-profiles)). 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.\n\n**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](https://platform.claude.com/docs/en/manage-claude/organization-webhooks)). If you adopted profiles during the beta, remove that value.\n\n**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](https://platform.claude.com/docs/en/manage-claude/user-profiles)). 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.\n\n**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](https://platform.claude.com/docs/en/manage-claude/user-profiles)).\n\n**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](https://platform.claude.com/docs/en/manage-claude/user-profiles)). If you don't know a customer's country, leave it out rather than sending a placeholder you can never remove.\n\nEach profile now reports `enforcement_status` as `unrestricted`, `restricted` or `banned` (the [API reference](https://platform.claude.com/docs/en/api/user_profiles/list) 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](https://platform.claude.com/docs/en/manage-claude/user-profiles)). 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`.\n\nThe 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](https://platform.claude.com/docs/en/manage-claude/user-profiles)). In code, that's one line with a fallback:\n\n``` python\nKNOWN = {\"unrestricted\", \"restricted\", \"banned\"}\n\ndef effective_status(raw: str) -> str:\n    # Anthropic can add values; fail toward caution.\n    return raw if raw in KNOWN else \"restricted\"\n```\n\nKnowing 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.\n\nAnthropic also added [organization webhooks](https://platform.claude.com/docs/en/manage-claude/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](https://platform.claude.com/docs/en/manage-claude/organization-webhooks)).\n\nThen comes the note: \"Anthropic doesn't send the two events, so an endpoint you register receives nothing\" ([docs](https://platform.claude.com/docs/en/manage-claude/organization-webhooks)). You can build the handler today. You can't rely on it today.\n\n*The enforcement webhook is wired to nothing for now. A scheduled check is the doorbell that works.*\n\nEven once delivery goes live, the docs describe a webhook you can't treat as the source of truth ([docs](https://platform.claude.com/docs/en/manage-claude/organization-webhooks)):\n\n`id`. Deduplicate on it.` resolved` event.\nDeliveries follow the [Standard Webhooks](https://www.standardwebhooks.com/) 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](https://platform.claude.com/docs/en/manage-claude/organization-webhooks)).\n\nEvery path above ends at the same call. Anthropic's own catch-up advice is to [list your profiles](https://platform.claude.com/docs/en/api/user_profiles/list) filtered by status ([docs](https://platform.claude.com/docs/en/manage-claude/organization-webhooks)):\n\n```\ncurl -g \"https://api.anthropic.com/v1/user_profiles\\\n?enforcement_status[]=restricted&enforcement_status[]=banned\" \\\n  -H \"x-api-key: $ANTHROPIC_API_KEY\" \\\n  -H \"anthropic-version: 2023-06-01\"\n```\n\nThe `-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](https://platform.claude.com/docs/en/manage-claude/organization-webhooks)). 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](https://platform.claude.com/docs/en/manage-claude/user-profiles)).\n\nRun 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.\n\nProfiles are optional, so a platform that skips them keeps the old risk ([MIXED News](https://mixed-news.com/en/claude-api-user-profiles-ban-one-customer-not-whole-account/)). 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?\n\nA 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](https://www.amazon.com/dp/B0GX3B8371) 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](https://www.amazon.com/dp/B0GYLH38X4) series, covering agents, RAG, prompts, observability, events, databases and system design.\n\nI 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](https://hermes-ide.com/download) or [star it on GitHub](https://github.com/hermes-hq/hermes-ide).", "url": "https://wpnews.pro/news/claude-s-api-can-now-ban-one-customer-instead-of-your-whole-org-its-webhook-isn", "canonical_source": "https://dev.to/gabrielanhaia/claudes-api-can-now-ban-one-customer-instead-of-your-whole-org-its-webhook-isnt-live-3mn0", "published_at": "2026-10-25 05:01:17+00:00", "updated_at": "2026-10-25 05:14:29.868679+00:00", "lang": "en", "topics": ["ai-products", "ai-tools", "developer-tools", "large-language-models", "artificial-intelligence"], "entities": ["Anthropic", "Claude", "Amazon Bedrock", "Google Cloud", "MIXED News"], "also_reported_by": [], "alternates": {"html": "https://wpnews.pro/news/claude-s-api-can-now-ban-one-customer-instead-of-your-whole-org-its-webhook-isn", "markdown": "https://wpnews.pro/news/claude-s-api-can-now-ban-one-customer-instead-of-your-whole-org-its-webhook-isn.md", "text": "https://wpnews.pro/news/claude-s-api-can-now-ban-one-customer-instead-of-your-whole-org-its-webhook-isn.txt", "jsonld": "https://wpnews.pro/news/claude-s-api-can-now-ban-one-customer-instead-of-your-whole-org-its-webhook-isn.jsonld"}}