splunk drain posts to your own Splunk HTTP Event Collector — Splunk Cloud or a self-hosted stack. Each record becomes one HEC event with source: lua, sourcetype: lua:log, and the whole Lua record under event, so event.eventName and event.attributes.* are searchable exactly as the event schema spells them.
Vendor destinations are opened per deployment. Where
splunk is not open yet, a create answers 422 DRAIN_TYPE_UNAVAILABLE and the message names the types that are. It may not be enabled on your deployment yet — that is a rollout state, not a fault.- A HEC token. Splunk Web → Settings → Data inputs → HTTP Event Collector → New Token.
- HEC enabled globally. On the same screen, Global Settings → All Tokens → Enabled. A token on a stack where HEC is globally disabled fails every delivery, and it is the most common setup mistake after the header form below.
- The token’s default index. Lua does not choose one — see below.
What to enter
Output
/services/collector/event is composed onto whatever host you give, and composition is idempotent: https://splunk.example.com:8088 and https://splunk.example.com:8088/services/collector/event store the same endpoint, so paste whichever form your Splunk UI showed you. A query string or fragment is dropped — no intake here reads one, and a credential pasted into ?token=… would otherwise be stored in clear.
There is no host allow-list for this type: a Splunk stack is your host. It is still validated as https: and as resolving entirely to public addresses, re-checked on every send.
How ownership verification works
403 means the token is wrong, disabled, or HEC is off globally. The drain stays in pending_verification and keeps buffering while you fix it. Verification is limited to 5 attempts per drain per hour, so read the error rather than retrying in a loop.
Splunk deliveries are not signed. There is no X-Lua-Signature and no signing secret; the Authorization header authenticates the sender.
What arrives
The body is HEC’s own format: the event objects concatenated, with no separator and no enclosing array. HEC answers400 Invalid data format to an array.
A timestamp that does not parse omits
time entirely, so Splunk stamps index time. That is the honest fallback: a zero epoch would file the event in 1970 and look like data rather than a gap.
The index is never sent
A HEC token carries its own list of allowed indexes and its own default. Lua does not name one, because naming an index a token may not write answers403 Incorrect index forever — a drain that verifies and then fails on every real batch. Your records land in the token’s default index. To route them elsewhere, mint a token whose default is the index you want.
Useful searches
Caps
The batch cap sits below the vendor’s request limit on purpose: the cap is applied to the stored byte estimate before the encoder adds its envelope, and the headroom is what makes “no
413” a property rather than a hope.
A record over 256 KiB has its body cut and suffixed ... [truncated by lua] rather than being dropped — you still get the event, its severity, and all of its attributes.
HEC signals back-pressure as 503 and rate limiting as 429. Both are statuses in the retry set, so nothing has to parse a Splunk error body.
Test the token with curl
Prove the token and the host before creating the drain. This sends exactly the shape a delivery has:200 {"text":"Success","code":0} means accepted. 403 {"text":"Invalid token","code":4} means the token is wrong; 400 {"text":"Incorrect index","code":7} means the token cannot write where it was pointed.
Then do the same through the real delivery path:
If it isn’t working
403 on every delivery
403 on every delivery
Either the stored header value is a bare token rather than
Splunk <token>, or HEC is disabled globally on the stack. Re-enter the whole value with lua drains update <id> --header Authorization, and check Global Settings → All Tokens → Enabled.400 Invalid data format
400 Invalid data format
That is what HEC answers to a JSON array. Lua sends concatenated objects, so this should not happen through a drain — if you see it, something between Lua and HEC is re-wrapping the body.
Events are in the wrong index
Events are in the wrong index
Lua never sends
index. The records land in the HEC token’s default index. Mint a token whose default is the index you want, and update the header value.Every event has the same timestamp
Every event has the same timestamp
You are looking at index time, which means
time was omitted because the record’s timestamp did not parse. Open one event and check event.timestamp; that field is always the platform’s own reading.Next steps
Event schema
What every field under
event means.Protecting your destination
What verification does and does not prove.
Delivery guarantees
Retry, drops, heartbeat, and health states.
lua drains
Every verb and flag.

