Skip to content

SIEM streaming

SIEM streaming forwards audit events to your SIEM (Splunk, Datadog, Sumo Logic, others) or any HTTP endpoint of your choosing in real time. Configure one or more forwarders in your account; on every event recorded via the events API, the audit service evaluates each enabled forwarder, optionally transforms the event, and POSTs the result to the destination. Successes and failures are logged per forwarder so you can inspect, debug, and retry.

Anatomy of a forwarder

Every forwarder is defined by:

FieldDescription
nameDisplay name.
descriptionFree text — what this forwarder is for.
enabledBoolean. When false, the forwarder is skipped entirely; nothing is sent and no delivery records are written for it.
http.methodThe HTTP method used to call the destination (POST is the typical case).
http.urlThe destination URL.
http.headersOptional list of {name, value} pairs. Use for HEC tokens, API keys, signatures. Header values are encrypted at rest and returned plaintext on GET so that a GET → mutate → PUT round-trip preserves them without re-entry. Header values surfaced on the delivery log (the audit trail of each delivery attempt) are redacted instead.
http.success_statusThe HTTP status (or pattern, e.g. 2xx) that indicates a successful delivery. Anything else is recorded as a failure.
http.tls_verifyBoolean. When true (the default), the destination's TLS certificate is verified against trusted CAs. Flip to false only for short-lived testing against a destination that serves an untrusted certificate (e.g. a Splunk Cloud trial stack on :8088). For long-lived self-signed setups, pin the issuing CA via ca_cert instead of disabling verification entirely.
http.ca_certOptional PEM-encoded certificate (or bundle) trusted in addition to the system CA store. Must contain one or more -----BEGIN CERTIFICATE----- blocks. Use this to pin a private or self-signed CA without disabling verification globally. Ignored when tls_verify is false.
transformOptional JSONata template. Maps the source event JSON into whatever shape the destination expects. If absent, the raw event JSON is sent.
filterOptional JSON Logic expression. If present, the forwarder only fires for events where the expression evaluates true. Other events are recorded as FILTERED_OUT deliveries on this forwarder.

Plan and entitlement

SIEM streaming requires the audit.siem_streaming entitlement, which ships on the Audit Enterprise plan. The entitlement gate runs at the fan-out (POST /api/v1/events), not at the management surface:

  • Configuration is plan-agnostic. Forwarders can be created, listed, updated, retried, and deleted on any plan. A customer who downgrades from Enterprise keeps their forwarder configuration intact.
  • Fan-out is plan-gated. When the account lacks audit.siem_streaming, the audit service short-circuits before any forwarder lookup — no HTTP delivery is attempted and no delivery records are written. The console surfaces the inactive state with a banner on the Forwarders page.
  • Re-upgrading resumes delivery. Subsequent events fan out normally with no further customer action; the previously-configured forwarders pick back up.

Filtering with JSON Logic

The filter field is a JSON Logic expression evaluated against the source event. It controls which events a forwarder picks up.

Forward only user.deleted events:

json
{ "==": [{ "var": "event_type" }, "user.deleted"] }

Forward only events where the actor is an API key:

json
{ "==": [{ "var": "actor_type" }, "API_KEY"] }

Forward only refunds over $1,000:

json
{
  "and": [
    { "==": [{ "var": "event_type" }, "payment.refunded"] },
    { ">": [{ "var": "data.snapshot.amount_cents" }, 100000] }
  ]
}

JSON Logic supports the standard operators (==, !=, >, <, in, and, or, not, plus several more); see jsonlogic.com for the full reference.

Per-event opt-out: do_not_forward

Suppress forwarding for a specific event by setting do_not_forward: true on the event when you record it. Every enabled forwarder skips the event and records a SKIPPED_DO_NOT_FORWARD delivery, so the skip is itself auditable. Useful when you record an event that you specifically don't want to leave the platform — typically because data contains content you've contractually agreed not to share with the SIEM vendor.

Transforming with JSONata

The transform field is a JSONata template that maps the source event into whatever shape the destination expects. The most common use case is wrapping smplkit's event into a vendor-specific envelope.

Example: Splunk HEC

Splunk HEC expects events wrapped in an envelope with time, host, source, sourcetype, and event fields.

Source event (as recorded by smplkit):

