# Anthropic user profiles: attribute API traffic to end customers

URL: https://infro.io/blog/anthropic-user-profiles-end-customer-attribution
Author: INFRO Editorial (https://infro.io/blog/editorial)
Publisher: INFRO
Published: 2026-10-27
Last modified: 2026-10-27
Last reviewed: 2026-10-27 (next review due 2026-11-26)
Topic: Security and governance
Format: Guide

> Anthropic launched user profiles on 20 October 2026: you register each end customer with `POST /v1/user_profiles`, store the `uprof_` id, and send it in the `anthropic-user-profile-id` header on every request made for that customer. A safety enforcement action, a restriction or a ban, can then apply to that one profile instead of your whole organization, and the profile's `enforcement_status` tells you when it happens. Profiles are not billed. OpenAI's equivalent is the `safety_identifier` request field, which replaces the deprecated `user` field. Attribution limits the blast radius of abuse; it does not prevent it, so pair it with per-customer spend caps and usage monitoring.

Key takeaways:
- Without a profile on the request, Anthropic says one end customer can trigger an enforcement action that applies to your whole organization.
- Register each end customer once with `POST /v1/user_profiles`, store the `uprof_` id, and send it in the `anthropic-user-profile-id` header; the create call is not idempotent.
- Use `access_type: application` for a signed-up user of your product and `passthrough` for a company you resell API access to; send no profile for your own internal traffic, ideally from a separate organization.
- Poll `enforcement_status` or subscribe to organization webhooks, so you learn that Anthropic restricted a customer before that customer tells you.
- OpenAI's equivalent is `safety_identifier` in the request body; OpenAI can block a flagged identifier, and repeated high-severity violations can still cost the whole organization access.
- Profiles narrow who is punished for abuse; per-customer spend caps and usage views are what stop one customer running up your provider bill.

Anthropic's user profiles exist so that one bad end customer does not take your whole product offline. Launched on 20 October 2026, they let you "tell Anthropic which of your end customers each API request is for", so that a safety enforcement action can apply to one end customer instead of the whole organization[1]. Anthropic's guide states the problem plainly: if you operate a platform or gateway, or resell API access, your end customers' requests arrive under your organization, and without a profile on the request "one end customer can trigger an enforcement action that applies to your whole organization"[2].

For a SaaS company with thousands of users on one Anthropic account, that is the difference between suspending one account in your own admin panel and explaining to every customer why the AI features stopped. This guide covers how profiles work, how to roll them out, how OpenAI's `safety_identifier` compares, and what attribution does not solve.

## How user profiles work

The mechanism has three parts[2]:

1. **Register** each end customer once with `POST /v1/user_profiles`. Anthropic returns a profile with an id that starts with `uprof_`. Store it on your customer record.
2. **Attribute** every request you make on that customer's behalf by sending the id in the `anthropic-user-profile-id` header. The official SDKs and CLI take a `user_profile_id` argument that sets the header for you. The header works on Messages, token counting, Message Batches and the OpenAI SDK compatibility endpoint.
3. **Watch** the profile's `enforcement_status`: `unrestricted`, `restricted` or `banned`. While an action is open, Anthropic refuses some or all inference requests that carry that profile's id.

Profiles are not billed and do not change prices[2]. A profile holds only what you send about the customer plus any status Anthropic sets; it does not contain conversation content, and Anthropic does not verify the details[2].

### Choosing who gets a profile

| Who the traffic is for | Profile | `access_type` |
| --- | --- | --- |
| A signed-up user of your product | One per user, created at sign-up | `application` (the default) |
| A company you resell API access to | One per company, not per person | `passthrough` (must be sent) |
| Your own organization | None; a request with no profile counts as your own usage | Not applicable |
| A workspace in your organization | No separate profile; profiles belong to the organization | Not applicable |

Table: Which traffic gets a profile, from Anthropic's user profiles guide, read 2026-10-27.

Anthropic recommends keeping end-customer traffic in one organization and your own internal usage in another, because profiles belong to an organization, not a workspace, and separate workspaces are not enough[2]. For a `passthrough` profile, Anthropic also asks you to send an identifier for the individual person in the Messages request's own `metadata.user_id` when one person is identifiable, so it can detect misuse by one person inside a company[2].

## Implementing profiles

Create the profile in your onboarding flow, not at request time:

```python
import anthropic

client = anthropic.Anthropic()

def ensure_profile(customer) -> str:
    """Create a profile once per end customer and store its id."""
    if customer.anthropic_profile_id:
        return customer.anthropic_profile_id
    profile = client.user_profiles.create(
        # application is the default: one signed-up user of your product
        external_user_details={
            "reference_id": customer.id,          # your own opaque id
            "country": customer.country_code,     # the customer's country, not yours
            "email_hash": customer.email_sha256,  # a hash, never the address
        },
    )
    customer.anthropic_profile_id = profile.id
    customer.save()
    return profile.id

def answer(customer, messages):
    return client.messages.create(
        model="claude-sonnet-5-5",
        max_tokens=1024,
        messages=messages,
        user_profile_id=ensure_profile(customer),
    )
```

Five details from Anthropic's guide decide whether this works in production[2]:

- **The create call is not idempotent.** Every `POST /v1/user_profiles` makes a new profile, even with the same `reference_id`. Guard creation with a database lock or a unique job per customer, and have service instances read the stored id rather than creating their own.
- **Send less, not placeholders.** Every field is optional. Leave out what you do not know; once set, a value in `external_user_details` can be replaced but not cleared. If you cannot share a name or email address, send `name_hash` and `email_hash`, computed with the same function and normalization for every profile.
- **Unknown or foreign ids fail.** On the Claude API, a malformed id, or one that belongs to another organization, returns `400 invalid_request_error`. On Message Batches the batch is created and each request in it errors instead.
- **Keep profiles current.** Update `external_user_details.account_status` when you suspend a customer yourself; delete the profile when the customer closes the account or asks you to erase their data, after you stop sending its id. Deleting does not remove Anthropic's trust and safety records for that id.
- **Bulk backfills need a conversation.** Profile creation is rate limited; contact your Anthropic account team before registering an existing customer base in bulk.

### Knowing when Anthropic acts

An enforcement action has a `type` (`restriction` or `ban`), a `reason` (one of `unsupported_region`, `fraud_or_abuse`, `high_harm`, `usage_policy_violation`, `account_compromise` or `unspecified`), an optional `action` the customer can take to clear it, and `opened_at` and `expires_at` times[2]. These fields arrived on 21 October 2026, with a list filter on `enforcement_status`[1]. To find affected customers, list profiles with `enforcement_status[]=restricted` and `enforcement_status[]=banned`; to be told, subscribe to organization webhooks, which were in beta and required contacting your account team at launch[1][2]. Anthropic notes that a status change does not reliably move `updated_at`, so compare the fields themselves[2].

Wire this into your own support tooling. When a profile becomes `restricted`, your product should show that customer a clear message and stop retrying, rather than surfacing a generic error.

### Platform coverage

Profiles are created on the Claude API, Claude Platform on AWS or Microsoft Foundry deployments hosted on Anthropic[2]. Amazon Bedrock and Google-managed Vertex do not have the endpoints: you create profiles on the Claude API or Claude Platform on AWS, link the AWS account or Google Cloud project in Anthropic's Verification Portal, and send the id with each request (a top-level `user_profile_id` body field on Claude in Amazon Bedrock). Those platforms check only the id's format, so a successful response does not confirm the profile exists, and the older Bedrock `InvokeModel` and `Converse` integration does not take profiles at all[2]. On Claude Platform on AWS, `enforcement_status` is always `unrestricted` and Anthropic does not refuse requests there because of an open action[2].

## How OpenAI's safety_identifier compares

OpenAI's equivalent is a request field, not a registered object. `safety_identifier` is "a stable identifier used to help detect users of your application that may be violating OpenAI's usage policies", up to 128 characters, and OpenAI recommends hashing the username or email[3]. The older `user` field is deprecated: OpenAI's reference says it "is being replaced by `safety_identifier` and `prompt_cache_key`"[3].

OpenAI's safety checks guide describes what happens when an identifier is flagged: for lower-severity concerns streaming may be delayed while more checks run, and for a high-confidence violation the identifier can be completely blocked from model access, with future requests for it returning an "identifier blocked" error[4]. The guide ties these checks to GPT-5 requests, and warns that an organization that repeatedly hits high thresholds can still lose access as a whole[4]. Send it in the body of Chat Completions, Responses and Images requests, and as the `OpenAI-Safety-Identifier` header for Realtime sessions[4].

|  | Anthropic user profiles | OpenAI `safety_identifier` |
| --- | --- | --- |
| Where it goes | `anthropic-user-profile-id` header (body field on Bedrock) | Request body field (header for Realtime) |
| Registration | `POST /v1/user_profiles` once per customer | None; any stable string |
| What you can query | `enforcement_status`, `enforcement_actions`, webhooks | No status endpoint described |
| Company vs person | `passthrough` profile for a company, plus `metadata.user_id` for a person | One identifier per user |
| Effect of a violation | Restriction or ban on the profile | Delay, or a block on the identifier |
| Organization still at risk | Unattributed traffic counts as yours | Repeated high-severity violations |

Table: Anthropic user profiles against OpenAI's safety_identifier, from both vendors' pages read 2026-10-27.

Where each loses: profiles take more work (onboarding code, a stored id, a backfill, a status check), and they protect you only on requests that carry a valid id. `safety_identifier` is one field to add, but there is no object you can query to learn that a customer was blocked; you find out from errors.

## What attribution does not solve

Attribution limits who is punished; it does not stop the abuse, and it does nothing about cost. One end customer scripting your feature as a free model proxy still runs up your provider bill until something refuses their requests. For a SaaS product the full control set is:

- **Attribution** on every provider: user profiles for Anthropic, `safety_identifier` for OpenAI.
- **Per-customer limits** in your own path: a spend or request cap per end customer, refused before the provider call, with an error your app can show.
- **Per-customer usage** you can see and bill from, so an outlier is visible in hours, not at month end.
- **An off switch** in your own admin panel, mirrored in the profile's `account_status`.

For leaders, the questions to ask are: does every AI request carry the end customer's identity to the provider, what stops one customer spending a hundred times the median, and who is alerted when a provider restricts a customer.

## Per-customer caps and usage with INFRO

INFRO is a control plane that sits between your applications and the provider accounts you already hold. You connect your own Anthropic and OpenAI accounts, your providers bill you directly, and INFRO adds the per-customer controls that attribution alone does not give you.

**Identify the end customer.** Your app sends an opaque end-customer id in `metadata.user_id` (or the `X-INFRO-User-Id` header) on each request through INFRO.

**Cap each end customer.** [End-customer caps](https://infro.io/docs/org/end-customer-caps) set one spend cap for every end customer, per organization or per project, with overrides your backend sets through the management API. A request that would pass its customer's cap is refused before it reaches your provider, with a `402` naming the customer and when the cap resets. Caps count INFRO's estimate of provider cost, from your negotiated or the list rate.

**See and bill usage.** **Budgets → End customers** lists your most expensive end customers, and [usage per end customer](https://infro.io/docs/org/end-customer-usage) exports requests, tokens and estimated provider cost per customer, by day or month, for you to bill your own users from in your billing system. Both are on every plan.

What INFRO does not do today: it does not create Anthropic user profiles, and it does not forward the `anthropic-user-profile-id` header, so requests sent through INFRO reach Anthropic without a profile and count as your organization's own usage. On `/v1/chat/completions` it passes OpenAI's `user` field to Anthropic as `metadata.user_id`, and forwards top-level parameters it does not model, such as `safety_identifier`, to an OpenAI connection unchanged; neither is a substitute for a profile. INFRO's caps reduce how much one customer can spend, not whether Anthropic attributes an enforcement action to them.

## Questions

### Do Anthropic user profiles cost anything?

No. Anthropic's guide says profiles are not billable and do not affect pricing. Profile creation is rate limited, and Anthropic asks you to contact your account team before registering an existing customer base in bulk.

### What is the difference between metadata.user_id and a user profile?

A profile identifies the end customer the request is for, in the `anthropic-user-profile-id` header, and is what lets an enforcement action land on that customer alone. `metadata.user_id` in the Messages request body identifies an individual person; Anthropic suggests sending it alongside a `passthrough` profile so it can spot misuse by one person inside a company.

### What happens to requests for a banned profile?

While an enforcement action is open, Anthropic refuses some or all inference requests that carry that profile's id. The profile's `enforcement_status` reads `restricted` or `banned`, and `enforcement_actions` lists each open action with a reason and, where one exists, what the end customer can do to clear it.

### Does OpenAI have the same feature?

The closest is `safety_identifier`, a stable per-user string you send in the request body of Chat Completions, Responses and Images requests (a header for Realtime). OpenAI may delay or block an identifier it flags, and says repeated high-severity violations can still cost the whole organization access to the affected models. There is no profile object to register or query.

### Do user profiles work on Amazon Bedrock and Vertex AI?

Partly. Bedrock and Google-managed Vertex do not have the `/v1/user_profiles` endpoints, so you create profiles in an organization on the Claude API or Claude Platform on AWS, link your AWS account or Google Cloud project to it, and send the id with each request. Those platforms check only the id's format, not that the profile exists.

## Sources

1. Claude Platform API release notes — Anthropic. https://platform.claude.com/docs/en/release-notes/api (accessed 2026-10-27)
2. User profiles for end-customer attribution — Anthropic. https://platform.claude.com/docs/en/manage-claude/user-profiles (accessed 2026-10-27)
3. Responses API reference — OpenAI. https://developers.openai.com/api/reference/python/resources/beta/subresources/responses/methods/retrieve (accessed 2026-10-27)
4. Safety checks — OpenAI. https://developers.openai.com/api/docs/guides/safety-checks (accessed 2026-10-27)
