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 forwarder that is enabled in the event's environment, 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.

Enablement and delivery configuration are per-environment: a forwarder can deliver production events to one destination, staging events to another, and ignore the rest. See Per-environment enablement and configuration.

Anatomy of a forwarder

Every forwarder is defined by:

FieldDescription
nameDisplay name.
descriptionFree text — what this forwarder is for.
enabledRead-only and always false. Enablement is per-environment — see environments below. You can't set this field; a forwarder delivers in an environment only when that environment's entry is enabled.
environmentsA map of per-environment settings, keyed by environment (production, staging, ...). Each entry has enabled (whether the forwarder delivers in that environment) and an optional configuration override. A forwarder with no entry for an environment is disabled there. See Per-environment enablement and configuration below.
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. Events that don't match are not forwarded and produce no delivery log entry.
forward_smplkit_eventsOptional boolean (default false). When true, the forwarder also receives platform change events — the changes smplkit records about your own resources (flags, configuration, and so on). These events aren't tied to a deployment environment, so each one is delivered through every environment the forwarder is enabled in, using that environment's resolved configuration. The filter and transform apply to them exactly as to your own events. See Forwarding platform change events.

Per-environment enablement and configuration

A forwarder is never enabled wholesale. Enablement lives in the environments map — one entry per environment you want the forwarder to deliver in:

json
{
  "name": "Splunk — prod and staging",
  "forwarder_type": "splunk_hec",
  "configuration": {
    "method": "POST",
    "url": "https://http-inputs-acme.splunkcloud.com/services/collector/event",
    "headers": [{ "name": "Authorization", "value": "Splunk <prod-hec-token>" }],
    "success_status": "2xx"
  },
  "environments": {
    "production": { "enabled": true },
    "staging": {
      "enabled": true,
      "configuration": {
        "method": "POST",
        "url": "https://http-inputs-acme-staging.splunkcloud.com/services/collector/event",
        "headers": [{ "name": "Authorization", "value": "Splunk <staging-hec-token>" }],
        "success_status": "2xx"
      }
    }
  }
}

The rules:

  • environments["<env>"].enabled controls whether the forwarder delivers in that environment. The top-level enabled is read-only and always false — you can't enable a forwarder globally.
  • An environment with no entry is disabled for this forwarder. Events recorded in it are never delivered here and produce no delivery records for this forwarder.
  • environments["<env>"].configuration is an optional per-environment override. Omit it to inherit the base configuration (above, production inherits the base Splunk Cloud URL and token). Provide it to fully replace the base configuration for that environment — as staging does above, pointing at a different stack with a different token. An override fully replaces the base; it is not deep-merged.
  • A per-environment configuration is validated against the same per-vendor template as the base configuration (URL pattern, fixed headers, placeholders).
  • Every environment you reference must exist and be managed for the account.

The filter and transform are forwarder-wide — they apply identically across every environment the forwarder is enabled in. Only enablement and configuration vary per environment.

Forwarding platform change events

Alongside the events you record, smplkit keeps an audit trail of the changes it makes to your own resources — when a flag's value changes, a configuration is updated, an API key is rotated, and so on. These are platform change events. They are recorded automatically and are not tied to a deployment environment.

Set forward_smplkit_events to true to stream them to a forwarder's destination, the same way you stream your own events:

json
{
  "name": "Splunk — all platform changes",
  "forwarder_type": "splunk_hec",
  "configuration": { "method": "POST", "url": "https://http-inputs-acme.splunkcloud.com/services/collector/event", "headers": [{ "name": "Authorization", "value": "Splunk <prod-hec-token>" }], "success_status": "2xx" },
  "forward_smplkit_events": true,
  "environments": {
    "production": { "enabled": true },
    "staging": { "enabled": true }
  }
}

How it behaves:

  • Because a platform change event isn't tied to one environment, each one is delivered through every environment the forwarder is enabled in — once per enabled environment, using that environment's resolved configuration. The forwarder above delivers each platform change event twice: once to the production destination and once to staging. A forwarder enabled in only one environment delivers each platform change event once.
  • Each resulting delivery is recorded in the delivery log under the environment it was delivered through, so you can inspect and retry platform-event deliveries per destination exactly like your own.
  • The filter and transform apply unchanged. A forwarder with forward_smplkit_events: false (the default) never receives platform change events.

In the console, the same events appear under the smplkit events option in the Audit › Events environment selector, and on each resource's History tab.

Migrating forwarders to environment scoping

Breaking change

Forwarder enablement moved from a single top-level enabled flag to the per-environment environments map. If you created forwarders before this change, update your configuration:

  • enabled is now read-only and always false. Setting it has no effect. To enable a forwarder, add an entry to environments with enabled: true for each environment you want to deliver in. A forwarder with an empty environments map delivers nowhere.
  • The filter[enabled] list query parameter is removed. Filter or inspect enablement through each forwarder's environments map instead.
  • Per-environment delivery configuration is now possible. Point different environments at different destinations (or different credentials for the same destination) using the optional per-environment configuration override; omit it to inherit the base configuration.
  • Deliveries are environment-scoped and now carry a read-only environment. The delivery log and bulk retry resolve a single environment per request.

To migrate a previously-enabled forwarder, fetch it, add an environments entry with enabled: true for each environment it should deliver in, and save it back.

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 no delivery is attempted; the do_not_forward flag is recorded on the event itself, so the suppression remains visible on the event record. 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 forwarder that is enabled in the event's environment, applies its filter, applies its transform, and POSTs to the destination using that forwarder's effective configuration for the environment (the per-environment configuration override if set, otherwise the base configuration). Each delivery 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.

The delivery log records delivery attempts only. Events that a forwarder's filter rejects, and events recorded with do_not_forward: true, are not forwarded and produce no delivery record. (Filter rejections are emitted to the service logs, so support can still trace why a given event wasn't delivered.)

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.

Each delivery carries an environment. A delivery record records the environment of the event it delivered (read-only); deliveries are scoped to a single environment. The delivery log for a forwarder is itself environment-scoped the same way every other read is — pass a comma-separated filter[environment] to restrict results to a subset of the environments you can access, or omit it to cover every environment your credential can access.

Querying the delivery log. The delivery log for a forwarder supports filtering by status (SUCCEEDED or FAILED), creation time range, and — to look up all delivery attempts for one specific event — by filter[event]. 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, within a single target environment — name it in the request body's environment field, or omit it and a single-environment credential implies it (a multi-environment credential must name it). Each delivery is re-attempted using the forwarder's effective configuration for that environment. Useful after an outage at the destination.

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