json
{
  "id": "01913e9c-a2c8-7b10-a8b6-cab12ee00001",
  "event_type": "order.placed",
  "resource_type": "order",
  "resource_id": "o-9876",
  "actor_type": "USER",
  "actor_id": "8a1c5e3a-2b9f-4d12-9e76-1d3c8b7a4e5f",
  "actor_label": "alice@example.com",
  "occurred_at": "2026-05-08T14:22:18Z",
  "data": {
    "snapshot": {
      "customer_id": "c-1234",
      "total_cents": 8990
    },
    "request_id": "req-d4f8a1c0"
  }
}

JSONata template:

{
  "time": $millis($parse(occurred_at)) / 1000,
  "host": "smplkit-audit",
  "source": "smplkit",
  "sourcetype": "_json",
  "event": {
    "event_type": event_type,
    "resource": resource_type & ":" & resource_id,
    "actor": actor_label,
    "details": data.snapshot
  }
}

Result POSTed to Splunk HEC:

json
{
  "time": 1746714138,
  "host": "smplkit-audit",
  "source": "smplkit",
  "sourcetype": "_json",
  "event": {
    "event_type": "order.placed",
    "resource": "order:o-9876",
    "actor": "alice@example.com",
    "details": {
      "customer_id": "c-1234",
      "total_cents": 8990
    }
  }
}

For the full JSONata language reference, see jsonata.org.

Vendor reference

The audit service treats every forwarder type as generic HTTP — the configuration shape (http.method, http.url, http.headers, http.success_status, transform) is identical across types. The forwarder_type value is a label that the console uses for icons, banners, and inline help; the audit service does not derive the URL, headers, or body shape from it. You supply each piece yourself based on what the destination requires.

The sub-sections below capture what each supported destination requires so you can fill in those fields correctly.

Datadog Logs

Send audit events as logs to Datadog's HTTP intake.

SettingValue
forwarder_typedatadog
HTTP methodPOST
URLhttps://http-intake.logs.{site}/api/v2/logs — replace {site} with the host for your Datadog site (US1: datadoghq.com, US3: us3.datadoghq.com, US5: us5.datadoghq.com, EU: datadoghq.eu, AP1: ap1.datadoghq.com, AP2: ap2.datadoghq.com, GOV: ddog-gov.com)
Required headersDD-API-KEY: <your-api-key>
Content-Type: application/json
BodyA JSON array of one or more log objects

Body fields (per log object):

FieldTypeRequiredNotes
messagestringyesThe log body — indexed for full-text search
ddsourcestringnoIntegration name (e.g. smplkit-audit); enables automatic parser selection
ddtagsstringnoComma-separated tags, e.g. env:prod,service:audit
hostnamestringnoOriginating host name
servicestringnoService name (matches APM if you use it)
(custom)anynoAny additional key-value pairs are indexed as facets

Example body:

json
[
  {
    "ddsource": "smplkit-audit",
    "ddtags": "env:prod,service:audit",
    "service": "smplkit",
    "message": "order.placed by alice@example.com on order:o-9876",
    "event_type": "order.placed",
    "actor": "alice@example.com",
    "occurred_at": "2026-05-08T14:22:18Z"
  }
]

