agent-activity

Report agent activity

Changed on

Prototype (AG-285). Reports activity observed by an agent runtime on a developer's machine - today, Claude Code hook events.

This is telemetry from cooperating machines, not attestation. Opal can only see what a configured, consenting client chooses to send, cannot verify that a fleet is configured at all, and has no way to detect a machine that is reporting nothing. Absence of activity for an agent therefore means "nothing was reported", never "the agent did nothing".

The request body is either a batch envelope ({"events": [...]}), a bare JSON array of events, or a single raw Claude Code hook payload - the last so a hook can POST here directly with no relay in between.

The bearer token establishes the organization and nothing finer. No field in the payload is treated as an identity claim Opal trusts: the actor headers below are used for best-effort attribution and display only. Events that cannot be attributed to an agent are retained and surfaced as unattributed activity rather than dropped.

post/agent-activity

Request

  • Base URL
    https://api.opal.dev/v1
  • Auth
    HTTP bearer

Headers

X-Opal-Actorstring

The identity the reporting client claims this activity belongs to - an email or an Anthropic user id. Unverified: a Claude Code hook payload contains no user identity, so this has to be injected by whoever deploys the hook (via the http handler's headers map and allowedEnvVars interpolation), and whoever controls the machine controls the header. Used only when the API key itself does not identify a developer. When the email belongs to a person in the organization, that person is recorded as who the activity was reported for (always as a claim), and the activity is attributed to the one Claude agent they own, if they own exactly one. The API key's own email is never treated this way: a key belongs to whoever created it, which with a shared key is not who is at the keyboard. Omitting it is valid. When an event's opal_reported object carries other emails, Opal also tries them, in the order configured_email, claude_account_email, git_email, after this header and skipping any that repeat it. The first one that resolves to an agent wins.

X-Opal-Hoststring

The name of the machine that sent this request, as that machine reports it (hostname -s). Unverified: two machines can share a name, and whoever controls the machine controls the header. Stored with each event, so a reader can tell which machine reported it. When an event's own opal_reported.hostname names a different machine, this header wins and the body's value is kept beside it. Omitting it is valid.

Request body

Response

A per-batch accounting of what was accepted, stored, and attributed. An organization that has agent activity turned off gets a 200 with an empty body for every body shape, so a sender does not keep the events and try again.

receivedinteger required

Events parsed out of the request body.

storedinteger required

Rows actually written. Lower than received when a retried delivery was deduplicated on tool_use_id.

attributedinteger required

Events that resolved to an agent in this organization.

unattributedinteger required

Events that resolved to no agent, and are therefore visible only in the unattributed activity list. A persistently high value means the reporting clients are not sending an identity Opal can resolve.

Changes

    • info

      added the new optional header request parameter X-Opal-Host

    • info

      added the new optional request property //

    • info

      added the new optional request property //

    • info

      removed the non-success response with the status

    • info

      added the new optional request property //