One Profile per Customer, Created Once
The model is small. You register each end customer once with POST /v1/user_profiles, store the uprof_ ID it returns on your own customer record, and send that ID in the anthropic-user-profile-id header on every request you make for that customer. Profiles are not billable and do not change pricing. Anthropic does not describe them as mandatory; what it describes is the cost of leaving them out.
Three details from the documentation decide how you wire it up:
- Creation is not idempotent. Every call creates a new profile, even when
reference_idmatches an existing one. Create the profile in your onboarding flow and have every service instance read the stored ID, rather than letting workers create profiles on demand and racing each other into duplicates. - Profiles belong to the organization, not the workspace. Anthropic counts a request with no profile ID as your own usage, and recommends keeping your internal usage in a separate organization. Separate workspaces in one organization are not enough.
- Pick the access type per business model. A signed-up user of your product gets an
applicationprofile, the default. A company you resell API access to gets onepassthroughprofile for the company, not one per employee, and you must send that value explicitly.
Creation is rate limited, and there is no cap on the number of profiles. If you are backfilling an existing customer base, Anthropic asks you to contact your account team before registering it in bulk. Tenant isolation inside your own database is a separate layer with its own failure modes; the PostgreSQL row-level security guide covers that side.
Which Header, and Which Beta Value
The question most teams get wrong is the beta header, because user profiles went through several beta versions before launch. The current reference lists user-profiles-2026-03-24, user-profiles-2026-08-18 and user-profiles-2026-09-04. As of October 26, 2026, the guide’s instruction is to send no user-profiles- value in anthropic-beta: under user-profiles-2026-09-04 and earlier, responses leave out both enforcement fields, and the enforcement_status[] list filter is rejected with an invalid_request_error. If an older integration pinned one of those values, removing it is the migration.
The header itself works on Messages, token counting and Message Batches requests. The SDKs expose it as an argument, user_profile_id in Python and TypeScript:
import anthropic
client = anthropic.Anthropic()
message = client.messages.create(
model="claude-opus-5-5",
max_tokens=1024,
messages=[{"role": "user", "content": prompt}],
user_profile_id=customer.anthropic_profile_id,
)
The Claude API checks the ID on every request and returns a 400 invalid_request_error when it is malformed or belongs to another organization, so a wrong ID fails loudly. Batches are the exception that bites. The header applies to every request in the batch, so a batch can carry only one customer’s traffic. Creating a batch checks only the ID’s format: a well-formed ID from the wrong organization still creates the batch, and each request then comes back as an errored result. Group batch jobs by tenant and inspect per-request results, not just the batch status. For the API version and SDK majors that ship the argument, see the 2026-10-21 migration guide.
Finding Restricted and Banned Profiles, All of Them
Every response that returns a profile carries enforcement_status. To find the profiles that need attention, list them with the filter. The pitfall is pagination: with a filter set, the service examines a bounded number of profiles per request, so a page may hold fewer than limit profiles, or none, and still carry a next_page. The reference is explicit that only a null next_page means the listing is complete. An empty page is not the end.
import os
import httpx
STATUSES = ["restricted", "banned", "pending"]
def enforced_profiles():
headers = {
"x-api-key": os.environ["ANTHROPIC_API_KEY"],
"anthropic-version": "2026-10-21",
}
page = None
while True:
params = [("enforcement_status[]", s) for s in STATUSES] + [("limit", "100")]
if page is not None:
params.append(("page", page))
resp = httpx.get("https://api.anthropic.com/v1/user_profiles",
params=params, headers=headers, timeout=30)
resp.raise_for_status()
body = resp.json()
yield from body["data"]
page = body["next_page"]
if page is None:
return
The guide’s example filters on restricted and banned. The reference defines a fourth value, pending, for a profile whose restriction can be cleared by an identity check that the end customer has submitted and Anthropic is still reviewing. That profile still has an open action, so the loop above includes it. Two more documented behaviours belong in the error handling: the filter can fail with overloaded_error when enforcement data cannot be read for the moment, so retry rather than treating the run as clean, and Anthropic may add values, so treat an unrecognised status as restricted.
Map Each Status to Product Behaviour
| Status | What the docs say | A reasonable product response |
|---|---|---|
unrestricted | No action is open. | Serve normally. |
restricted | At least one action is open, none is a ban. Anthropic refuses some or all requests carrying the ID. If an action’s action is verify, the end user can clear it now with an identity check. | Degrade that tenant’s AI features and say why; surface the verification path when verify is set. |
pending | The end user has submitted the identity check and review has not ended. | Show “under review”; do not loop retries against refused requests. |
banned | Anthropic has stopped serving the end user where the ban applies and does not plan to serve them there again. The end user cannot clear it. | Stop sending that customer’s traffic and record your own decision in account_status. |
The product response column is our recommendation, not Anthropic’s. The one rule that follows directly from the docs is the negative one: do not route a restricted customer’s traffic without the header. A request with no profile ID counts as your own usage, which puts the whole organization back in the blast radius you just removed.
Anthropic added organization webhooks to the Admin API on October 20, in beta behind webhooks-2026-10-07, with two events: user_profile.enforcement_action_opened and user_profile.enforcement_action_resolved. Access is by request through your Anthropic account team, and the feature is unavailable to organizations with HIPAA readiness. If event delivery has not been enabled for your organization, use the polling loop above to detect open actions. Keep polling as reconciliation after enabling webhooks: Anthropic drops an event when every retry fails, and sends no opened event for actions opened before event delivery was enabled for your organization or before you registered your endpoint.
What Not to Put in a Profile
A profile holds only what you send about the customer, plus the program and enforcement status that Anthropic sets. It never contains conversation content, but that does not make it a safe place for personal data. Anthropic’s data retention page marks the user profiles API as not eligible for zero data retention and not HIPAA-eligible. Profile data is retained under the standard policy until you delete the profile. HIPAA-enabled organizations can use profiles, but PHI must not be included in the profile data you send.
In practice that means three rules. Use reference_id for your own opaque customer key, not an email address. If you cannot share a name or email, send name_hash and email_hash computed with one function and one normalization across all profiles, because only identically computed hashes match. And keep metadata, which allows at most 16 keys of up to 64 characters with values up to 512 characters, for operational tags such as plan or region. Deleting a profile removes what you supplied but not the trust and safety records Anthropic keeps about that ID, and inference requests carrying the deleted ID can still succeed for about 10 minutes.
The operating decision
Create one profile per end customer at onboarding: an individual product user for application, or a company buying API access for passthrough. Store the ID and refuse to send that customer’s traffic without it. Remove any user-profiles- beta value, batch by profile ID, and run the filtered listing on a schedule until next_page is null. Minimize the data you put in profiles: use opaque IDs and hashes where possible, and include no PHI. The broader integration patterns live in the Claude API guide.
Sources checked October 26, 2026
- Anthropic: Claude Platform release notes, October 20 and October 21, 2026
- Anthropic: User profiles for end-customer attribution
- Anthropic: User Profiles API reference (create, update, delete, beta header values)
- Anthropic: List User Profiles (enforcement_status filter, pagination)
- Anthropic: Organization webhooks (beta)
- Anthropic: API and data retention, feature eligibility table