---
title: "Emit and react to signals"
description: "Emit an app signal when something needs attention, listen for built-in and your own signals, and read what was recorded with mainsdk signals."
section: "guides"
status: "Building for 0.4"
spec: "signals"
canonical: "https://mainsdk.dev/docs/guides/react-to-signals"
updated: "2026-10-28"
---

# Emit and react to signals

> Emit an app signal when something needs attention, listen for built-in and your own signals, and read what was recorded with mainsdk signals.

Status: Building for 0.4 · Canonical: https://mainsdk.dev/docs/guides/react-to-signals · Updated: 2026-10-28

You will emit one signal, listen for it and read it from the command line. It takes about 10 minutes. Signals are how the parts of an app, and the playbooks that act on them, say that something changed.

## You need

- A MainSDK app. For app-wide signals across instances, a `signals.key` secret.

## 1. Emit a signal

```ts title="app/jobs/import.ts"
import { signals } from "mainsdk";

await signals.emit("app.import_failed", { severity: "high", subject: "import:nightly", data: { rows: 3 } });
```

Your names start with `app.`. A repeat of a live signal folds into the first and is delivered again only when the severity rises.

## 2. Listen

```ts title="app/signals.ts"
import { signals } from "mainsdk";

const stop = signals.on("pressure.*", (s) => console.log(s.name, s.severity, s.trust), { min: "medium" });
```

`on` takes a name, a `prefix.*` glob or `*`, and returns an unsubscribe function. Handlers run off the request path.

## 3. Share it across instances

```yaml title="main.yaml"
signals:
  key: secret:MAINSDK_SIGNAL_KEY
```

Then emit with `scope: "app"`. Without `key`, app-scope signals are unsigned, and `mainsdk check --env prod` reports `MAIN-SIGNAL-0007` on a shared broadcast.

## 4. Read what was recorded

```bash
npx mainsdk signals --min medium
npx mainsdk signals --name "pressure.*" --json
npx mainsdk signals --follow
```

## Check it worked

`npx mainsdk signals` lists `app.import_failed` with its severity, subject, count and age.

## Troubleshooting

- `MAIN-SIGNAL-0001`: the name, severity or data was refused. Data is at most 4 KB.
- `MAIN-SIGNAL-0002`: an attribute looked personal or secret and was dropped.
- `MAIN-SIGNAL-0005`: a handler threw or ran over one second.

See the [signals interface](https://mainsdk.dev/docs/interfaces/signals), and [respond to attacks and pressure](https://mainsdk.dev/docs/guides/respond-to-attacks-and-pressure) to act on signals.
