Select spec version:

Sources Listen Command Reference

Alpha
Complete Rudder CLI reference for sources listen and sources events: flags, output streams, the local query API, limits, and error codes.
Available Plans
  • free
  • growth
  • enterprise

This page is a detailed reference for the rudder-cli sources commands. sources listen runs a local server that accepts events the way RudderStack ingestion does and prints each accepted event on stdout. sources events reads the events a listener keeps in memory.

For a step-by-step walkthrough with examples, see How to Verify App Events Locally.

Experimental feature

The sources commands are experimental and disabled by default. You must enable experimental mode and the sourcesListen flag before you run them. Experimental features are functional but their commands, flags, and output can change or be removed in future releases, so avoid relying on them in production workflows.

Overview

CommandUse case
rudder-cli sources listenStart a local server that answers RudderStack SDKs and prints each accepted event on stdout.
rudder-cli sources eventsRead the events a listener started with --store keeps in memory.

The listener needs no access token, no workspace, and no network. It does not deliver events to destinations, run transformations, or validate events against a Tracking Plan.

Enable the experimental flag

The sources commands require two settings:

  1. Experimental mode (experimental): The main switch for all experimental features.
  2. The sourcesListen flag: Turns on the sources commands.

If either is disabled, every sources command exits with code 1 and the error code experimental_disabled. The message names both environment variables.

Edit your Rudder CLI configuration file (~/.rudder/config.json):

json
{
  "experimental": true,
  "flags": {
    "sourcesListen": true
  }
}
rudder-cli sources --help prints the full user guide for these commands, and does not need the flag.

Start a listener

Command syntax

bash
rudder-cli sources listen [flags]

Parameters

ParameterTypeDescription
--portIntegerTCP port to listen on. 0, the default, lets the operating system pick a free port, so parallel listeners never collide. A fixed port that is taken fails at start with port_in_use.
--bindStringIP address to listen on. Default: 127.0.0.1. Use 0.0.0.0 only inside a container. A non-loopback bind prints a warning on stderr.
--write-keyStringAccept only this write key and answer 401 to every other key and to a request without one on the ingestion routes.

On /sourceConfig, a missing key gets 401 and a key that is not allowed gets 400.

Repeat the flag for more keys. Without it, the listener accepts any key, including a missing one.
-s, --storeFlagAlso keep every request in memory, and serve the query API and the review page.
--allow-hostStringWith --store, also accept this Host name on the query API, such as a container service name. Repeat the flag for more names.
-h, --helpFlagDisplays help information for the command.

Description

sources listen runs in the foreground until it receives SIGINT (Ctrl+C) or SIGTERM. It then finishes the requests in progress and exits with code 0. To run it in the background, use your shell’s & and stop it with kill <pid>.

The listener answers every route an SDK calls:

  • The ingestion routes /v1/track, /v1/identify, /v1/page, /v1/screen, /v1/group, /v1/alias, /v1/batch, /v1/merge, /v1/import, /v1/audiencelist, /beacon/v1/batch, /pixel/v1/track, and /pixel/v1/page, with RudderStack’s status codes and error messages.
  • /sourceConfig, with a fixed source configuration that has no destinations, so browser and mobile SDKs finish loading.
  • CORS preflights and the health routes.

Output

The listener writes two streams:

StreamContent
stdoutEach accepted event as one JSON line, exactly as the SDK sent it, with whitespace removed. A batch of five events gives five lines. Nothing is added: no sequence number, no receive time, no messageId the SDK did not send. Configuration calls, preflights, health checks, and refused requests write nothing to stdout.
stderrFirst, the ready line. Then one refused: STATUS METHOD ROUTE: REASON line for each refused ingestion request. A startup error is the last line, as one JSON object.

The ready line is one JSON object that the listener prints once the port accepts connections:

