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:
| Field | Description |
|---|---|
name | Display name. |
description | Free text — what this forwarder is for. |
enabled | Boolean. When false, the forwarder is skipped entirely; nothing is sent and no delivery records are written for it. |
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. 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:
{ "==": [{ "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 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):
{
"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 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 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.FILTERED_OUT— the forwarder's filter evaluated false; the event was not sent.SKIPPED_DO_NOT_FORWARD— the source event haddo_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.
Related
- Smpl Audit overview — events, fields, query patterns
- API Reference — Audit — full schema for forwarders, deliveries, and retry endpoints

