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 below

Parameters

ParameterValuesDefault
classapi_activity or detection_findingrequired — one class per request
since, untilRFC 3339 timestamps, both inclusiveunbounded
afterThe opaque cursor from the previous page's headernone — start of the range
limit1 – 5000 rows per page1000

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:

HeaderMeaning
X-Trefur-Ocsf-VersionThe OCSF schema version every event declares — 1.9.0.
X-Trefur-Export-RowsRows in this page.
X-Trefur-Snapshot-Sha256SHA-256 of the exact bytes of this page, so you can record what you ingested.
X-Trefur-Next-CursorPresent 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 wasapi.operation is the tool name (falling back to the step type); api.service.name is 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.code and .error appear when the step recorded them.
  • Who ran itactor.application names the framework and the observed agent; actor.session.uid is the run; actor.user carries a user id and type only, never a name or an email address.
  • The agentai_agent carries the agent id and name, the run as instance_uid, the manifest hash as version, and ai_model with the model name and the provider inferred from it (unrecognised model names report Unknown rather than a guess).
  • Timing and outcometime, start_time, end_time, duration; status_id is Success or Failure, and Unknown when the step's status is not one Trefur recognises.
  • Model calls onlymessage_context is 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.uid is the finding id; finding_info.title and types are its category.
  • finding_info.analytic names the rule that fired — the rule id and name where the detector recorded one.
  • severity_id maps Trefur's severities onto OCSF's; a severity Trefur does not recognise becomes Unknown rather than a guess. is_alert is set at High and above.
  • status_id is New, or Resolved once someone resolves it; activity_id follows.
  • 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.