API reference Resources
Activity
Interactions, file events, scans and comments.
13 operations
One subject's thread
Query parameters
artifact or issue.
Response200
The thread, oldest first, tombstones included
Oldest first, tombstones included. A discussion is read in the order it happened, and a withdrawn comment keeps its row and its position — its absence would leave the replies under it reading as remarks about nothing.
The author's display name.
Errors
- 400
Missing or unknown subject_kind
- 403
Caller lacks the grant that reads this subject
- 404
No such subject in this workspace
Add a comment, or a reply
Recorded in the audit log by its ids, never its text.
Request bodyPostCommentRequest
The comment this replies to. Must itself be top-level, and must be on the same subject.
Response200
The stored comment
The author's display name.
null exactly when withdrawn_at is set, and the schema says the same thing as a CHECK in both directions: a tombstone cannot carry text and a live comment cannot be blank.
null for a top-level comment; otherwise the comment this replies to. Never two levels deep.
Errors
- 400
Empty or over-long body, an unknown subject kind, or a reply to a reply
- 403
Caller lacks the grant that reads this subject
- 404
No such subject in this workspace
Withdraw your own comment
Returns the tombstone, not 204: the drawer replaces the row in place rather than removing it, and giving it the row back means it does not have to re-fetch the thread to find out what happened.
Audited. Taking a statement back out of a record of what was decided is exactly the act somebody will later need to account for, and the row it leaves behind no longer says who wrote it — the tombstone keeps author_id, but only the audit log says when and by whom it was withdrawn.
Path parameters
Response200
The tombstone
The author's display name.
null exactly when withdrawn_at is set, and the schema says the same thing as a CHECK in both directions: a tombstone cannot carry text and a live comment cannot be blank.
null for a top-level comment; otherwise the comment this replies to. Never two levels deep.
Errors
- 403
The comment belongs to somebody else
- 404
No such comment in this workspace
File events — fleet-wide, or one artifact's timeline
One route for both, because they are one question asked at two scopes. A separate timeline endpoint would be a second place to add an event kind, and the day somebody added one to only the first is the day the drawer started lying by omission.
Query parameters
The filter expression. Fields: kind, machine, artifact, category, at.
The drawer's timeline is this and not a second parameter: all;artifact~in~<id>. An artifact_id shortcut beside f would be a second way to say one thing, and the two would answer differently the first time one of them learned about a new event kind.
Free text over the artifact's name and location.
Response200
Everything the filter matched, not just this page.
Pass back as cursor for the next page. null means this was the last.
One file event
One file event, by id. 404 for anything this workspace does not hold: an unknown id, another workspace's event, or a value that is not an id at all.
Path parameters
Response200
The scan that produced this event, when one is recorded. null for events recorded before scans were tracked, and on events the server wrote itself because a scanned location stopped being reported: no scan produced those.
Errors
- 404
No such event in this workspace
The Interactions log — intercepted prompts, newest first
Query parameters
The filter expression, e.g. all;service~in~anthropic;outcome~in~blocked.
The first segment is the join (all or any); each later segment is field~op~values, with op one of in, nin, before, after and values separated by commas. A malformed segment is ignored; a field this list does not have is a 400 naming it.
Fields: service, host, client_kind, client_name, tactic, outcome, prompt, machine, user, account, session, correlation, at. user is the OS account signed in to the machine; account is the account signed in to the AI service. all;session~in~<id> lists one session. correlation is the id a hook row shares with the network row it caused, so all;correlation~in~<id> returns both halves of one interaction.
prompt takes found (a prompt was read from the request), none (the request was read and held no prompt) or unread (the request was never read for a prompt). all;prompt~in~found is the log of what people actually typed.
Free text over the service, host, endpoint and declared tool name. ANDs with the expression whichever way its conditions are joined.
It does not reach the prompt. Searching bodies would let a caller confirm a string is in somebody's prompt without rendering it, which is the one thing a content grant exists to govern.
Where the previous page stopped. Opaque; an unreadable one starts over.
Rows wanted. Clamped to 200.
Response200
How many entries attachments holds.
The files that rode this interaction, containers before members.
What happened to this row's prompt and response text, as one word: withheld, dropped, pending, captured or not_captured.
capturednot_captureddroppedwithheldpending`true` only when content exists on this row and this caller may not see it.
Without it, a caller reads null previews and cannot tell never captured from withheld from you, and those are different facts about different things: the first is a statement about the machine's capture tactic — nothing exists, nobody is being kept from anything — and the second is a statement about the reader. Rendering the second as the first tells an operator there is no content here when there is, which is the kind of quiet inaccuracy that surfaces during an incident review.
Per row, not per response. One page legitimately mixes both: a metadata-tactic interaction beside a content-tactic one genuinely has nothing to withhold, and a response-level flag would mark it as withheld and be wrong about it.
false for a caller holding interaction_content:read, and false for a row that never had content to begin with. It is a fact about this row and this reader, so it is computed in the projection beside the redaction it describes — one place, which is the only way the two cannot disagree.
On a network row, the policies a tool's hook already decided for this prompt; the hook row shares this row's correlation_id. Filled on GET /interactions/{id}; empty on the list.
Every interaction the filter matched, across all pages, not only this one.
Pass back as cursor for the next page. null means this was the last.
Errors
- 401
No valid session
- 403
Caller lacks machines:read in this workspace
List interactions grouped by session
A page of sessions, newest activity first. A session is on the page when any of its interactions matches f and q, ordered by its newest matching one; matched says how many matched. Metadata only: read a session's interactions with GET /interactions/by-session/rows.
Query parameters
The filter expression, e.g. all;service~in~anthropic;outcome~in~blocked.
The first segment is the join (all or any); each later segment is field~op~values, with op one of in, nin, before, after and values separated by commas. A malformed segment is ignored; a field this list does not have is a 400 naming it.
Fields: service, host, client_kind, client_name, tactic, outcome, prompt, machine, user, account, session, correlation, at. user is the OS account signed in to the machine; account is the account signed in to the AI service. all;session~in~<id> lists one session. correlation is the id a hook row shares with the network row it caused, so all;correlation~in~<id> returns both halves of one interaction.
prompt takes found (a prompt was read from the request), none (the request was read and held no prompt) or unread (the request was never read for a prompt). all;prompt~in~found is the log of what people actually typed.
Free text over the service, host, endpoint and declared tool name. ANDs with the expression whichever way its conditions are joined.
It does not reach the prompt. Searching bodies would let a caller confirm a string is in somebody's prompt without rendering it, which is the one thing a content grant exists to govern.
Where the previous page stopped. Opaque; an unreadable one starts over.
Rows wanted. Clamped to 200.
Response200
Of count, the ones a decision blocked.
Every interaction in the session, whatever the filter says. On the group with no session, the ones that matched.
observed_at of the session's first and last interaction.
Of those, the ones the filter and the search matched — what opening the session lists. Equal to count when nothing narrows the list.
The tool's declared name, from the session's most recent row that declared one. null when none did, and on the group with no session.
Sessions with at least one matching interaction; the group with no session counts as one.
Pass back as cursor for the next page. null means this was the last.
Errors
- 400
The filter names a field this list does not have
- 401
No valid session
- 403
Caller lacks machines:read in this workspace
One session's interactions
The interactions in one session that the filter matches, oldest first. Rows, permissions and content rules are the same as on GET /interactions.
Query parameters
The session whose interactions to read, verbatim. Exactly one of this and unsessioned.
true reads the interactions that carry no session id — the list's No session group.
The list's filter expression, as on GET /interactions.
The list's free text, as on GET /interactions.
Where the previous page stopped. Opaque; an unreadable one starts over.
Rows wanted. Clamped to 200.
Response200
How many entries attachments holds.
The files that rode this interaction, containers before members.
What happened to this row's prompt and response text, as one word: withheld, dropped, pending, captured or not_captured.
capturednot_captureddroppedwithheldpending`true` only when content exists on this row and this caller may not see it.
Without it, a caller reads null previews and cannot tell never captured from withheld from you, and those are different facts about different things: the first is a statement about the machine's capture tactic — nothing exists, nobody is being kept from anything — and the second is a statement about the reader. Rendering the second as the first tells an operator there is no content here when there is, which is the kind of quiet inaccuracy that surfaces during an incident review.
Per row, not per response. One page legitimately mixes both: a metadata-tactic interaction beside a content-tactic one genuinely has nothing to withhold, and a response-level flag would mark it as withheld and be wrong about it.
false for a caller holding interaction_content:read, and false for a row that never had content to begin with. It is a fact about this row and this reader, so it is computed in the projection beside the redaction it describes — one place, which is the only way the two cannot disagree.
On a network row, the policies a tool's hook already decided for this prompt; the hook row shares this row's correlation_id. Filled on GET /interactions/{id}; empty on the list.
Every interaction the filter matched, across all pages, not only this one.
Pass back as cursor for the next page. null means this was the last.
Errors
- 400
Neither or both of
sessionandunsessioned, or a field this list does not have- 401
No valid session
- 403
Caller lacks machines:read in this workspace
Session summaries
Summaries of the sessions named by ids. Metadata only: counts, a tool name, an OS account and a machine. A session id with no interactions is left out of the answer.
Query parameters
Session ids, one per repeated ids parameter (?ids=a&ids=b).
Repeated rather than comma-separated because a session id is the AI service's own text and may itself hold a comma or leading and trailing spaces; each value is taken verbatim. At most 100, each at most 256 bytes; an id with no interactions is left out of the answer rather than being an error.
Response200
Of those, the ones a decision blocked.
Interactions in the session.
observed_at of the first and last interaction — the endpoint's clock.
The tool's declared name, from the session's most recent row that declared one. null when none did.
Errors
- 400
More than 100 session ids
- 401
No valid session
- 403
Caller lacks machines:read in this workspace
One interaction, for the drawer
The same projection the list returns, so a row and the drawer opened from it can never disagree about what an interaction is.
Path parameters
Response200
How many entries attachments holds.
The files that rode this interaction, containers before members.
Lowercased; "" when the file had none.
parsed | partial | unsupported | failed | encrypted_skipped | declined_decrypt.
What happened to this row's prompt and response text, as one word: withheld, dropped, pending, captured or not_captured.
capturednot_captureddroppedwithheldpending`true` only when content exists on this row and this caller may not see it.
Without it, a caller reads null previews and cannot tell never captured from withheld from you, and those are different facts about different things: the first is a statement about the machine's capture tactic — nothing exists, nobody is being kept from anything — and the second is a statement about the reader. Rendering the second as the first tells an operator there is no content here when there is, which is the kind of quiet inaccuracy that surfaces during an incident review.
Per row, not per response. One page legitimately mixes both: a metadata-tactic interaction beside a content-tactic one genuinely has nothing to withhold, and a response-level flag would mark it as withheld and be wrong about it.
false for a caller holding interaction_content:read, and false for a row that never had content to begin with. It is a fact about this row and this reader, so it is computed in the projection beside the redaction it describes — one place, which is the only way the two cannot disagree.
On a network row, the policies a tool's hook already decided for this prompt; the hook row shares this row's correlation_id. Filled on GET /interactions/{id}; empty on the list.
Everything decided about this interaction, oldest first. Empty means nothing has decided anything about it yet, since a later verdict can still attach to it. Empty does not mean it passed: evaluated_by says whether a policy checked it.
inline, policy or engine. Shown verbatim: "blocked on the laptop from the bundle" and "blocked by the server" are different facts to somebody chasing a false positive.
passed, alerted or blocked. An interaction that alerted and was blocked has two of these, deliberately.
true while a verdict is still owed: the decision is undetermined and its policy has not finished evaluating the interaction yet. false on an undetermined decision means evaluation finished and still could not decide.
The policies that evaluated this interaction, whether or not they matched: Enabled or Monitor policies whose conditions were checked. Non-empty with no objecting decision reads as Allowed; empty with no decision reads as Not evaluated. Empty on older interactions.
The policy's name; null when it has since been deleted — it still evaluated this, and the id is the honest answer.
Which capture produced this row: endpoint_agent or connector. New values may be added; treat an unknown one as a label, not an error.
The person service_account names. The address is matched without regard to letter case. null where the row carries no service account, or where no person has ever been seen with it.
hook rows only: what the tool was doing (prompt, shell, file_read, mcp_call, tool_use, session_start).
Whether this caller may reveal them now: both grants, a key on this deployment, and something held.
How it was captured: network, extension or hook. null on older interactions recorded before capture was tracked.
Declared by the capture surface. null means nothing declared one, which is not the same as other.
The tool's own name, e.g. "Claude Code". Declared by the capture surface, never inferred from a User-Agent. client_kind says what form of client it was; this tells one tool from another. null means no capture surface declared a name, which is common, not that the tool is unknown.
The connection whose sync ingested this interaction. null on every interaction a machine reported.
Joins a hook row to the network row the same call caused.
The path and query. null on some older interactions, which does not mean an empty path.
How many originals are held (Restricted only).
The Redaction in logs mode this interaction's text was stored under. Absent on the list; set on GET /interactions/{id}.
The machine that reported this interaction. null on an interaction a connection ingested (a Copilot turn, say, was reported by no machine); connection_id is set exactly when this is null.
The model this request named, verbatim from the body, e.g. gpt-4o-mini, auto, claude-opus-4-1.
null is common: some services (Gemini, Bedrock) name the model in the URL, and a model is never guessed from a host or an endpoint.
The logged-in OS account. An identifiable person.
What the person did: sent (a message they submitted) or draft (text that left the machine while they were still typing it). prompt_turns says how many requests were folded into a draft.
Metadata, returned without content access. null on some older interactions.
Where in the body the text came from — messages[].content. Provenance, shown beside the text, and what an operator uses to tell a real attribution from a lucky field name. A JSON address, not content.
What the person actually typed, read out of the request. Content: it is returned only to a caller allowed to read interaction content.
Three distinct answers. null with prompt_tier also null means the request was never read for a prompt (a metadata-only exchange, or an older interaction). "" with a prompt_tier of unknown_shape or undecodable means it was read and held no prompt. null with a prompt_tier present means the caller may not see it, which content_withheld also says.
How prompt_text was found, and therefore how much to trust it: known_shape, named_field, unknown_shape or undecodable.
Metadata, returned without content access: that a request carried a prompt is a fact like its endpoint or byte counts.
How many requests folded into this row. 1 for a send; for a draft, the length of the prefix chain — 5 for sel…selamlar.
Without it a collapsed draft looks like one request when it was five, and an operator asking how much this endpoint actually sent would be off by the whole chain.
How many values in the stored text were replaced or masked.
The start of the request body. Content.
null does not mean the prompt was empty. Either content was never captured (the machine's capture tactic collects none, or no body was seen), or the caller is not allowed to read interaction content and it was withheld. content_withheld tells the two apart.
The address the AI service is signed in as. An identifiable person, and null is common: it is only visible under a content tactic.
The AI service's own conversation id. null when unknown; one is never made up.
The person signed in to the machine: the person whose presence matches this row's machine and OS account.
Separate from account_person_id, because the account somebody uses for the AI service may belong to a different person from the one signed in to the machine. null where the row records no OS account, or where no person has been seen with that account on that machine yet.
Errors
- 404
No such interaction in this workspace
Uncovered traffic from a machine
What this machine sent that no capture rule covers. Use it to tell a quiet machine from one whose traffic is going somewhere Oxelone does not record, such as a vendor's renamed endpoint. Metadata only; it needs the same permission as reading machines, not content access. A machine in another workspace, or a malformed id, is a 404; a machine whose traffic is fully covered returns 200 with an empty list.
Path parameters
Response200
Endpoints no rule covers, newest activity first
Newest activity first, one row per path, not grouped. Paths that differ only by a generated segment appear as separate rows.
The path, as the agent reported it, without query or fragment.
How many requests went there. A thousand requests are one row that counts to a thousand.
Errors
- 401
No valid session
- 403
Caller lacks machines:read in this workspace
- 404
No such machine in this workspace
The Scans surface
Query parameters
The filter expression. Fields: kind, outcome, machine, at.
Response200
Whether this run changed the inventory. A run that did is always kept; of the runs that did not, only each machine's most recent few are.
One scan run, for the drawer
Path parameters
Response200
Whether this run changed the inventory. A run that did is always kept; of the runs that did not, only each machine's most recent few are.
open, completed, partial or failed. A run still open long after it started is a scan that never finished, which is itself a problem worth looking into.
Elapsed milliseconds between started_at and finished_at.
Errors
- 404
No such scan in this workspace