json
{"ready":true,"apiVersion":"v1","serverId":"225d1f4623d83972","url":"http://127.0.0.1:4321","port":4321,"bind":"127.0.0.1","pid":66751,"startedAt":"2026-10-14T17:14:57Z","writeKey":"dev","writeKeyPolicy":"any"}
FieldDescription
urlThe base URL to give the SDK as its data plane URL (and config URL).
port, bind, pidThe port, the bound address, and the process ID to stop.
serverIdA random ID for this run. A restarted listener has a new one.
writeKey, writeKeyPolicydev and any by default. With --write-key, the first allowed key and allowlist. A key longer than 8 characters appears masked.
cursor, uiOnly with --store: the current position in the store, and the review page URL.

A refused line looks like this:

refused: 400 POST /v1/track: invalid json
refused: 401 POST /v1/batch: invalid write key
refused: 401 POST /v1/track: failed to read writekey from header

A pixel request answers 200 even when its event is dropped. It still prints a refused: line.

Examples

  • Print events as they arrive:
bash
rudder-cli sources listen --port 4321
  • Show only one event name:
bash
rudder-cli sources listen --port 4321 | grep '"event":"Order Completed"'
  • Run in the background, with the events and the log in files:
bash
rudder-cli sources listen --port 0 > events.ndjson 2> listen.log &
  • Accept only two write keys:
bash
rudder-cli sources listen --port 4321 --write-key web-key --write-key api-key
  • Keep requests in memory for sources events and the review page:
bash
rudder-cli sources listen --port 4321 --store

Read stored events

Command syntax

bash
rudder-cli sources events --url <listener-url> [flags]

Parameters

ParameterTypeDescription
--urlStringThe listener URL from the ready line. Default: the RUDDERSTACK_DEV_URL environment variable.
--sinceStringRead after this cursor, since a duration such as 5m, or since an RFC 3339 time. Default: 0.
--eventStringOnly events with this name, or a prefix that ends in * such as 'Order*'. Repeat the flag for more names.
--typeStringOnly events of this type, such as track or page. Repeat the flag for more types.
--user-idStringOnly events with this userId.
--anonymous-idStringOnly events with this anonymousId.
--write-keyStringOnly requests sent with this write key. '' selects requests without one. Repeat the flag for more keys.
--limitIntegerPrint at most this many events, from 1 to 1000. Default: 100. A request is never split across pages.
--viewStringlist (a table of whole events, the default), compact (without the context the SDK collects on its own), or full.
--fieldsStringKeep only this dotted path of each event, such as properties. Repeat the flag for more paths. Cannot be combined with --view.
--server-idStringFail with server_changed unless the listener has this serverId.
--timeoutDurationGive up on the listener after this long. Default: 5s.
-j, --jsonFlagPrint NDJSON, one event per line, instead of a table.
-h, --helpFlagDisplays help information for the command.

Different flags combine with AND. A repeated flag combines with OR.

Description

sources events reads from a listener started with --store. Without --store, it fails with store_disabled. With --json, each line is an accepted event exactly as the SDK sent it, the same lines the listener prints on stdout. When more events are left than --limit allows, stderr prints more events: continue with --since N, and stdout stays pure.

Examples

  • The properties of one event:
bash
rudder-cli sources events --url http://127.0.0.1:4321 --event 'Order Completed' --fields properties --json
  • A table of what arrived in the last five minutes:
bash
rudder-cli sources events --url http://127.0.0.1:4321 --since 5m
  • Events without the auto-collected context:
bash
rudder-cli sources events --url http://127.0.0.1:4321 --view compact --json

Query API

A listener started with --store serves a read-only HTTP API on the same port, under /_dev/v1/. Use it from a test process in any language.

RouteDescription
GET /_dev/v1/The index: every route with its parameters and an example URL.
GET /_dev/v1/infoThe listener’s identity, current cursor, and store statistics. Use it as a readiness probe.
GET /_dev/v1/eventsThe stored accepted events as NDJSON. It takes the same parameters as sources events: since, event, type, userId, anonymousId, writeKey, limit, view, fields, serverId.
GET /_dev/v1/requestsThe request records after since: status, outcome, the reason a request was refused, six request headers, the raw body, and the response.

