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:
| Field | Description |
|---|---|
name | Display name. |
description | Free text — what this forwarder is for. |
enabled | Read-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. |
environments | A 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.method | The HTTP method used to call the destination (POST is the typical case). |
http.url | The destination URL. |
http.headers | Optional 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_status | The HTTP status (or pattern, e.g. 2xx) that indicates a successful delivery. Anything else is recorded as a failure. |
http.tls_verify | Boolean. 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_cert | Optional 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. |
transform | Optional JSONata template. Maps the source event JSON into whatever shape the destination expects. If absent, the raw event JSON is sent. |
filter | Optional 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_events | Optional 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:
{
"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>"].enabledcontrols whether the forwarder delivers in that environment. The top-levelenabledis 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>"].configurationis an optional per-environment override. Omit it to inherit the baseconfiguration(above,productioninherits the base Splunk Cloud URL and token). Provide it to fully replace the base configuration for that environment — asstagingdoes above, pointing at a different stack with a different token. An override fully replaces the base; it is not deep-merged.- A per-environment
configurationis validated against the same per-vendor template as the baseconfiguration(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:
{
"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 theproductiondestination and once tostaging. 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
filterandtransformapply unchanged. A forwarder withforward_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:
enabledis now read-only and always false. Setting it has no effect. To enable a forwarder, add an entry toenvironmentswithenabled: truefor each environment you want to deliver in. A forwarder with an emptyenvironmentsmap delivers nowhere.- The
filter[enabled]list query parameter is removed. Filter or inspect enablement through each forwarder'senvironmentsmap 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
configurationoverride; omit it to inherit the baseconfiguration. - 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:
{ "==": [{ "var": "event_type" }, "user.deleted"] }Forward only events where the actor is an API key:
{ "==": [{ "var": "actor_type" }, "API_KEY"] }Forward only refunds over $1,000:
{
"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):
{
"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:
{
"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.
| Setting | Value |
|---|---|
forwarder_type | datadog |
| HTTP method | POST |
| URL | https://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 headers | DD-API-KEY: <your-api-key>Content-Type: application/json |
| Body | A JSON array of one or more log objects |
Body fields (per log object):
| Field | Type | Required | Notes |
|---|---|---|---|
message | string | yes | The log body — indexed for full-text search |
ddsource | string | no | Integration name (e.g. smplkit-audit); enables automatic parser selection |
ddtags | string | no | Comma-separated tags, e.g. env:prod,service:audit |
hostname | string | no | Originating host name |
service | string | no | Service name (matches APM if you use it) |
| (custom) | any | no | Any additional key-value pairs are indexed as facets |
Example body:
[
{
"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.
| Setting | Value |
|---|---|
forwarder_type | elastic |
| HTTP method | POST |
| URL | Bulk: https://<your-es-host>/_bulk · single doc: https://<your-es-host>/<index>/_doc |
| Required headers | Authorization: ApiKey <base64(id:api_key)>Content-Type: application/x-ndjson (Bulk) or application/json (single doc) |
| Body | Bulk: NDJSON — alternating action and source lines, terminated by a newline. Single doc: a JSON object representing the document. |
Bulk body fields:
| Line | Type | Required | Notes |
|---|---|---|---|
| Action line | object | yes | Names the operation and target index, e.g. {"index": {"_index": "smpl-audit"}}. Supported operations include index, create, update, delete. |
| Source line | object | yes (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.
| Setting | Value |
|---|---|
forwarder_type | honeycomb |
| HTTP method | POST |
| URL | Batch: 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 headers | X-Honeycomb-Team: <ingest-api-key>Content-Type: application/jsonHoneycomb Classic accounts only: also X-Honeycomb-Dataset: <dataset> |
| Body | Batch: a JSON array of event envelopes. Single event: just the data object. |
Body fields (batch envelope, per array element):
| Field | Type | Required | Notes |
|---|---|---|---|
data | object | yes | The event payload — any key-value pairs you want to index |
time | string | no | RFC3339 timestamp; defaults to receive time at the server |
samplerate | integer | no | Sampling 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):
[
{
"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.
| Setting | Value |
|---|---|
forwarder_type | new_relic |
| HTTP method | POST |
| URL | US: 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 headers | Api-Key: <license-or-ingest-key>Content-Type: application/json |
| Body | Either the simplified shape (a single JSON object) or the detailed shape (array of {common, logs} envelopes) |
Simplified body fields (a single object):
| Field | Type | Required | Notes |
|---|---|---|---|
message | string | no, but recommended | Primary searchable log field |
timestamp | integer or string | no | Unix epoch ms, Unix epoch s, or ISO8601; defaults to receive time |
logtype | string | no | Identifies the log shape for parsing rules |
| (custom) | any | no | Any other fields are indexed as attributes |
Example body (simplified):
{
"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):
[
{
"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.
| Setting | Value |
|---|---|
forwarder_type | splunk_hec |
| HTTP method | POST |
| URL | https://<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 headers | Authorization: Splunk <HEC-token>Content-Type: application/json |
| Body | One event envelope, or several envelopes concatenated (no array — Splunk reads them one after the other) |
Body fields (per envelope):
| Field | Type | Required | Notes |
|---|---|---|---|
event | any | yes | The event payload — a string or object |
time | number | no | Unix epoch seconds (fractional ok); defaults to receive time |
host | string | no | Originating host |
source | string | no | Free-form source label |
sourcetype | string | no | Splunk source type, e.g. _json |
index | string | no | Target index (HEC token must allow it) |
fields | object | no | Indexed metadata (key-value pairs) |
Example body:
{
"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.
| Setting | Value |
|---|---|
forwarder_type | sumo_logic |
| HTTP method | POST |
| URL | Whatever 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 headers | Presigned mode: none. Auth-Header mode: Authorization: <token-as-shown-by-Sumo>.Either mode: Content-Type: application/json for JSON payloads. |
| Body | Any 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):
{
"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.
| Setting | Value |
|---|---|
forwarder_type | http |
| HTTP method | Whatever your destination expects (POST, PUT, PATCH, GET, DELETE) |
| URL | Any http:// or https:// URL |
| Required headers | Whatever your destination requires — set them in http.headers |
| Body | Whatever 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 configuredsuccess_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
environmentfield, 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.
Related
- Smpl Audit overview — events, fields, query patterns
- API Reference — Audit — full schema for forwarders, deliveries, and retry endpoints

