OCSF export
One endpoint that renders your agent activity and your findings in the Open Cybersecurity Schema Framework, so a SIEM can read them without a Trefur-specific parser. The body is NDJSON: one OCSF event per line.
Who can pull it, and how
The export requires the admin or owner role, and today a role reaches Trefur only from a signed-in session. Anything else answers 403.
A workspace API key cannot pull this export. Say that plainly before you plan around it. Trefur's /platform/v1 surface accepts API keys and only API keys — a session token is refused there outright — and an API key carries scopes, never a role. A key-authenticated caller therefore arrives as a viewer and is refused. That holds for every class of key, and no scope changes it. A scheduled SIEM collector running on a key is not something you can build yet.
What works is the dashboard, signed in as an admin or owner. Go to Infrastructure → Data Lifecycle → Audit Sinks and open the OCSF export tab: pick the class and an optional range, and it downloads one page. That is the supported route, and for most SIEM onboarding it is enough.
The request behind that button is the one below. If you are scripting against it with a signed-in session — to follow the cursor to the end of a range, which the button does not do — these are the parameters it takes.
GET /api/observe/exports/ocsf
?class=api_activity
&since=2026-09-01T00:00:00Z
&until=2026-09-08T00:00:00Z
&limit=1000
Origin: your dashboard (app.trefur.com)
Authorization: the signed-in session's bearer token
Answers: application/x-ndjson, plus the headers belowParameters
| Parameter | Values | Default |
|---|---|---|
class | api_activity or detection_finding | required — one class per request |
since, until | RFC 3339 timestamps, both inclusive | unbounded |
after | The opaque cursor from the previous page's header | none — start of the range |
limit | 1 – 5000 rows per page | 1000 |
A bad limit, an unparseable timestamp, a missing or unknown class, and a malformed cursor each answer 400 with the reason. The time range is carried inside the cursor, so a continuation stays inside the pull it was issued for: changing since or until on a paged request does nothing. A cursor issued for one class is refused on the other.
Response
application/x-ndjson, one JSON object per line, plus these headers:
| Header | Meaning |
|---|---|
X-Trefur-Ocsf-Version | The OCSF schema version every event declares — 1.9.0. |
X-Trefur-Export-Rows | Rows in this page. |
X-Trefur-Snapshot-Sha256 | SHA-256 of the exact bytes of this page, so you can record what you ingested. |
X-Trefur-Next-Cursor | Present only when another page exists. Its absence is the end of the pull. |
To page to the end of a range, repeat the request with after set to the previous page's X-Trefur-Next-Cursor, and stop when the header is absent. The dashboard's download does not do this for you — it takes the first page and stops.
api_activity (class_uid 6003)
One event per agent step — a tool call, a model call, a retrieval. Each carries the ai_operation profile in metadata.profiles.
- What the step was —
api.operationis the tool name (falling back to the step type);api.service.nameis the host of the outbound endpoint, taken as everything before the first/once the scheme is removed — so an endpoint recorded with a query string but no path keeps that query string;api.response.codeand.errorappear when the step recorded them. - Who ran it —
actor.applicationnames the framework and the observed agent;actor.session.uidis the run;actor.usercarries a user id and type only, never a name or an email address. - The agent —
ai_agentcarries the agent id and name, the run asinstance_uid, the manifest hash asversion, andai_modelwith the model name and the provider inferred from it (unrecognised model names reportUnknownrather than a guess). - Timing and outcome —
time,start_time,end_time,duration;status_idis Success or Failure, and Unknown when the step's status is not one Trefur recognises. - Model calls only —
message_contextis emitted for LLM-style steps and carries token counts only. - Trefur's own identifiers — run, step, provenance and principal fields live under
unmapped.trefur, where they cannot be mistaken for OCSF-defined values.
One thing to set expectations on: severity_id on api_activity is Informational for everything except a failed step, which is Low. It is a record of activity, not a risk signal. Risk lives in the other class.
Sample data is not filtered out of api_activity. If your workspace has seeded demo runs, their steps are exported alongside real ones and will land in your SIEM. detection_finding does exclude sample rows; api_activity does not. If you seeded demo data to try Trefur out, either clear it before you wire the export up, or filter it on the way in — the run identifiers under unmapped.trefur are what to filter on.
detection_finding (class_uid 2004)
One event per finding Trefur raised — including Agent Threat Rules matches.
finding_info.uidis the finding id;finding_info.titleandtypesare its category.finding_info.analyticnames the rule that fired — the rule id and name where the detector recorded one.severity_idmaps Trefur's severities onto OCSF's; a severity Trefur does not recognise becomes Unknown rather than a guess.is_alertis set at High and above.status_idis New, or Resolved once someone resolves it;activity_idfollows.- Demo and sample rows are excluded from this class. See the note under api_activity below.
What the export does not carry
No customer content — but the two classes get there differently, and the difference is worth knowing if you are the one signing off on it.
For api_activity it is by construction. The query behind it selects no content column at all: not inputs, outputs, tool arguments, tool results, messages or request bodies. Those columns exist on the row and are never read, so there is nothing to leak through a mapping mistake.
For detection_finding it is by removal. The query does read the finding's description and its metadata; the description is then dropped and never exported, and the metadata is cut down to a registered list of structural keys, with anything unrecognised discarded. The result is the same, but it rests on the mapping being right rather than on the query never having asked.
- No prompts, completions, tool arguments or tool results.
- No matched text. A finding's excerpt is dropped before export; only the rule identity and the structural fields survive.
- No finding description — the category is the title.
- No machine names. The only host-shaped value is the host part of a step's outbound endpoint, with the caveat above.
- No user names or email addresses — identifiers only.
Every pull is recorded
The first page of a pull writes an entry to your audit ledger naming the class, the range and the limit, and who pulled it. Continuation pages are part of that same pull and are not recorded separately. Because the entry is written in the same transaction that renders the page, a recorded pull is a pull that succeeded — an attempt that failed to render leaves no entry. See the ledger export.
In the dashboard
Infrastructure → Data Lifecycle → Audit Sinks, then the OCSF export tab. Pick the class and an optional range and it downloads a single page. The browser download does not follow the cursor.
Limits
- One class per request. There is no combined feed and no format parameter — NDJSON is the only representation.
- A page is rendered whole before it is sent; it is not streamed.
- If any event in a page fails schema validation the whole page fails with
500. Nothing partial is returned, and no event is padded with invented values to satisfy the schema. - The export reads what Trefur already stored. It does not re-derive, re-score or backfill anything, and rows outside your workspace's retention window are gone.
- There is no push, no webhook and no scheduled delivery. You pull.
- No API key can pull this export. A scheduled, unattended collector is not possible today; a person with an admin or owner session is.
- api_activity does not exclude sample data. detection_finding does.
- The snapshot hash covers one page. It is a record of what you received, not a signature — for signed output see the audit ledger export.