JSONata transform to produce that body (wrapping the smplkit event in Datadog's envelope):

[{
  "ddsource": "smplkit-audit",
  "ddtags": "env:prod,service:audit",
  "service": "smplkit",
  "message": event_type & " by " & actor_label & " on " & resource_type & ":" & resource_id,
  "event_type": event_type,
  "actor": actor_label,
  "occurred_at": occurred_at,
  "data": data
}]

Learn more about Datadog's Logs Send API.

Elastic (Elasticsearch)

Send audit events to an Elasticsearch index via the Bulk API (recommended) or the single-document _doc endpoint.

SettingValue
forwarder_typeelastic
HTTP methodPOST
URLBulk: https://<your-es-host>/_bulk  ·  single doc: https://<your-es-host>/<index>/_doc
Required headersAuthorization: ApiKey <base64(id:api_key)>
Content-Type: application/x-ndjson (Bulk) or application/json (single doc)
BodyBulk: NDJSON — alternating action and source lines, terminated by a newline.
Single doc: a JSON object representing the document.

Bulk body fields:

LineTypeRequiredNotes
Action lineobjectyesNames the operation and target index, e.g. {"index": {"_index": "smpl-audit"}}. Supported operations include index, create, update, delete.
Source lineobjectyes (for index/create/update)The document body. Omit for delete.

The file must end with \n. Do not pretty-print — newlines are significant.

Example body (bulk, two events):

{"index":{"_index":"smpl-audit"}}
{"event_type":"order.placed","actor":"alice@example.com","resource":"order:o-9876","occurred_at":"2026-05-08T14:22:18Z","data":{"total_cents":8990}}
{"index":{"_index":"smpl-audit"}}
{"event_type":"order.refunded","actor":"bob@example.com","resource":"order:o-9876","occurred_at":"2026-05-08T15:01:42Z","data":{"refund_cents":8990}}

For a single-document POST to …/smpl-audit/_doc, the body is just one of those source lines as a regular JSON object.

Learn more about Elasticsearch's Bulk API.

Honeycomb

Send audit events as Honeycomb events. Use the batch endpoint to send several events per request.

SettingValue
forwarder_typehoneycomb
HTTP methodPOST
URLBatch: https://api.honeycomb.io/1/batch/<dataset>  ·  single event: https://api.honeycomb.io/1/events/<dataset>. EU tenants use api.eu1.honeycomb.io. <dataset> is your destination dataset slug.
Required headersX-Honeycomb-Team: <ingest-api-key>
Content-Type: application/json
Honeycomb Classic accounts only: also X-Honeycomb-Dataset: <dataset>
BodyBatch: a JSON array of event envelopes. Single event: just the data object.

Body fields (batch envelope, per array element):

FieldTypeRequiredNotes
dataobjectyesThe event payload — any key-value pairs you want to index
timestringnoRFC3339 timestamp; defaults to receive time at the server
samplerateintegernoSampling denominator (2 = 1-in-2); defaults to 1 (no sampling)

Each event must be under 1 MB uncompressed. Field values capped at 64 KB; up to 2,000 fields per event.

Example body (batch):

json
[
  {
    "time": "2026-05-08T14:22:18Z",
    "data": {
      "event_type": "order.placed",
      "resource": "order:o-9876",
      "actor": "alice@example.com",
      "total_cents": 8990
    }
  }
]

Learn more about Honeycomb's Events API.

New Relic Logs

Send audit events to New Relic's Log API.

SettingValue
forwarder_typenew_relic
HTTP methodPOST
URLUS: https://log-api.newrelic.com/log/v1  ·  EU: https://log-api.eu.newrelic.com/log/v1  ·  FedRAMP: https://gov-log-api.newrelic.com/log/v1
Required headersApi-Key: <license-or-ingest-key>
Content-Type: application/json
BodyEither the simplified shape (a single JSON object) or the detailed shape (array of {common, logs} envelopes)

Simplified body fields (a single object):

FieldTypeRequiredNotes
messagestringno, but recommendedPrimary searchable log field
timestampinteger or stringnoUnix epoch ms, Unix epoch s, or ISO8601; defaults to receive time
logtypestringnoIdentifies the log shape for parsing rules
(custom)anynoAny other fields are indexed as attributes

Example body (simplified):

json
{
  "timestamp": "2026-05-08T14:22:18Z",
  "logtype": "smplkit-audit",
  "message": "order.placed by alice@example.com on order:o-9876",
  "event_type": "order.placed",
  "actor": "alice@example.com",
  "resource": "order:o-9876"
}

Detailed body (array of envelopes; useful for batching with shared attributes):

json
[
  {
    "common": { "attributes": { "service": "smplkit", "logtype": "smplkit-audit" } },
    "logs": [
      { "timestamp": "2026-05-08T14:22:18Z", "message": "order.placed", "event_type": "order.placed" },
      { "timestamp": "2026-05-08T15:01:42Z", "message": "order.refunded", "event_type": "order.refunded" }
    ]
  }
]

Learn more about New Relic's Log API.

Splunk HEC

Send audit events to Splunk's HTTP Event Collector. The full transform example for HEC is shown in Transforming with JSONata above; the reference below covers everything else.

SettingValue
forwarder_typesplunk_hec
HTTP methodPOST
URLhttps://<your-splunk-host>:8088/services/collector/event (JSON events) or …/services/collector/raw (plain text). For Splunk Cloud, the host is your stack's HEC URL, typically https://http-inputs-<stack>.splunkcloud.com/services/collector/event.
Required headersAuthorization: Splunk <HEC-token>
Content-Type: application/json
BodyOne event envelope, or several envelopes concatenated (no array — Splunk reads them one after the other)

Body fields (per envelope):

FieldTypeRequiredNotes
eventanyyesThe event payload — a string or object
timenumbernoUnix epoch seconds (fractional ok); defaults to receive time
hoststringnoOriginating host
sourcestringnoFree-form source label
sourcetypestringnoSplunk source type, e.g. _json
indexstringnoTarget index (HEC token must allow it)
fieldsobjectnoIndexed metadata (key-value pairs)

Example body:

json
{
  "time": 1746714138,
  "host": "smplkit-audit",
  "source": "smplkit",
  "sourcetype": "_json",
  "event": {
    "event_type": "order.placed",
    "resource": "order:o-9876",
    "actor": "alice@example.com",
    "data": { "total_cents": 8990 }
  }
}

Learn more about Splunk's HTTP Event Collector.

Sumo Logic

Send audit events to a Sumo Logic HTTP Logs and Metrics Source. The URL is generated by Sumo when you create the source.

SettingValue
forwarder_typesumo_logic
HTTP methodPOST
URLWhatever URL Sumo Logic mints for your HTTP Source — typically https://endpoint{N}.collection.sumologic.com/receiver/v1/http/<source-id>. Sumo offers two delivery modes per source: Presigned URL (the auth token is encoded in the URL — no header needed) and Auth Header (the URL is clean and the token rides in an Authorization header — exact format shown in Sumo's UI when you pick this mode).
Required headersPresigned mode: none.
Auth-Header mode: Authorization: <token-as-shown-by-Sumo>.
Either mode: Content-Type: application/json for JSON payloads.
BodyAny JSON. Sumo Logic ingests it as-is; the parser configuration on the source determines how fields are extracted. Batch multiple events by concatenating JSON objects, one per line.

Required body fields: none — Sumo accepts whatever JSON you send. For better searchability inside Sumo, include a timestamp and a stable identifier you can group on. Aim for payloads between 100 KB and 1 MB before compression.

Example body (single event):

json
{
  "occurred_at": "2026-05-08T14:22:18Z",
  "event_type": "order.placed",
  "actor": "alice@example.com",
  "resource": "order:o-9876",
  "data": { "total_cents": 8990 }
}

Learn more about Sumo Logic's HTTP Source.

Custom HTTP destinations

Use forwarder_type: http when your destination isn't one of the named integrations above — your own ingest API, a webhook receiver, a generic SIEM that isn't on the list, etc.

SettingValue
forwarder_typehttp
HTTP methodWhatever your destination expects (POST, PUT, PATCH, GET, DELETE)
URLAny http:// or https:// URL
Required headersWhatever your destination requires — set them in http.headers
BodyWhatever your destination expects — shape it with transform (omit transform to send the raw smplkit event JSON unchanged)

There are no per-vendor constraints in this mode. The audit service enforces only the generic rules: URL scheme must be http/https, hostname must be present, SSRF guard blocks private/internal addresses, request size and timeout limits apply.

Delivery, failure, and retry

When an event is recorded, the audit service evaluates each enabled forwarder, applies its filter, applies its transform, and POSTs to the destination. Each attempt produces a delivery record with one of:

  • SUCCEEDED — the destination returned the configured success_status.
  • FAILED — the destination returned a different status, the request timed out, the URL was unreachable, or the response was rejected by SSRF guards.
  • FILTERED_OUT — the forwarder's filter evaluated false; the event was not sent.
  • SKIPPED_DO_NOT_FORWARD — the source event had do_not_forward: true.

Accounts without the audit.siem_streaming entitlement do not produce delivery records at all — the fan-out is short-circuited before any forwarder lookup, so no row exists to inspect. The forwarder configuration is preserved and resumes delivering once the account is upgraded to Pro.

Forwarder outcomes never affect the source event. The event POST always succeeds (or fails) on its own merits; forwarder evaluation runs after the event is committed, and a delivery failure does not roll back the event.

Misconfigured forwarders. If the URL is unreachable, credentials are wrong, or the destination returns errors, deliveries are recorded as FAILED with the destination's response (status, body, transport error) attached. The forwarder remains enabled and continues to evaluate against subsequent events; the platform does not auto-disable forwarders on repeated failures.

Querying the delivery log. The delivery log for a forwarder supports filtering by status (SUCCEEDED, FAILED, FILTERED_OUT, SKIPPED_DO_NOT_FORWARD), creation time range, and — to look up all delivery attempts for one specific event — by filter[event_id]. See the API Reference for full parameter details.

Retrying failed deliveries. Two mechanisms:

  • Per-delivery retry. Inspect a single failed delivery, fix the underlying issue (rotate credentials, correct the URL, fix a transform), and retry just that delivery.
  • Bulk retry. Replay every failed delivery for a forwarder in one call. Useful after an outage at the destination.

Each retry creates a new delivery row with an incremented attempt number; prior attempts stay for audit.