Claude Platform Docs
AdminSecurity posture

Send Security posture events to your SIEM

Request the Security posture activities from the Compliance API Activity Feed, read one of them field by field, and keep your SIEM current with a scheduled read that catches late activities.

This page takes you from a Compliance Access Key to Security posture activities arriving as events in your security information and event management (SIEM) system on a schedule. The overview says which organizations have these activities. You need a Compliance Access Key that covers the organization, with the read:compliance_activities or read:org_audit scope.

Request the activities

To read only these activities, filter the feed with activity_types[]. In this request, created_at.gte sets the earliest created_at to read, and order=asc returns the oldest activities first:

cURL
curl --fail-with-body -sS -G \
  "https://api.anthropic.com/v1/compliance/activities" \
  -H "x-api-key: $ANTHROPIC_COMPLIANCE_ACCESS_KEY" \
  -H "anthropic-version: 2026-10-21" \
  --data-urlencode "activity_types[]=org_security_control_attestation_updated" \
  --data-urlencode "created_at.gte=2026-10-20T00:00:00Z" \
  --data-urlencode "order=asc" \
  --data-urlencode "limit=5000"

The API referencePreview with links to the API reference available — press Tab until focus enters the card, Escape to return. describes each parameter.

Read one activity

Each element of the response’s data array is an activity like this one.

{
  "id": "activity_01Hq7ZtYc4Vb2NmKx8RsPd3W",
  "type": "org_security_control_attestation_updated",
  "created_at": "2026-10-20T15:04:00.220114Z",
  "organization_id": "org_01JuY4bkwkH1x4PodBJaWmh2",
  "organization_uuid": "91012d09-e48b-438e-a489-1bebfd8fa6f9",
  "actor": {
    "type": "user_actor",
    "email_address": "jane.doe@example.com",
    "user_id": "user_01WxYzAbCdEfGhJkMnPqRsTu",
    "ip_address": "192.0.2.34",
    "user_agent": "Mozilla/5.0..."
  },
  "control": "directory_sync_enabled",
  "action": "attested",
  "status": "accepted_risk"
}
  • control names the security control, one recommended setting or practice.
  • action says what happened to the control’s attestation, a person’s recorded statement about the control. Here attested means one was recorded for a control that had none.
  • status is the attestation’s value. Here it is Accepted risk (accepted_risk): the control is not in place, and the organization accepts the risk.
  • actor here identifies the person who attested, by user ID and email address.
  • created_at here is when they attested.

The Security posture events reference lists more fields and values.

If the response is empty

An empty data array is valid. The feed holds only changes made since it began recording these activities for the organization. It is empty until an attestation is made or changed.

Check that the organization has these activities. Then have someone attest to a control. After a while, repeat the request.

Run it on a schedule

Any of these activities can become queryable later than the Activity Feed’s usual 1-minute indexing lag, with a created_at that is already in the past. So an integration that only moves forward, from a cursor or an advancing created_at.gte, never sees such an activity. Re-read a fixed look-back window of 4 hours on every run and drop the activities you already stored:

query = activity_types[]=org_security_control_attestation_updated
      & created_at.gte={now - 4 hours}
      & order=asc
      & limit=5000
cursor = null
loop:
  if cursor is null:
    page = GET /v1/compliance/activities?{query}
  else:
    page = GET /v1/compliance/activities?{query}&after_id={cursor}
  for activity in page.data:
    if activity.id is not in stored_ids:
      store(activity)
      add activity.id to stored_ids
  if not page.has_more: break
  cursor = page.last_id
  • Run often: Run the loop as often as your alerts need, for example every 5 minutes.
  • Keep the same query for the whole run: Compute created_at.gte, in UTC and URL-encoded, at the start of each run. Send the same filters and order on every page. With order=asc, passing last_id as after_id returns the next page of newer activities. Paginate results explains after_id and last_id.
  • Deduplicate on id: This step is required, because consecutive runs overlap.
  • Handle failed runs: If a request fails after the retries in Handle Compliance API errors, end the run. The next run reads its whole window again. If runs fail or stop for longer than 4 hours, the next daily re-read reads the activities they missed.

Re-read 15 days once a day

Once a day, run the same loop with a look-back window of 15 days in place of 4 hours. This daily re-read also reads an activity that becomes queryable up to 14 days after its created_at.

  • Record the start time of each daily re-read that completes.
  • Delete a stored id only if you stored it more than 16 days before the latest recorded start.
  • If no start is recorded, set created_at.gte as far back as you need; the feed keeps activities for 6 years.
  • If more than a day has passed since the latest recorded start, run the next daily re-read with created_at.gte set to 15 days before that start.

If you already ingest the full feed

An integration that reads the whole Activity Feed, with no activity_types[] filter that leaves these activities out, already receives them. Check two things:

Check that it works

After someone attests to a control, run the loop until it stores the new activity. Then run it again: it does not store that activity a second time.

Next steps

To alert on the time you stored an activity, not on its created_at, see Detect changes in Security posture. For the Open Cybersecurity Schema Framework (OCSF), see Map Security posture events to OCSF. For current results, see Security posture results reference.

Was this page helpful?