Design your compliance integration
Choose between polling and cursor-driven Activity Feed consumption, correlate Compliance API events with your SIEM, and plan retention.
A production Compliance API integration makes three design choices: how it consumes the Activity Feed, how its output correlates with your security information and event management (SIEM) system, and where long-term copies of activity and content live. These choices are independent of the endpoints themselves; this page helps you evaluate the tradeoffs.
This page assumes you have read the following pages:
- Query the Activity Feed, which defines the parameters and pagination contract referenced throughout.
- Retrieve and delete chats, files, and projects, which defines the chat, file, and project endpoints and the
deleted_atsemantics referenced in Plan content retention. - Retrieve session transcripts, which defines the local and remote session endpoints.
Choose a feed-consumption pattern
The Activity Feed supports two consumption patterns: periodic window polling bounded by created_at.gte and created_at.lt, and cursor-driven incremental reads that persist a cursor from one response and pass it on the next request. Both return identical Activity objects; the difference is the state your client persists between calls.
Both patterns share these constraints:
- Activities are queryable within 1 minute of occurring and retained for 6 years. Recording is not retroactive: it begins when the Compliance API is first enabled for your organization, and activity from before enablement is not backfilled.
- The maximum
limitfor each page is 5,000. - Cursor values are opaque strings that you must not parse.
- Requests are limited to 600 per minute per parent organization, shared across every key, every linked organization, and every
/v1/compliance/*endpoint; unlike the local session endpoints, the remote session endpoints carry a second request budget on top. See 429 Too Many Requests for the response headers and retry contract.
| Pattern | Choose when |
|---|---|
| Window polling | Your pipeline runs on a fixed schedule, you prefer stateless workers, and you can tolerate replaying or overlapping windows |
| Cursor-driven incremental reads | You want the lowest latency between an activity occurring and your pipeline ingesting it, you want to avoid re-reading pages you already drained, and you have a durable place to persist a cursor between runs |
Window polling
Set created_at.lt at least 1 minute in the past so that every activity in the window is already queryable. Use created_at.gte for the lower bound and created_at.lt for the upper bound so that consecutive windows tile without gaps or overlap; reuse the previous window's lt value as the next window's gte.
curl --fail-with-body -sS -G \
"https://api.anthropic.com/v1/compliance/activities" \
--header "x-api-key: $ANTHROPIC_COMPLIANCE_ACCESS_KEY" \
--header "anthropic-version: 2023-06-01" \
--data-urlencode "created_at.gte=2026-04-20T07:00:00Z" \
--data-urlencode "created_at.lt=2026-04-20T08:00:00Z" \
--data-urlencode "limit=5000"When the response has has_more: true, the window contains more than one page of activities. Either page within the window by passing the response's last_id as after_id on the next request (stopping when has_more is false), or choose a smaller time window. See Paginate results for the full contract.
Even with clean tiling, an activity that indexes after its window has closed never appears in a later window. Deduplicate on the activity id and either widen each new window so it overlaps the previous one by a few minutes or run a periodic reconciliation pass that re-queries an older window.
Cursor-driven incremental reads
first_id="activity_01XyDMpzjS89pFZXqSFUBDr6" # first_id from a previous response
curl --fail-with-body -sS -G \
"https://api.anthropic.com/v1/compliance/activities" \
--header "x-api-key: $ANTHROPIC_COMPLIANCE_ACCESS_KEY" \
--header "anthropic-version: 2023-06-01" \
--data-urlencode "limit=5000" \
--data-urlencode "before_id=$first_id"Page through until has_more is false, then persist first_id from the final response and pass it unchanged as before_id on the next run to retrieve activities newer than the saved cursor. To walk in the opposite direction for a backfill, persist last_id and pass it as after_id instead. For the full cursor-vs-page-token reference and retry semantics, see Paginate results.
A production catch-up loop fetches activities recorded since your last poll by driving iteration off has_more and first_id:
cursor = stored_cursor
loop:
page = GET /v1/compliance/activities?before_id={cursor}&limit=5000
store(page.data)
if page.first_id is not null:
cursor = page.first_id
if not page.has_more: break
persist(cursor)Cursors survive key rotation; see Manage and rotate keys.
Correlate with your SIEM
Each Activity carries fields you can join against events already in your SIEM (Splunk, Datadog, Microsoft Sentinel, Cribl, or similar):
| Compliance API field | Join target |
|---|---|
actor.user_id | Your identity provider's stable user identifier |
actor.email_address | Directory email when a stable ID is unavailable |
actor.ip_address | Network, VPN, and endpoint logs |
actor.user_agent | Endpoint and device inventory, and the client app that made the request |
created_at | Time-window correlation across any source |
actor.user_id and actor.email_address are present when actor.type is user_actor. actor.ip_address and actor.user_agent are absent on some actor types, such as anthropic_actor and scim_directory_sync_actor. Check the discriminator before reading any of these fields. user_id is a stable, opaque identifier for the user account: it is consistent across every Compliance API endpoint and activity payload, and it does not change when the user's email or display name changes. Use user_id, not email_address, as the primary join key.
Calls to the Compliance API itself emit compliance_api_accessed activities. Ingest these alongside other activity types so your SIEM records who queried compliance data, and when. Pass activity_types[]=compliance_api_accessed to scope the query, then in your client, read actor.api_key_id from each activity whose actor.type is api_actor to attribute the access to a specific Compliance Access Key or Admin API key.
Plan content retention
Five retention horizons govern what you can retrieve later:
| Data | Retained for | Controlled by |
|---|---|---|
| Activity Feed records | 6 years | Anthropic |
| Chat, file, and project content | Your organization's claude.ai retention policy, unless a user deletes it sooner | Your organization |
| Local session transcripts (sessions on users' machines) | 6 years by default, or your organization's custom conversation retention period when a finite one is set | Anthropic by default; your organization when it sets a custom period |
| Remote session transcripts (sessions in the cloud) | 6 years, unless a user deletes the session sooner | Anthropic |
| Content hard-deleted through the Compliance API | Not retained; deletion is immediate and permanent | The caller of the DELETE endpoint |
To learn how the rest of the Claude Platform handles retention, see API and data retention.
Decide between export-and-archive and on-demand API retrieval as follows:
- If your legal-hold or audit horizon exceeds 6 years for activity metadata or session transcripts, export Activity Feed pages and session transcripts to your own archive as you ingest them.
- If your content-retention policy is shorter than your eDiscovery horizon, export chat and file content before the retention window expires; the Compliance API cannot return content that retention has already removed. The same applies to local session transcripts, which follow your organization's custom conversation retention period when a finite one is set, even when that period is shorter than 6 years. The local session endpoints stop returning messages older than your organization's current period as soon as the setting changes, and lengthening the period later does not restore transcripts that have already expired, so export any transcript you must keep beyond it.
- If you must retain chat content or remote session transcripts after users delete them in claude.ai (for example, under a legal hold), export chat, file, artifact, and remote session content to your own archive as you ingest it; the Compliance API cannot return content that a user has already deleted.
- If a workflow might issue a Compliance API hard-delete (for example, DLP enforcement), retrieve and archive the target content first. There is no recovery window after a hard-delete.
In every other case, rely on direct API retrieval and avoid maintaining a parallel copy.
Delivery guarantees and completeness
Treat the Activity Feed as at-least-once: a correctly paginated traversal returns every activity at least once, but a retry after a partial failure can re-deliver activities you already stored. Deduplicate on the activity id field.
The list endpoints do not return a total_count field or a checksum. To attest that an export run is complete, log:
- The starting cursor and the terminal
last_id. - The number of records exported.
- The run timestamp and the
request-idof the final page.
Activity volume is not a completeness check. The claude_*_viewed activity types, such as claude_chat_viewed, follow each app's loading pattern (see Understand the Activity object). A period with chat messages but no claude_chat_viewed activities does not on its own indicate missing data. Rely on the traversal and the overlap or reconciliation pass described in Window polling instead.
The content endpoints (chats, files, projects, project attachments, and local and remote session transcripts) serve Claude Enterprise data only. The Activity Feed surfaces administrative and resource events organization-wide. The Compliance API does not include:
- Prompt text or model responses from Claude Console, or from Claude API workloads authenticated with an API key.
- On-device activity in local sessions that is never sent to Anthropic, such as local files that Claude did not read.
- Claude Code usage authenticated with a Claude Console API key, run through a third-party cloud platform (Amazon Bedrock, Google Cloud, or Microsoft Foundry), or run in Claude Code on the web.
- Local sessions from organizations with HIPAA readiness enabled, and local sessions for which zero data retention is in effect.
- Thinking blocks, and images or other binary content, inside session transcripts (transcripts carry user prompts, assistant responses, and tool activity only; local session transcripts show a placeholder
textblock where binary content was omitted). - The original file for a chat attachment that claude.ai stored as extracted text, such as some Word, PowerPoint, and PDF uploads (the file content endpoint returns the extracted text; see Retrieve files and artifacts).
- The system prompt of local sessions (a marker message stands in for it).
- Tool definitions and MCP server configuration in session transcripts (local or remote), and citation metadata on
textblocks in local session transcripts. - Local session transcript content in an organization whose customer-managed encryption key cannot currently be used. Those requests return 503 Service Unavailable, and session metadata is still listed.
- Content removed by your organization's retention policy.
- Content of chats that users delete in claude.ai (the chats are still listed, with
deleted_atpopulated). - Remote sessions that users delete (deleted sessions are no longer listed, and the messages endpoint returns 404 for them).
- Content hard-deleted through the Compliance API.
See the Compliance API FAQ for more on what the Compliance API does and does not capture.
For chain of custody, store the exported records with provenance metadata: source endpoint, query parameters, run timestamp, and a content hash of each record.
Next steps
Filter parameters, pagination, and the Activity object schema.
The chat, file, and project endpoints, including hard delete.
List the sessions your users run in Claude apps and agents, such as Cowork and Claude Code, and retrieve their transcripts.
Was this page helpful?