# Build and debug

The CLI covers the everyday loop: configure the app, backfill data, debug and investigate. Run the commands from the terminal or a script, or ask your agent in plain language.

## Call the Stream API

Every Stream endpoint available in the API and the SDKs is exposed in the CLI:

```sh
getstream api --help
```

For example:

```sh
getstream api UpdateChannelType --name messaging --request '{
  "polls": true
}'
```

Like every app-scoped command, `api` acts on the current project's app unless `--app-id` or the `STREAM_API_KEY` and `STREAM_API_SECRET` environment variables override it.

An endpoint's path parameters (like an id or a name) become flags. The request body and query parameters are passed as JSON with `--request`. Responses are JSON, and can be queried with `--jq`. In-depth help is available for every endpoint:

```sh
getstream api UpdateChannelType --help
getstream api UpdateChannelType --schema  # for full request schema
```

### Examples

The `api` command is useful for configuring your app, backfills, debugging and investigation.

**Configure your app**

Set up channel type configuration:

```sh
getstream api UpdateChannelType --name livestream --request '{
  "max_message_length": 280
}'
```

**Backfill data**

Prepare app users. For large volumes, [import bulk data](#import-bulk-data) instead.

```sh
getstream api UpdateUsers --request '{
  "users": {
    "alice": { "id": "alice", "name": "Alice", "role": "admin" },
    "bob": { "id": "bob", "name": "Bob" }
  }
}'
```

**Debug**

Fetch a message by id. Add `--verbose` to any command to see the request and the full response.

```sh
getstream api GetMessage --id <message-id>
```

**Investigate**

Count the channels with a message since a given time:

```sh
getstream api QueryChannels --request '{
  "filter_conditions": {
    "last_message_at": { "$gt": "2026-09-22T09:00:00Z" }
  },
  "limit": 30
}' --jq '.channels | length'
```

When working with an agent, just ask:

- Has there been any chat activity in the past hour?
- Are guest users allowed to send audio on livestreams?
- Who follows the user alice?

## Mint user tokens

To create a JWT user token for development purposes:

```sh
getstream token alice --ttl 1h
```

Like every app-scoped command, `token` acts on the current project's app unless `--app-id` or the `STREAM_API_KEY` and `STREAM_API_SECRET` environment variables override it.

The token lets you impersonate any user during development. Without `--ttl` the token never expires, so prefer setting a shorter one. This command doesn't replace setting up proper [authentication](https://getstream.io/chat/docs/javascript/tokens-and-authentication/) in your app.

When working with an agent, just ask:

- Give me a token for alice that expires in an hour.
- Mint tokens for three test users.

## Test webhooks locally

When building and testing apps that rely on Stream [webhooks](https://getstream.io/docs/platform/webhooks/), it's often useful to temporarily route webhooks to your local dev server:

```sh
getstream webhook --port 3000 --path /webhooks/stream
```

Like every app-scoped command, `webhook` acts on the current project's app unless `--app-id` or the `STREAM_API_KEY` and `STREAM_API_SECRET` environment variables override it.

The command uses [Cloudflare Quick Tunnels](https://try.cloudflare.com) and [cloudflared](https://github.com/cloudflare/cloudflared) to proxy webhook events to your local server. A temporary webhook is set up in your app configuration, and it's automatically cleaned up when you exit the command with Ctrl-C or kill it.

You can set up multiple temporary webhooks listening for different events on different routes:

```sh
getstream webhook --port 3000 --path /webhooks/messages --event message.new &
messages=$!
getstream webhook --port 3000 --path /webhooks/users --event user.updated &
users=$!

# ...

kill "$messages" "$users"
wait
```

If the command is killed abruptly, it can leave a dangling webhook. To clean these up:

```sh
getstream webhook --prune
```

Webhook URLs include the unique id (`?cliwebhook`) for your CLI install. When pruning, all webhooks that were set up by your CLI are cleaned up, including the ones currently being proxied.

>
> **Warning:** Webhook event payloads will pass through the Cloudflare network. Be mindful when using this for production apps handling sensitive data.
>

When working with an agent, just ask:

- Forward message.new webhooks to my dev server on port 3000.
- Clean up any Stream webhooks left over from earlier sessions.

## Livestream from the terminal

To test a livestream, video or audio call, you can use tools like ffmpeg and OBS to publish over [RTMP](https://getstream.io/video/docs/api/streaming/rtmp/), [SRT](https://getstream.io/video/docs/api/streaming/srt/), and [WHIP](https://getstream.io/video/docs/api/streaming/whip/). The CLI generates the signed ingress URL these tools need:

```sh
ffmpeg -re -i video.mp4 -c:v libx264 -c:a aac -f mpegts \
  "$(getstream ingress srt --cid livestream:my-stream --user alice --ttl 1h)"
```

Like every app-scoped command, `ingress` acts on the current project's app unless `--app-id` or the `STREAM_API_KEY` and `STREAM_API_SECRET` environment variables override it.

The call is created if it doesn't exist, and the specified user is the publisher. Without `--ttl` the signed URL never expires, so prefer setting a shorter one.

If you're using OBS (or other GUI software), the CLI can provide copyable values for stream settings:

```sh
getstream ingress srt --cid livestream:my-stream --user alice --ttl 1h --obs # or rtmp, whip
```

When working with an agent, just ask:

- Start a test livestream into livestream:my-stream from sample.mp4.
- Give me the OBS settings to stream into my call.

## Import bulk data

To move a large dataset into your app, such as message history from another provider, use bulk import instead of calling the API record by record:

```sh
getstream import chat export.jsonl --watch
```

Like every app-scoped command, `import` acts on the current project's app unless `--app-id` or the `STREAM_API_KEY` and `STREAM_API_SECRET` environment variables override it.

For guidance on the import file format, see:

- [Importing Chat data](https://getstream.io/chat/docs/node/import/)
- [Importing Activity Feeds data](https://getstream.io/activity-feeds/docs/node/importing-data-feeds/)

The import is processed in the background. Run with `--watch` to follow it until it finishes, or check on it later:

```sh
getstream import status <task-id>
getstream import list
```

Chat imports run in upsert mode, so re-importing a file updates existing objects instead of duplicating them.

When working with an agent, just ask:

- Import export.jsonl into my chat app and tell me when it's done.
- Did my last import finish, and did anything fail?

## Look up the docs

The CLI gives your agent access to the latest Stream documentation, so it can answer questions about Stream for you. Your agent runs `getstream docs` under the hood, which keeps an up-to-date local copy of the documentation.

Just ask:

- How do I enable message reminders, and which SDKs support them?
- What's the difference between soft-deleting and hard-deleting a message?
- How do I start a livestream in backstage mode and go live later?
- How do listeners in an audio room request permission to speak?
- How do I add reactions to an activity and show their counts?
- How does ranking work for a personalized "for you" feed?
- How do I block specific words in chat messages with a blocklist?
- What happens to a message flagged by AI moderation, and where do I review it?

---

For the most recent version of this documentation, visit [https://getstream.io/cli/docs/build-and-debug/](https://getstream.io/cli/docs/build-and-debug/).