Forwarding the audit log to your SIEM (push)
The engine already lets a SIEM collector pull the hash-chained audit trail on a schedule (wiring the audit feed into your SIEM). Push is the other direction: the engine dials out to your SIEM’s own intake, on the scheduler’s own tick, so the audit trail reaches your SIEM without anything on your side ever polling for it.
One fact matters before any of the mechanics below. Push sidesteps the Cloudflare Access problem the pull feed can run into. Because the engine is the one making the outbound call, there is no inbound request for a Cloudflare Access application in front of your console hostname to turn away. If your console hostname sits behind Access and you would rather not carve an exemption into it for a collector, push gets the same audit trail out without touching your Access policy at all.
Why push, alongside pull
SIEMs do not agree on how they want to receive events, so downpipes supports both of the two mainstream methods rather than picking one. Some collectors are built to poll a REST endpoint on a schedule, which is what the pull feed serves, and Microsoft Sentinel, Exabeam, Cribl and Sumo Logic all take that cleanly. Others expect the opposite: an HTTP endpoint of their own that a source posts into. Splunk and the CrowdStrike Falcon Next-Gen SIEM lean on this, through Splunk’s HTTP Event Collector (HEC), and Datadog accepts this shape only, because its hosted pull sources cannot carry the custom header the pull feed needs behind an Access-fronted hostname.
Push shapes a wider set of formats than it once did, mapped in the table below, so most HTTP-intake SIEMs can now take it. A few, like Cribl and Sumo Logic, do either method well, and are yours to place on whichever fits your topology. Push exists so the choice is yours to make per SIEM, not a limitation of the platform. Running both at once is fine too, for example a collector that polls for one system alongside a push destination that feeds a second.
Push dials out, so a Cloudflare Access perimeter never sees it
The pull feed rides your console’s public hostname, because /support/* is proxied through the console to the engine. When a Cloudflare Access application fronts that hostname, the edge answers a collector’s poll with a redirect to your team’s login page before the engine ever sees the bearer credential, and the fix is to add a service token or a narrow path exemption for the collector (the full account is on if Cloudflare Access fronts the hostname).
Push has no equivalent problem, and the reason is structural rather than a workaround. The engine is the client on a push delivery: it opens the connection to your SIEM, and your SIEM never opens a connection to the engine. Cloudflare Access protects a hostname by gating inbound requests to it, and a push delivery is not an inbound request to your console hostname at all, so there is nothing on that hostname for an Access policy to gate. You do not add a service token, you do not carve out a Bypass, and you do not need the split-topology arrangement the pull page describes as its last resort. What you take on instead is a stored secret and an outbound destination you control, held to the security posture below.
This is the concrete reason to prefer push for an Access-fronted deployment: not that push is newer or simpler, but that it structurally cannot be blocked by a perimeter that only ever gates inbound traffic.
Which format for which SIEM
A push destination is four choices: a sink (where the batch goes), a format (the shape of the body), the auth the sink needs, and whether it is enabled. The engine shapes every batch to your chosen format and delivers it over your chosen sink. Eight body formats across three sinks are built to reach the mainstream SIEMs; the table maps each SIEM to the format and sink to start with, and the sinks and their honest caveats follow.
| SIEM | Format | Sink | Notes |
|---|---|---|---|
| Splunk | splunk-hec |
http |
HTTP Event Collector, Authorization: Splunk <token> |
| CrowdStrike Falcon Next-Gen SIEM | splunk-hec |
http |
Its HEC-compatible connector, Authorization: Bearer <token> |
| Cribl | splunk-hec |
http |
Cribl’s HEC-style source; it also polls the pull feed if you prefer |
| Sumo Logic | splunk-hec |
http |
HEC-compatible HTTP source; pull-capable too |
| Panther | splunk-hec or json-array |
http |
HTTP log source; use json-array if it wants a bare array. Needs a schema/log-type step in Panther before fields normalise |
| Rapid7 InsightIDR | splunk-hec |
http |
HEC-style HTTP intake |
| Datadog | datadog |
http |
DD-API-KEY: <key>; push only, its hosted pull cannot carry the feed’s custom header |
| Elastic | datadog or json-array |
http |
The Datadog body flattens cleanly; json-array suits an array-splitting input |
| Google Security Operations | ndjson |
http |
One audit event per line. Needs a schema/log-type step in Google Security Operations before fields normalise |
| Stellar Cyber | ndjson or json-array |
http |
Generic HTTP intake that splits on newlines or an array |
| Any generic HTTPS collector | ndjson |
http |
The default format; splits N events out of one body |
| Graylog | gelf |
http |
Graylog’s GELF HTTP input |
| QRadar | cef or leef |
syslog-tls |
Needs a one-time Universal CEF (or LEEF) log source added before its DSM parses the stream; a real but small step, not zero-touch. Only over real syslog, not an HTTPS POST (see the sinks below) |
| LogRhythm | cef |
syslog-tls |
No generic CEF source ships in LogRhythm: you build a bespoke Log Source Type plus an MPE (Message Processing Engine) rule yourself, over syslog TLS. Meaningfully more work than QRadar’s one-time log source add, closer to writing a parser than clicking a wizard |
| FortiSIEM | cef |
syslog-tls (or http) |
Reported, community sources rather than FortiSIEM’s own primary docs, to parse CEF automatically over syslog TLS; treat as plausible, not vendor-confirmed. The http fallback is unconfirmed the same way |
| Securonix | cef |
syslog-tls (or http) |
Needs a schema/log-type step in Securonix before the incoming stream’s fields normalise, not automatic on arrival. The http fallback is unconfirmed in the vendor’s own primary docs |
| Wazuh | ndjson |
s3 |
The engine drops NDJSON batches into a bucket Wazuh already reads |
| Microsoft Sentinel | pull | pull feed | Push cannot reach it; deploy the Codeless connector, no CEF needed (below) |
| Exabeam | pull | pull feed | Its REST API collector polls the feed |
| Cortex (Palo Alto) | pull | pull feed | Polls the feed with a custom header |
| Logpoint | pull | pull feed | Polls the feed |
Nothing about a specific vendor is hard-coded beyond these envelope shapes, so any collector that accepts one of these formats over the matching sink is a candidate target, not only the named products. The last four rows are the SIEMs that take the feed by pull rather than push; the pull page owns their mechanics, and Microsoft Sentinel has its own note below.
The eighth format is raw-json, the Downpipes wrapper: one JSON object carrying the whole batch and its metadata rather than a per-event shape. No named SIEM needs it, which is why it is not in the table above; choose it for a collector of your own that wants the batch verbatim. The exact bytes each format puts on the wire are in payload shapes on the wire below.
The three sinks
A sink is where a shaped batch is delivered. The format decides the body; the sink decides the transport and the auth.
| Sink | What the engine does | Auth |
|---|---|---|
http (default) |
POSTs the shaped batch to your HTTPS endpoint with one named auth header | A sealed header secret, or a URL-path token (below) |
s3 |
Writes each drain batch as an NDJSON object into an S3-compatible bucket your SIEM already reads, keyed by a prefix and the batch’s sequence range | A sealed S3 access key over a signed request (SigV4), no header |
syslog-tls |
Opens a TLS connection to your SIEM’s syslog listener and writes each event as an RFC 5424 record whose message is the CEF or LEEF line | Network-level in v1, so no stored secret |
The URL-token option
The Carry the auth token in the URL option lives on the http sink. A few HTTP intakes carry the secret in the URL path or query rather than in a header, Devo being the common one. Turn the option on and the engine splices the sealed secret into your endpoint template and sends no auth header. The token is still sealed at rest and never appears in a log line, on the delivery trail, or in the redacted view; the trail records the host only.
The s3 sink is the universal fallback and the native path for Wazuh: any SIEM that reads an object store can be fed by dropping NDJSON batches into a bucket, and this reuses the engine’s existing signed-request S3 client rather than any new signing code. The syslog-tls sink exists for one specific reason, which the next warning states plainly.
Setting up the S3 sink
The s3 sink drops NDJSON objects into a bucket your SIEM already reads, so its fields describe that bucket and a credential to write to it. The bucket must already exist; the engine writes into it and never creates it.
The S3 endpoint URL is the S3-compatible endpoint the bucket lives on, https://s3.<region>.amazonaws.com for Amazon S3 or https://<account>.r2.cloudflarestorage.com for Cloudflare R2, and it is screened like the HTTP endpoint, so a private, loopback or cloud-metadata address is refused. The Bucket is that bucket’s name. The Region is the bucket’s real region, for example ap-southeast-2; use auto for R2, but Amazon S3 rejects auto and answers a bucket in another region with an HTTP 301, so set the real region there. The Key prefix is an optional folder path inside the bucket to keep the audit objects together. Leaving it blank does not write at the bucket root: the engine falls back to a downpipes-audit prefix, so the objects land under downpipes-audit/ unless you set a prefix of your own.
The Access Key ID and secret access key are an S3-API credential for that bucket, the same kind of credential a backup destination uses, and you mint it wherever the bucket lives, not in downpipes. For a Cloudflare R2 bucket, create an R2 API token (with the S3-compatible access key id and secret it gives you) in the Cloudflare dashboard under R2, API, Manage API Tokens; for Amazon S3, create an IAM user or role access key; for another S3-compatible store, use that provider’s access-key screen.
The engine only ever writes to this sink: it PUTs one NDJSON object per batch, keyed by your prefix and the batch’s sequence range, and never reads, lists or deletes (putSiemBatchToS3, engine/src/cron/siem-push-pass.ts). So the credential needs exactly one permission, s3:PutObject on that bucket, scoped to your prefix if you want to keep it tight. It does not need read, list or delete, and giving it more than PutObject grants access this sink never uses. There is no separate write-probe for the push sink the way there is for a backup destination, so a key that cannot PUT surfaces as a delivery failure on the trail rather than a refusal at save time; confirm the key can write to the bucket before you rely on the feed.
CEF and LEEF need real syslog, and syslog arriving is not the same as being parsed
A plain HTTPS POST of a CEF or LEEF line is never read by the built-in log-source parsers (the DSMs) of QRadar or LogRhythm at all. Those parsers only look at real syslog transport, an RFC 5424 record over TCP with TLS on port 6514, which is why the engine delivers cef and leef over the syslog-tls sink and not over http. Arriving over syslog TLS is necessary on both, but it is not by itself sufficient: QRadar still needs the one-time Universal CEF or LEEF log source added before its DSM parses the stream, and LogRhythm has no generic CEF source at all, so you build a bespoke Log Source Type and an MPE rule yourself, as the table above says. Transport is not parsing; budget for the extra step regardless of which sink carries the bytes.
Two parts of that path can only be confirmed from your own deployment, and this page will not pretend otherwise. First, whether a Cloudflare Worker can open an outbound socket to port 6514 is undocumented by Cloudflare, which documents port 25 and its own ranges as blocked and stays silent on 514 and 6514, so confirm the connection lands from your engine before you rely on it. Second, whether a given SIEM accepts and parses the exact CEF or LEEF line the engine emits depends on how you configure that SIEM’s log source, which only a live send to your own instance confirms.
FortiSIEM sits apart from the rest of this table: its syslog-tls auto-parse, and the http fallback it is said to share with Securonix, both rest on community reports rather than FortiSIEM’s own primary documentation, so treat FortiSIEM as plausible, not confirmed. Securonix’s http fallback carries that same unconfirmed status; its syslog-tls path is different, a documented schema step rather than a guess, as the table above says.
Setting up the syslog sink
The syslog-tls sink delivers to a syslog collector over TCP with TLS, and it is the sink that carries the cef and leef formats (a plain HTTP POST of a CEF or LEEF line is never parsed, as the warning above explains). It has two fields.
This sink carries cef or leef and nothing else, and a syslog destination saved with any other format is refused rather than converted. A syslog record’s message is one CEF or LEEF line, so there is no HEC, NDJSON or GELF shape for it to carry, and quietly substituting CEF would send your SIEM plausible bytes under the wrong parser with nothing on the record to say so. Pick cef or leef above, or choose a different delivery.
The Syslog host is the hostname or address of your collector, a bare host with no scheme and no path, for example siem.internal.example or 10.0.4.20. It is required. Unlike the HTTP and S3 endpoints, the syslog host is deliberately not screened against private, loopback or on-premises addresses, because a syslog collector on your own private network is the normal case; you are naming a destination on your own infrastructure, not an arbitrary URL the engine dials out to.
Not being egress-screened is not the same as not being verified. The connection is implicit TLS, and the certificate your listener presents is validated against the public trust store like any other, with no way to hand the engine a private CA bundle. A collector on your own network holding a certificate from your internal CA, or a self-signed one, fails the handshake and nothing is written; the trail records syslog-tls-untrusted. Issue that listener a certificate from a public CA for a name you control, or use the http or s3 sink instead.
The Port is a whole number from 1 to 65535. Leave it blank and it defaults to 6514, the RFC 5425 port for syslog over TLS.
Microsoft Sentinel takes the feed by pull, not push
Microsoft Sentinel is the one large SIEM this push cannot reach. Its log ingestion authenticates with OAuth2 and an HMAC-signed request, so there is no static HTTP intake to point a push destination at, and it needs no CEF. Sentinel consumes the same audit feed by pull instead, through a Codeless connector you deploy into your own Azure workspace. The deployable ARM package and its deploy guide ship in the engine repo under integrations/microsoft-sentinel/; the connector polls /support/audit-feed with a bearer token and the feed’s own nextAfterSeq cursor, exactly as the pull page describes. For the pull mechanics the connector rides, read wiring the audit feed into your SIEM.
Payload shapes on the wire
Whichever format you choose, the underlying events are identical to the ones the pull feed serves: the same redaction-safe, identity-bearing fields (seq, ts, actorEmail, actorSubject, actorMethod, sourceIp, action, outcome, target, prevHash, hash). Only the envelope around them changes, and the default for a new destination is now ndjson.
ndjson writes one raw audit event per line, so a generic HTTP intake splits the body into N events with no array or wrapper to unpick. This is the default.
{"seq":4097,"ts":"2026-07-05T02:14:08Z","action":"restore-approve","outcome":"success","actorEmail":"ops@acme.example","...":"..."}
{"seq":4098,"ts":"2026-07-05T02:14:09Z","action":"downpipe.update","outcome":"success","...":"..."}
json-array sends the same events as one bare array, for an intake that splits an array rather than newlines (Elastic and Panther are the common ones).
[ { "seq": 4097, "action": "restore-approve", "outcome": "success", "...": "..." },
{ "seq": 4098, "action": "downpipe.update", "...": "..." } ]
splunk-hec sends one HEC event object per audit event, whitespace-separated in the one request body (HEC accepts concatenated JSON objects), with time as epoch seconds and the audit event nested under event.
{"time":1718763248,"source":"downpipes","sourcetype":"downpipe:audit","event":{"seq":4097,"action":"restore-approve","...":"..."}}
{"time":1718763249,"source":"downpipes","sourcetype":"downpipe:audit","event":{"seq":4098,"...":"..."}}
datadog sends a JSON array with ddsource and service set, a status field, and the audit event flattened alongside a message string, batched under Datadog’s 5 MB and 1000-item request limits. Left unset, Datadog defaults an event to info, which would flatten a denied or failed action to the same severity as a success; the shaper avoids that by always setting one, a denied or failed outcome maps to error or warn and a success maps to info, so a failed or denied action already reads at the right severity in Datadog with nothing for you to configure.
[ { "ddsource": "downpipes", "service": "downpipe-engine", "status": "info", "message": "restore-approve success",
"seq": 4097, "action": "restore-approve", "outcome": "success", "...": "..." } ]
raw-json posts the pull feed’s own wrapper body, so a collector that already parses the pull response can point at either mechanism with the same parser. It stays selectable, but it is no longer the default because no SIEM preferred the wrapper over the split-friendly shapes.
{ "kind": "downpipe-audit-feed", "v": 1, "afterSeq": 4096, "nextAfterSeq": 4220,
"headSeq": 4220, "headHash": "sha384:...", "count": 124, "events": [ { "seq": 4097, "...": "..." } ] }
cef, leef and gelf are the SIEM-specific shapes. cef emits an ArcSight CEF line per event, delivered over syslog-tls for QRadar and LogRhythm auto-parse; leef emits IBM QRadar’s LEEF 2.0 line; gelf emits a Graylog GELF object per line for the GELF HTTP input. A CEF line looks like this, with the audit action as both the event class and the act key and the timestamp as epoch milliseconds in rt:
CEF:0|Maelstrom AI|Downpipes|0.1.4|downpipe.update|Downpipe updated|1|rt=1783131453482 suser=ops@acme.example src=203.0.113.42 act=downpipe.update outcome=success cs3Label=targetKind cs3=downpipe cs4Label=targetId cs4=dp_8f2a1
Every field value the engine places into a CEF, LEEF or GELF line is escaped or rejected so that a crafted value, an actor email or a target name holding a delimiter or a newline, cannot break the format’s grammar or forge a second event. GELF is JSON-encoded and so is safe by construction; CEF escapes the reserved characters; LEEF rejects a value that carries its delimiter.
Every shaped batch is capped under the tightest target’s per-request limit (Datadog’s 1000 events or 5 MB, whichever comes first), so a large backlog sends across several ticks rather than as one oversized request.
Setting up the push destination
One requirement decides whether a destination can deliver at all, on every one of the three sinks, and it is worth knowing before you paste anything into the form. The host you name must present a TLS certificate that chains to a publicly trusted root and that matches the hostname you entered. Certificate verification happens before the request is sent, so a self-signed certificate, an expired one, a chain missing its intermediate, or a certificate issued for some other name, each refuse the connection while your auth secret is still unused. The engine offers no way to skip verification and will not: an audit trail delivered over a connection nobody authenticated is not evidence of anything.
This matters because the failure lands at the same moment in setup that a wrong token does, and reads like one. It is not an auth fault. The credential was never offered to the endpoint, so re-minting the token, re-pasting the secret, or changing the header name will not move it. The fix is on the endpoint’s side, or on a different endpoint.
Where the certificate fault is named
On the http sink the delivery trail records the reason network-tls, which the console expands into the certificate sentence rather than showing you the slug, and GET /admin/push carries the same code on the attempt, so a support pack shows it too. An engine older than 2026-07-31 could record the same fault as http-5xx, because the delivery runtime answers a certificate it refuses with a synthesised 525 or 526 status instead of throwing, and that read as your SIEM erroring rather than as its certificate. Both statuses now classify as network-tls.
The syslog-tls sink opens its socket against the same public trust store and names its own handshake failure syslog-tls-untrusted. The s3 sink reaches your bucket’s endpoint over HTTPS, so its endpoint carries the same requirement.
An Owner configures the destination
Choose the sink (an HTTPS endpoint, an S3 bucket, or a syslog host), pick the format (the default is
ndjson), and supply the sink’s details. For the HTTPS sink that means the endpoint URL and the auth header your SIEM expects, or the URL-token option, with the secret pasted once. The auth header name defaults toAuthorizationwhen you leave it blank, and otherwise takes any valid HTTP header name of up to 100 characters, for exampleDD-API-KEY; a hop-by-hop header is refused, and the URL-token option sends no header at all. For a Splunk HEC destination, that pasted value must be the literalSplunk <token>string, the word “Splunk”, one space, then the token: HEC treats that prefix as part of the authentication scheme, not decoration, so the token alone will not authenticate. Setting Splunk up from its own tile on the Integrations screen, the console refuses a value without that prefix as you leave the box, rather than accepting it and leaving you with a 401 that reads exactly like a revoked token. That check belongs to the vendor and not to the wire format: CrowdStrike Falcon Next-Gen SIEM takes the samesplunk-hecshape over the same HTTPS sink and issues a plain bearer token, so its own tile asks for the token alone. For the S3 sink it means the bucket, region and endpoint plus the access key, sealed the same way. This is owner-only and requires a fresh step-up re-authentication; once a second owner exists on the account, it is also dual-control gated the same way setting a backup destination is, because the engine has to reconstruct the secret on every delivery rather than only verify it once. A non-owner sees the control disabled with the reason, never hidden. A stored secret is sealed at rest the same way a destination credential is, and it is never read back: changing it later means re-entering it, not editing it.Send a test event
The Test send button pushes one synthetic, audit-shaped event through the same egress-secure path the real drain uses, and reports the sink’s outcome: a rejection from your SIEM, a timeout, a refused connection, or a reason from the egress guard. A test send never advances the delivery cursor, so it cannot be mistaken for real drain progress.
A pass here proves the wiring, and that is all it proves. It says the endpoint was reachable, the certificate was trusted, and the credential was accepted. On the HTTP sink it does not prove the event was indexed, for the reason set out under at-least-once delivery: a Splunk HEC refusal can arrive inside a
200. Confirm the synthetic event in your SIEM before you treat the destination as working. It is labelled in its owndetailfield as a synthetic test event, so it is easy to find and easy to tell apart from a real engine action.Enable it
With the wiring proven, enable the destination. From the next scheduler tick onward, the engine reads events above the cursor, shapes them per your chosen format, and delivers the batch over your chosen sink.
Watch the delivery trail and the cursor lag
The panel shows recent attempts (an outcome and a coarse reason, never a response body) and how far the cursor sits behind the current head. A growing lag alongside failing attempts means your sink is unreachable or rejecting the batch, and the trail tells you which.
Replacing the destination follows the same owner path and kills the old secret immediately, the same as re-minting a pull credential does. Clearing it needs only the owner gate and step-up, with no second-owner approval, because closing an outbound path is the safe direction rather than the risky one.
Security posture
The push destination is an egress of identity-bearing audit data, a stored secret, and a sink you control, so it carries three protections, each reusing a pattern the engine already has elsewhere.
The auth secret is sealed at rest and never returned. What you see back from the engine, in the console or from GET /admin/push, carries the sink, the format, the endpoint (or, for the S3 sink, the bucket, region, endpoint and prefix), the header name, whether the destination is enabled, who set it and when, and the delivery trail. It never carries the header secret, the S3 access key, a URL-path token, or a hash of any of them. An S3 access key is sealed under its own distinct wrapping, separate from the header secret, so one sink’s key can never be resolved through another’s path. Rotation is re-enter, not edit, exactly as a pull credential’s rotation is revoke and re-mint rather than an in-place change.
Opening or replacing the destination is owner-only, step-up-authenticated, and, once a second owner exists, dual-control gated: the same authority path a backup destination follows, described in full at four-eyes change control. Clearing the destination is owner-only and step-up-authenticated too, but carries no second-owner approval, because closing the path is the safe direction.
The outbound call inherits every egress guard the alert webhook already has. An HTTP endpoint must be https; a private, loopback, link-local, or cloud-metadata address is refused by default, at both save time and send time; a redirect is not followed; the request carries a timeout; and the response is never read beyond its status. The S3 endpoint is screened by the same rules before any request, and the syslog sink opens an outbound-only TLS socket to the host you name (its one open question, whether a Worker can reach port 6514 at all, is the owner-verify item above). No new outbound code path bypasses these guards, because the send reuses the engine’s existing egress-secure fetch and signed-request clients. The screening itself, including the one deliberate override for a genuine private sink and the one gap it does not close, is described in full at securing notification webhooks; the push destination is held to the same standard.
A stolen ambient session cannot redirect the audit egress
Every route that sets, replaces, or clears the push destination requires a fresh step-up re-authentication, the same requirement every destination-mutating route carries. A session cookie alone is not enough to point your audit trail at a new endpoint.
At-least-once delivery, not exactly-once
The drain is at-least-once, and this page says so plainly rather than implying more. On each scheduler tick, if the destination is enabled, the engine reads events with a sequence above the last delivered one, shapes them, and delivers the batch once. On a success (a 2xx for the HTTP sink, a completed PUT for the S3 sink, or a batch written and flushed for the syslog sink) it advances the cursor to the last delivered sequence and records a success on the trail. On anything else, it leaves the cursor where it was, records the failure and a coarse reason, and retries on a later tick with backoff, so a persistently unreachable sink is not hammered every tick.
That leaves one honest gap. A success followed by the engine failing before the cursor is persisted means the same batch sends again on the next tick. Nothing here claims exactly-once delivery. Every audit event carries a stable seq and a hash, and either is a safe dedup key on your SIEM side: index on seq (or hash) and a re-sent event lands as a duplicate you can drop rather than a second incident.
On the HTTP sink, a 2xx is acceptance, not proof of indexing
The distinction matters most on Splunk HEC, because HEC answers 200 with a JSON body that carries its own status code, and several refusals arrive that way rather than as an HTTP error. Measured against a live Splunk Cloud stack, {"text":"Incorrect index","code":7} arrives under HTTP 200. The engine reads the HTTP status when it decides whether a batch was delivered, so a refusal carried in the body of a 2xx is recorded as a success and the cursor moves past those events. They are not in your index and they will not be re-sent.
Two consequences to plan for, both of which you can check from the Splunk side today.
Set the index on the HEC token, not in downpipes. The engine sends time, source, sourcetype and event, and does not send an index key, so the index is whichever one the token is configured to write to. There is no index field on the destination and nothing in the console can correct a mismatch. If the token’s default index is wrong or the token is not allowed to write to it, HEC accepts the request and drops the events, and the delivery trail shows a success.
Confirm the events arrived, once, in Splunk. A green delivery trail says the request was accepted. Search your index for sourcetype="downpipe:audit" after you enable the destination and confirm the count moves, and alert on the absence of events rather than relying on the trail alone. That is the same discipline the pull feed’s headHash gives you, applied to the push direction.
The other sinks do not have this gap. The S3 PUT and the syslog write are confirmed by the transport itself, with nothing to carry a refusal past a successful status.
The drain runs on the scheduler’s own tick cadence, minutes rather than per event. It is built for an audit trail, not a real-time stream, so treat a few minutes of lag as normal rather than a fault.
When a delivery fails, and what the reason means
The delivery trail records a coarse reason per failure, never the error text, so nothing about your endpoint or its certificate is retained. The console expands that reason into what to check. These are the ones worth knowing before you set a destination up.
The left column is the code the trail stores and the API returns; the console shows the sentence beside it rather than the slug.
| Reason on the trail | What it means, and where to look |
|---|---|
network-tls (certificate not trusted) |
The TLS handshake failed, so nothing was sent and the credential was never offered. The certificate your endpoint serves is self-signed, expired, missing an intermediate, or issued for a different hostname. Check it on the exact host and port you entered, not on 443 |
network-dns (hostname did not resolve) |
The endpoint’s host does not exist. Check the spelling, and check the ingest hostname exists on your plan at all |
network-reset (connection refused or dropped) |
Nothing accepted the connection on that port from your Cloudflare account |
timeout (timed out) |
The endpoint accepted the connection and did not answer inside the send timeout |
http-auth (credential rejected) |
A 401 or 403. The token or key is wrong, expired, or lacks permission. This is not a network fault |
http-rate-limited (throttled) |
A 429. The credential is fine and the batch is retried on a later tick |
http-bad-request (request shape refused) |
A 400. The format you selected may not be the one this endpoint accepts |
A vendor's ingest port can serve a different certificate than its web port
A stack that looks healthy in a browser is not evidence that its ingest port will be accepted, because the two can serve different certificates. Splunk Cloud is the case we have measured. The customer-facing HEC endpoint is a dedicated ingest hostname, http-inputs-<stack>.splunkcloud.com, and that is the host to configure where your plan publishes it. Where it does not resolve, HEC is reached on the stack’s own host at port 8088, and on the trial stack measured on 2026-07-31 that port served splunkd’s stock self-signed certificate (CN=SplunkServerDefaultCert, issued by CN=SplunkCommonCA) while 443 on the same host served a valid CA-issued chain. Certificate verification refuses the self-signed port before any HTTP is exchanged, so no batch reaches it.
Read that as the shape of the problem rather than a description of every stack: check what your own ingest host and port present, on that host and that port. If it is splunkd’s default certificate, the remedy is on the Splunk side, by installing a certificate for the ingest listener that chains to a public CA. Splunk documents this, and Splunk Cloud customers can raise it with Splunk support. The engine offers no way to skip verification.
Where the runtime does not disclose why a connection failed, the trail says the connection failed and does not guess. For an HTTPS endpoint the certificate chain and the hostname are the two things to check first.
What the push carries, and what it does not
The events pushed out are the same redaction-safe records the pull feed serves, so the same honesty applies here. The push carries operator and admin identity on purpose: member emails, source IPs, roles, and approver emails, because attributing who did what is the point of an audit trail. It carries no customer backup data, no keys, and no secret values.
| Field | Example | What it is |
|---|---|---|
actorEmail |
ops@acme.example |
The verified display email of the human who acted |
actorSubject |
https://acme.cloudflareaccess.com|01J9Z7M3QF8K2WX4P6R0V5T1AB |
The stable opaque principal the engine authorises on |
sourceIp |
203.0.113.7 |
The source address the engine saw |
The feed carries operator identity, on both paths
Whether you consume the audit trail by pull or by push, it carries member emails, source IPs, roles, and approver emails, so it is never described as free of personal data or identifying information. Apply your SIEM’s data-handling rules to it, the same as you would to any access log.
Where this fits
This page owns the push mechanics: the formats, the three sinks, the security posture, and the at-least-once semantics. For the pull counterpart, cursor by sequence, and the continuity verification the same events support, read wiring the audit feed into your SIEM. For the owner-minted credential the pull path uses instead of a stored secret, read owner-minted pull credentials. The wire-level reference for the pull endpoints is support diagnostics and audit-feed pull endpoints; the four push admin routes are catalogued alongside every other /admin route in the admin API endpoint catalogue. For the egress screening the outbound call inherits in full, read securing notification webhooks. For Microsoft Sentinel, deploy the Codeless connector shipped under integrations/microsoft-sentinel/ in the engine repo and consume the feed by pull. For the audit chain itself and its on-screen export, read the audit log.
Last updated .