Skip to main content
A 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.
Before you begin
  • A HEC token. Splunk Web → SettingsData inputsHTTP Event CollectorNew Token.
  • HEC enabled globally. On the same screen, Global SettingsAll TokensEnabled. 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
In CI, read the value from the environment instead:
Store Splunk <token>, not the bare token. The stored header value is sent verbatim as Authorization; Lua never prefixes a scheme onto it, because prefixing one onto a value that already has it would 401 forever. A bare token is the single most common Splunk setup mistake, and it looks like a bad token rather than a bad header.
/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

A vendor preset is reachability-checked, not ownership-verified. lua drains verify posts one batch holding a single lua.drain.test record, and any 2xx from the intake is accepted. It proves the host answers and the credential works. It does not prove you own that host — no Splunk intake can echo a challenge token back, so only a generic HTTPS drain can be ownership-verified and only an object-storage drain proves ownership by writing into a bucket.Point a splunk drain only at a stack you control.
A 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 answers 400 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 answers 403 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

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 SettingsAll TokensEnabled.
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.
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.
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.