Filter with kind (ingestion by default, control, or all), statusCode (such as 401 or 4xx), failed, writeKey, messageId, and serverId.

Shape the page with limit (0 to 1,000, default 100), view (list by default, compact, or full), fields, and maxBytes (default 24,000 bytes; 0 turns the cap off). When maxBytes cuts the page, truncated.next is the command that reads the rest.
GET /_dev/v1/requests/{seq}One request record.
GET /_dev/v1/guideThe user guide that rudder-cli sources --help prints.
GET /_dev/v1/events?view=countsCounts of the requests and events after since, and a diagnosis of why events are missing: codes such as nothing_received, filtered_empty, preflight_only, sdk_config_rejected, sdk_loaded_no_events, and all_accepted, each with a next command.

The counts follow the event filters. The diagnosis reads every request after since, so a filter that matches nothing shows as filtered_empty, not as nothing_received.

Add wait (up to 110s) and min to hold the answer until at least min matching events arrive. limit and fields do not apply.

Every GET route also answers HEAD.

The query API has no authentication. It accepts only requests whose Host is localhost, an address in 127.0.0.0/8, the bound IP, or a name passed with --allow-host. It sends no CORS headers and refuses cross-site browser requests, so a web page cannot read captured events. Call it from a shell or a test process, never from inside the page under test.

Records never keep Authorization or cookie headers. A write key longer than 8 characters appears masked.

Review page

A listener started with --store serves a read-only web page at the ui URL from the ready line. It shows the captured events and requests, filters by event name and write key, searches payload values, and explains why events are missing. It loads nothing from the network.

Limits and differences from RudderStack

A passing check is strong evidence that the app instruments events correctly, not a guarantee. The listener answers with RudderStack’s status codes and messages, and adds limits of its own:

  • A request body is at most 2,048,000 bytes, raw and decoded, and a batch has at most 10,000 events. Larger requests get 413.
  • At most 8 requests are read at once. More get 503. A body that takes over 10 seconds to arrive is refused.
  • With --store, the listener keeps the last 10,000 requests or 64 MiB, and drops the oldest whole requests first.
  • It does not check the 32 KB per-event limit or the JSON depth limit.
  • It does not deliver events, run transformations, or validate events against a Tracking Plan.
  • If a reader stops reading stdout, the SDK’s request waits until it reads again.

Error codes

Errors print on stderr with a Next: command that fixes them. With --json, an error is one JSON object with error.code and error.next. Every command exits with code 0 on success and 1 on an error.

CodeCauseFix
experimental_disabledExperimental mode or the sourcesListen flag is off.Enable the experimental flag.
port_in_useThe fixed --port is taken.Use another port, or --port 0.
store_disabledsources events or the query API reached a listener started without --store.Restart the listener with --store, or read its stdout.
server_unreachableNo listener answers at --url, or something else answered there.Check the url in the ready line.
server_changedAnother or a restarted listener answers at --url, and you passed --server-id.Read the new ready line and repeat the action.
usageA bad flag, value, or combination, such as --limit 0 or --fields with --view.Run the command with --help.
host_not_allowedA query API request with a Host the listener does not accept.Call it as 127.0.0.1:<port> or localhost:<port>, or start the listener with --allow-host <name>.
browser_originA web page on another site called the query API.Call the API from a shell or a test process.
listen_failedThe listener could not listen on the address, or stopped serving, for a reason other than a taken port. For example, the --bind IP is not on this machine.Use a --bind address the machine owns, such as 127.0.0.1.

See more

Questions? Let's figure it out together.

Join the RudderStack Slack community to connect with other users, customers, and the RudderStack team — or reach out for direct support.