The descriptions of the operations and fields are the contract’s own, in English.
/audit/v1/organizations/{org}/security-eventsList an organization's security events
- Credential
- API key
- Permission
- Any of
audit.read_ownaudit.readsupport-access.read
Pages the organization's security events newest first, keyset-paginated by the opaque cursor.
Read more
Requires audit.read_own (any level) or audit.read. audit.read_own is held by Team Admin, Admin and Root; the built-in User role holds neither, so a plain User reads no security event (403 policy-denied), and a custom role may carry audit.read_own to give its holders their own events. WHICH events: a caller holding audit.read at organization level (the Administrator role) sees every event of the organization; any other caller sees only the events whose actor OR subject is themselves, whatever filters they send. WHICH details: ip and user_agent are decrypted only for a caller holding audit.sensitive.read, or for an event whose ACTOR is the caller (the address and user agent are those of the caller's own request - their own login, their own change); being only the subject of an event (someone acted on you) never reveals the actor's details; otherwise they are null and the event carries redacted: ["ip","user_agent"] (listing the captured fields withheld). WHO: each event names its actor and its subject user with their email and display name, resolved when the page is read (one directory lookup per page) and served under the same rule as ip and user_agent - another user's email and display name only to a caller holding audit.sensitive.read, the caller's own always, null for a user the organization's directory does not know; an event made with an API key also carries actor.key_id. The actor and subject filters accept a ref or an email. WHAT: each event's item also carries a readable label (see SecurityEventItem.label), resolved in the same per-page batch and bounded to the organization. Anonymous (unauthenticated) refusals are recorded under a reserved organization no caller can read. Reads are not sensitive and record nothing. Only a role that holds the permission opens it: the resource grants of earlier versions no longer exist and never open the log. The organization in the path must be the caller's own: another organization, or one that does not exist, is 403 tenant-mismatch, never 404.
curl "https://console.eu.ccsio.ai/audit/v1/organizations/{org}/security-events" \
-H "Authorization: Bearer $CCSIO_API_KEY"Parameters
| Name | In | Description |
|---|---|---|
X-Request-IDoptional | header | string Optional request ULID. A valid ULID (upper or lowercase) is honored; when the header is absent or invalid, the runtime mints the id. The response X-Request-ID header carries the canonical uppercase form. |
orgrequired | path | string The organization whose security events are read (C7 identifier). It must be the caller's own organization (403 tenant-mismatch otherwise). |
typeoptional | query | string Optional. One event type of the catalog (SecurityEventType) or a group wildcard '<group>.*' (for example member.*). Anything else renders 400 invalid-filter. |
actoroptional | query | one of Optional. Narrows to events whose actor is this value: a C0 ref (the actor's ref) or an email address (it contains '@'), matched case-insensitively against the organization's users and resolved to their refs. Read moreEmail filters follow the visibility rule of the emails themselves: a caller without audit.sensitive.read may filter only by their own email, any other address matches no event; an address that matches no user matches no event (an empty page, not an error). A malformed address, or a value that is neither a ref nor an email, renders 400 invalid-filter. |
subjectoptional | query | one of Optional. Narrows to events about this user: a C0 user ref or an email address (it contains '@'), matched case-insensitively against the organization's users and resolved to their refs. Read moreEmail filters follow the visibility rule of the emails themselves: a caller without audit.sensitive.read may filter only by their own email, any other address matches no event; an address that matches no user matches no event (an empty page, not an error). A malformed address, or a value that is neither a ref nor an email, renders 400 invalid-filter. |
outcomeoptional | query | string Optional. success, denied or failure; anything else renders 400 invalid-filter. |
fromoptional | query | string Optional. The inclusive lower bound on occurred_at, an RFC 3339 instant; the range is [from, to) and from-after-to renders 400 invalid-time-range, a malformed instant 400 invalid-request. |
tooptional | query | string Optional. The exclusive upper bound on occurred_at, an RFC 3339 instant; the range is [from, to) and from-after-to renders 400 invalid-time-range, a malformed instant 400 invalid-request. |
cursoroptional | query | string Optional. The opaque next_cursor of a previous page (given at most once; a second occurrence renders 400 invalid-request). A malformed cursor, or one minted for the export, renders 400 invalid-cursor. |
limitoptional | query | integer Optional page size, default 50, 1..500. A non-integer renders 400 invalid-request, a value outside 1..500 renders 400 invalid-filter. |
Responses
- 200
A page of security events and the cursor for the next page.
Schema
SecurityEventListResponseeventsarray of SecurityEventrequiredNewest first by (occurred_at, id).
idstringrequiredThe event id (ULID).
pattern ^[0-7][0-9A-HJKMNP-TV-Z]{25}$
occurred_atstring (date-time)requiredWhen the operation happened; RFC 3339 UTC, millisecond precision.
recorded_atstring (date-time)requiredWhen the event was written; the export's ordering key, RFC 3339 UTC, millisecond precision.
org_idstringrequiredThe organization whose log holds the event.
pattern ^[A-Za-z0-9][A-Za-z0-9._:/-]{0,127}$
event_typestringrequiredThe closed event catalog: who did what in an organization. Event types are stable identifiers; the group before the dot is what a '<group>.*' filter matches. The support group records operator support mode: support.session_started and support.session_ended (an operator opened and closed a time-boxed, read-only support session in the organization; the details carry the reason, the expiry and, at the end, end_reason), support.data_accessed (the operator read a kind of data of the organization; once per session and category, the category in the details: request_logs, usage, security_events, members, teams, roles, api_keys, limits or mcp_installations) and support.access_changed (an administrator allowed or denied support sessions; the details carry mode).
Allowed values:
"member.added""member.removed""member.role_changed""member.invited""member.blocked""member.unblocked""member.password_reset_sent""member.invitation_resent""organization.created""organization.updated""organization.status_changed""organization.deleted""organization.member_transferred_out""organization.member_transferred_in""team.created""team.updated""team.archived""team.unarchived""team.member_added""team.member_removed""team.member_role_changed""api_key.created""api_key.revoked""api_key.deleted""service_account.created""user.provisioned""user.disabled""user.enabled""role.created""role.updated""role.deleted""entitlement.created""entitlement.revoked""limits.updated""limits.deleted""issuer.registered""issuer.removed""deployment.transitioned""deployment.published""model.registered""model_registrar.created""model_registrar.revoked""platform_operator.granted""platform_operator.revoked""memory.stored""memory.promoted""memory.erased""user.login""user.logout""sso.configured""sso.updated""sso.removed""user.provisioned_via_sso""sso.domain_added""sso.domain_verified""sso.domain_removed""mcp.installation.created""mcp.installation.updated""mcp.installation.deleted""mcp.installation.tested""mcp.credential.set""mcp.credential.removed""mcp.user_credential.set""mcp.user_credential.removed""registry.item.created""registry.item.updated""registry.item.deleted""registry.imported""billing.checkout_started""billing.portal_opened""billing.account_set""billing.limits_set""billing.plan_changed""billing.model_price_set""billing.credit_adjusted""access_request.created""access_request.approved""access_request.rejected""access_request.canceled""model_group.created""model_group.updated""model_group.deleted""model_group.model_added""model_group.model_removed""model_group.plan_assigned""model_group.plan_unassigned""model_group.organization_assigned""model_group.organization_unassigned""model_provider.created""model_provider.updated""model_provider.credential_set""model_provider.deleted""model_provider.model_added""model_provider.model_removed""support.session_started""support.data_accessed""support.session_ended""support.access_changed"outcomestringrequiredsuccess: the operation took effect; denied: it was refused (an authn 401 or an authz 403); failure: the operation was attempted and did not succeed (a connection test that failed, also when its request was refused 422 connection-failed). Only mcp.installation.tested records failure.
Allowed values:
"success""denied""failure"actorobjectrequiredWho acted. ref and kind are recorded with the event; email, display_name and key_id are the human-readable identity: email and display_name are resolved from the organization's user directory when the event is read (never stored with the event), key_id is recorded with the event. The actor of a support.* event is a CCSolutions.io operator: their name and email are served, under the same rule as any other user, from what the plane recorded when their session started.
refstringrequiredThe acting principal's ref; 'anonymous' when no principal was verified.
pattern ^[A-Za-z0-9][A-Za-z0-9._:/-]{0,127}$
kindstringrequireduser: a human OIDC session (a human platform operator included: their actions are recorded as the human, with their own user_ref); service_account: an API key; platform: the platform's own authority (a platform administrator key or the bootstrap credential); anonymous: an unauthenticated refusal.
Allowed values:
"user""service_account""platform""anonymous"emailstring or nullrequiredThe actor's email address, resolved at read time. null when the actor is no user the organization knows - one that is or was a member of it - (a deleted user, a principal outside the organization such as the platform's own authority, the bootstrap credential or a human platform operator acting in an organization they are no member of and who never held a support session there (one who did is resolved from the name and email frozen on the session), an anonymous refusal) or when this caller may not see it: another user's email and display_name are served only to a caller holding audit.sensitive.read, the caller's own always.
at most 254 characters
display_namestring or nullrequiredThe actor's display name, resolved at read time; null under the same conditions as email, and when the user has none.
at most 128 characters
key_idstring or nullrequiredThe id of the API key the request was authenticated with (actor.ref is then the key's principal, the key's owner); recorded with the event. null for an interactive (OIDC) session, for the bootstrap credential and for events recorded before keys were captured.
pattern ^[A-Za-z0-9][A-Za-z0-9._:/-]{0,127}$
subject_user_refstring or nullrequiredThe user the event is about (the member removed, the account blocked, the user who logged in); null when it is about no user.
pattern ^[A-Za-z0-9][A-Za-z0-9._:/-]{0,127}$
subject_userSecurityEventSubjectUser or nullrequiredThe user the event is about with the identity resolved at read time; null when it is about no user (subject_user_ref is null as well). subject_user_ref is kept for compatibility and always equals subject_user.ref.
itemobjectrequiredtypestringrequiredThe kind of thing acted on (organization, member, team, role, api_key, entitlement, ...).
pattern ^[a-z][a-z0-9_]{0,63}$
idstringrequiredIts raw identifier - never a digest.
1 to 512 characters
labelstring or nullrequiredA readable name for the item, resolved when the event is read (never stored with the event) in ONE lookup per kind per page, always within the event's own organization: a user, member or service_account item reads the user's email (the display name for a user without one: a service account), under the same visibility rule as the actor's and the subject's email - another user's only to a caller holding audit.sensitive.read, the caller's own always; an api_key item reads the key's name; a team item the team's name; a role item the role's name (the built-in roles too, custom ones from the organization's roles); an organization item the organization's display name (only when it is the event's own organization). Every other item type, and an item the organization does not know (deleted, never existed, outside the organization) or this caller may not see, reads null. id stays the authoritative raw identifier.
at most 254 characters
ipstring or nullrequiredThe client IP address (resolved through the trusted proxies), decrypted per the visibility rule; null when not captured or withheld.
user_agentstring or nullrequiredThe client user agent, decrypted per the visibility rule; null when not captured or withheld.
at most 512 characters
request_idstringrequiredThe platform request id of the HTTP request that produced the event.
pattern ^[A-Za-z0-9][A-Za-z0-9._:/-]{0,127}$
session_refstring or nullrequiredThe login session correlation (login and logout events).
pattern ^[A-Za-z0-9._:-]{1,128}$
counterpart_org_idstring or nullrequiredThe other organization of a member transfer.
pattern ^[A-Za-z0-9][A-Za-z0-9._:/-]{0,127}$
correlation_idstring or nullrequiredShared by the events one operation produced (the outgoing and incoming halves of a member transfer).
pattern ^[A-Za-z0-9][A-Za-z0-9._:/-]{0,127}$
detailsobjectrequiredSmall non-sensitive context (the role granted, the status moved to, the team of a team entitlement).
redactedarray of stringoptionalThe captured client details withheld from this caller. Absent when nothing was withheld.
Allowed values:
"ip""user_agent"unreadablearray of stringoptionalClient details the caller may see but that could not be decrypted (a key retired without re-keying); logged server-side. Absent in normal operation.
Allowed values:
"ip""user_agent"
next_cursorstring or nullrequiredPass as the cursor query parameter for the next page; null on the last page.
- 400
Client refusal unrelated to authentication, including malformed input, a denied or failed request.
- 401
The request carries no credential, more than one, or one the platform does not accept, and is refused before the operation runs; `error.code` says why. Supply exactly one credential. Codes: invalid-request (no credential, or both Authorization:
Read more
Bearer and x-api-key), api-key-invalid (a value that is not a platform API key, or a key of a kind the platform does not accept), api-key-unknown, api-key-revoked, api-key-expired, bootstrap-disabled (the one-shot installation credential, once a key exists), token-malformed (a bearer token that is not a JWT), token-algorithm-unsupported, token-signature-invalid, token-claim-invalid, token-issuer-mismatch, token-audience-mismatch, token-expired, token-not-yet-valid, issuer-untrusted, issuer-residency-mismatch (the issuer belongs to another plane), issuer-resolution-unknown, jwks-unavailable, jwks-key-not-found, staff-token-inactive (a platform operator's token), staff-not-operator, staff-directory-unavailable, identity-not-provisioned (a verified identity with no account on this plane), organization-not-found (a verified identity or key whose organization this plane does not know), internal-error (a fault while the credential is checked is refused too, fail closed), context-cancelled
- 402
The organization has to pay first: its standing is payment_required.
Read more
The request boundary applies this payment gate to every route except an organization's billing routes, the caller's own context, organization list, onboarding, session report and personal keys, the public routes, and the routes of platform operators, who administer an organization that has to pay. The operations the gate covers declare this response. On /v1 the refusal is rendered in the OpenAI error format. Code: payment-required
- 403
The credential is valid but may not do this here; the request is refused by the request boundary before the handler runs, and `error.code` says which refusal. An operation may add codes of its own.
Read more
Codes: policy-denied (no role of the caller holds the permission the operation needs, or the credential class may not call it: a personal API key outside inference and MCP, a route that needs a signed-in person, the one-shot installation credential outside its three steps), tenant-mismatch (the organization a path, query, body or X-Tenant header names is not the caller's own), organization-provisioning, organization-suspended, organization-disabled, organization-deleted (the state of the caller's organization), user-disabled, residency-mismatch (the organization lives on another plane), organization-selection-required (a caller with more than one organization, or a platform operator, must name one in X-Organization-Id), organization-not-selectable, no-active-organization-membership, team-not-selectable (the X-Organization-Id or X-Team-Id the caller sent does not name an organization or team it may act in), identity-managed-by-provider (a person who is not a platform operator changes an organization's status, which belongs to the identity provider), support-session-required (a platform operator reads an organization's data without an open support session)
- 405
The path is claimed but the request method is not allowed. Go ServeMux returns its standard plain-text response and lists permitted methods in Allow.
- 500
Platform failure, rendered without internal cause details.
- 503
A dependency the operation needs is not configured on this plane or could not be reached, and nothing was changed. `error.code` names it; an operation lists the ones it can answer besides the boundary's. Retry later. Codes: support-audit-unavailable (the request boundary: an operator's read under an open support session could not be recorded, so nothing was served)
{
"events": [
{
"id": "01K7YQ6G3H5J7K9N1Q3S5V7X9Z",
"occurred_at": "2026-10-14T09:50:12.431Z",
"recorded_at": "2026-10-14T09:50:12.447Z",
"org_id": "acme",
"event_type": "member.role_changed",
"outcome": "success",
"actor": {
"ref": "user/onboard/01K7YN1B3C5D7F9H2K4M6P8R0T",
"kind": "user",
"email": "[email protected]",
"display_name": "Ana García",
"key_id": null
},
"subject_user_ref": "user/onboard/01K7YN2C4D6F8H1K3M5P7R9T0V",
"subject_user": {
"ref": "user/onboard/01K7YN2C4D6F8H1K3M5P7R9T0V",
"email": "[email protected]",
"display_name": "Ben Okafor"
},
"item": {
"type": "member",
"id": "user/onboard/01K7YN2C4D6F8H1K3M5P7R9T0V",
"label": "[email protected]"
},
"ip": "203.0.113.24",
"user_agent": "Mozilla/5.0 (X11; Linux x86_64)",
"request_id": "req-01k7yq6f2d4g6j8m0p2r4t6v8x",
"session_ref": "sess-7f3a",
"counterpart_org_id": null,
"correlation_id": null,
"details": {
"role": "organization_admin"
}
},
{
"id": "01K7YQ4D8M2P6S9V3X5Z7B1C4F",
"occurred_at": "2026-10-14T09:30:00.118Z",
"recorded_at": "2026-10-14T09:30:00.131Z",
"org_id": "acme",
"event_type": "api_key.created",
"outcome": "success",
"actor": {
"ref": "user/onboard/01K7YN1B3C5D7F9H2K4M6P8R0T",
"kind": "user",
"email": "[email protected]",
"display_name": "Ana García",
"key_id": null
},
"subject_user_ref": "svc/01K7YP2C9W4X5E8H3J6M0N1Q2R",
"subject_user": {
"ref": "svc/01K7YP2C9W4X5E8H3J6M0N1Q2R",
"email": null,
"display_name": "billing-app"
},
"item": {
"type": "api_key",
"id": "01K7YQ4D8M2P6S9V3X5Z7B1C4F",
"label": "billing-app production"
},
"ip": "203.0.113.24",
"user_agent": "Mozilla/5.0 (X11; Linux x86_64)",
"request_id": "req-01k7yq4c7l3n5q7s9u1w3y5a7c",
"session_ref": "sess-7f3a",
"counterpart_org_id": null,
"correlation_id": null,
"details": {}
}
],
"next_cursor": null
}/audit/v1/organizations/{org}/security-events/exportPull the organization's security events as NDJSON (SIEM feed)
- Credential
- API key
- Permission
audit.read
The SIEM pull feed: application/x-ndjson, one SecurityEvent object per line, in ASCENDING (recorded_at, event id) order.
Read more
Requires audit.read at organization level (the Administrator role; a service-account API key holding a custom role with audit.read - plus audit.sensitive.read for the client details - is the intended consumer). The same ip/user_agent redaction as the list applies. Every line carries the same actor.email / actor.display_name / actor.key_id and subject_user fields, and the same item.label, as the list, resolved when the page is pulled under the same audit.sensitive.read rule (a consumer without it receives null for every other user's email). Resume with the X-Next-Cursor response header: pass it as the cursor of the next call to receive exactly the events recorded after the last one served - no gap, no repeat, stable while new events arrive. An idle poll returns an empty body and the same cursor back. Events younger than a short settle window (2 seconds) are withheld so a pull can never skip a row that was still committing. Not sensitive; a caller without audit.read is 403 policy-denied (audit.read_own alone is not enough). The organization in the path must be the caller's own: another organization, or one that does not exist, is 403 tenant-mismatch, never 404.
curl "https://console.eu.ccsio.ai/audit/v1/organizations/{org}/security-events/export" \
-H "Authorization: Bearer $CCSIO_API_KEY"Parameters
| Name | In | Description |
|---|---|---|
X-Request-IDoptional | header | string Optional request ULID. A valid ULID (upper or lowercase) is honored; when the header is absent or invalid, the runtime mints the id. The response X-Request-ID header carries the canonical uppercase form. |
orgrequired | path | string The organization whose security events are read (C7 identifier). It must be the caller's own organization (403 tenant-mismatch otherwise). |
cursoroptional | query | string Optional. The X-Next-Cursor of the previous page: resume strictly after the last event already received. Absent = from the oldest event. A malformed cursor, or one minted for the list, renders 400 invalid-cursor. |
limitoptional | query | integer Optional page size, default 500, 1..5000. A non-integer renders 400 invalid-request, a value outside 1..5000 renders 400 invalid-filter. |
Responses
- 200
Newline-delimited JSON events, oldest first; X-Next-Cursor carries the resume position.
- 400
Client refusal unrelated to authentication, including malformed input, a denied or failed request.
- 401
The request carries no credential, more than one, or one the platform does not accept, and is refused before the operation runs; `error.code` says why. Supply exactly one credential. Codes: invalid-request (no credential, or both Authorization:
Read more
Bearer and x-api-key), api-key-invalid (a value that is not a platform API key, or a key of a kind the platform does not accept), api-key-unknown, api-key-revoked, api-key-expired, bootstrap-disabled (the one-shot installation credential, once a key exists), token-malformed (a bearer token that is not a JWT), token-algorithm-unsupported, token-signature-invalid, token-claim-invalid, token-issuer-mismatch, token-audience-mismatch, token-expired, token-not-yet-valid, issuer-untrusted, issuer-residency-mismatch (the issuer belongs to another plane), issuer-resolution-unknown, jwks-unavailable, jwks-key-not-found, staff-token-inactive (a platform operator's token), staff-not-operator, staff-directory-unavailable, identity-not-provisioned (a verified identity with no account on this plane), organization-not-found (a verified identity or key whose organization this plane does not know), internal-error (a fault while the credential is checked is refused too, fail closed), context-cancelled
- 402
The organization has to pay first: its standing is payment_required.
Read more
The request boundary applies this payment gate to every route except an organization's billing routes, the caller's own context, organization list, onboarding, session report and personal keys, the public routes, and the routes of platform operators, who administer an organization that has to pay. The operations the gate covers declare this response. On /v1 the refusal is rendered in the OpenAI error format. Code: payment-required
- 403
The credential is valid but may not do this here; the request is refused by the request boundary before the handler runs, and `error.code` says which refusal. An operation may add codes of its own.
Read more
Codes: policy-denied (no role of the caller holds the permission the operation needs, or the credential class may not call it: a personal API key outside inference and MCP, a route that needs a signed-in person, the one-shot installation credential outside its three steps), tenant-mismatch (the organization a path, query, body or X-Tenant header names is not the caller's own), organization-provisioning, organization-suspended, organization-disabled, organization-deleted (the state of the caller's organization), user-disabled, residency-mismatch (the organization lives on another plane), organization-selection-required (a caller with more than one organization, or a platform operator, must name one in X-Organization-Id), organization-not-selectable, no-active-organization-membership, team-not-selectable (the X-Organization-Id or X-Team-Id the caller sent does not name an organization or team it may act in), identity-managed-by-provider (a person who is not a platform operator changes an organization's status, which belongs to the identity provider), support-session-required (a platform operator reads an organization's data without an open support session)
- 405
The path is claimed but the request method is not allowed. Go ServeMux returns its standard plain-text response and lists permitted methods in Allow.
- 500
Platform failure, rendered without internal cause details.
- 503
A dependency the operation needs is not configured on this plane or could not be reached, and nothing was changed. `error.code` names it; an operation lists the ones it can answer besides the boundary's. Retry later. Codes: support-audit-unavailable (the request boundary: an operator's read under an open support session could not be recorded, so nothing was served)
Generated from the platform contracts at commit 5f833620.