> ## Documentation Index
> Fetch the complete documentation index at: https://docs.awaithumans.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Channels

> How a task gets delivered to a human. Slack, email, dashboard.

A channel is "where does the human find out about the task and respond?" awaithumans ships with three:

| Channel                  | Inbox                     | Response                                  |
| ------------------------ | ------------------------- | ----------------------------------------- |
| [Dashboard](#dashboard)  | `/queue` page (always on) | Form                                      |
| [Slack](/channels/slack) | Channel post or DM        | Block Kit modal OR NL thread reply        |
| [Email](/channels/email) | Inbox message             | Action buttons OR magic-link confirmation |

You activate channels per-task via `notify=`:

```python theme={"system"}
from pydantic import BaseModel
from awaithumans import await_human_sync


class RefundRequest(BaseModel):
    order_id: str
    amount_usd: int


class RefundDecision(BaseModel):
    approved: bool


decision = await_human_sync(
    task="Approve refund?",
    payload_schema=RefundRequest,
    payload=RefundRequest(order_id="A-4721", amount_usd=250),
    response_schema=RefundDecision,
    timeout_seconds=900,
    notify=[
        "slack:#approvals",            # post to a channel
        "email:reviews@acme.com",      # email a person
    ],
)
```

The dashboard always sees the task regardless of `notify=` — the queue is the operator's source of truth.

## Notify-string format

`notify=` is a list of strings, each prefixed by the channel:

| Prefix      | Example                       | Meaning                                              |
| ----------- | ----------------------------- | ---------------------------------------------------- |
| `slack:#`   | `slack:#approvals`            | Post to Slack channel (broadcast — first-claim wins) |
| `slack:@`   | `slack:@U01ABC...`            | DM the Slack user (direct assignment)                |
| `email:`    | `email:alice@acme.com`        | Email this address                                   |
| `email+id:` | `email+acme:reviews@acme.com` | Email via the named sender identity                  |

Multiple notifications fire in parallel — slow Slack API calls don't block email.

## Dashboard

Always on. Operators log in at `/login`, see their task queue, click in to fill the form. The dashboard reads the same task data the SDK created — there's nothing channel-specific about it.

By default a non-operator user sees only tasks routed to them. Operators see everything.

```
http://localhost:3001/queue        ← operator's task queue
http://localhost:3001/task?id=...  ← single task review form
http://localhost:3001/audit?...    ← audit trail
```

## Why not webhooks-as-channels?

We deliberately don't expose "raw webhook" as a channel — `notify=["webhook:https://..."]` isn't a thing. The reason: the channel layer owns the human-facing UI generation. A webhook destination has no UI to render; you'd be implementing one ad-hoc on the receiving end. If you want webhook delivery for a system (not a human), use the `callback_url=` parameter, which fires on every terminal status transition. That's how the [Temporal](/adapters/temporal) and LangGraph adapters get their signals.

## Adding a custom channel

Channels live in `server/channels/`. A channel module exports:

* A `notify_task(task_id, task_title, notify, form_definition, ...)` function — called from `BackgroundTasks` after task creation
* A webhook route under `/api/channels/<your-channel>/...` that handles inbound messages

See `server/channels/slack/` and `server/channels/email/` for the canonical examples. New channels are a Phase-2 community contribution surface — see [the contribution guide](https://github.com/awaithumans/awaithumans/blob/main/CONTRIBUTING.md).
