Valet|Docs

CLI: Channels

Channels are the entry points for messages into your agents. Each channel has a type (webhook, slack, telegram, email, mcp, cron, heartbeat) and belongs to exactly one agent with a configurable session strategy.

Browse the channel catalog

# List all catalog entries
valet channels catalog

# Show details for a specific entry
valet channels catalog get <name>
$ valet channels catalog
github-webhook  webhook   WEBHOOK_SECRET  GitHub webhook (HMAC-SHA256)
slack-events    webhook   WEBHOOK_SECRET  Slack Events API

Create from catalog

valet channels create <entry> [--agent <agent>] [--org <org>] [--as <name>]

If the argument matches a catalog entry, provisions a channel using that entry's default configuration. Managed secrets are auto-generated for any secret slots the entry defines. Use --as to give the channel a custom name.

Create a webhook channel

valet channels create webhook [name] \
  [--agent <agent>] [--org <org>] \
  [--verify <scheme>] [--secret-name <NAME>] [--signature-header <header>] \
  [--delivery-key-header <header>] [--delivery-key-path <path>]

Generates a webhook URL and a managed signing secret used to verify inbound requests. The managed secret is stored as a secret-kind env var (valet env) and can be rotated with valet env set. When --secret-name is provided, the channel references an existing secret by that name instead of auto-generating one — use this for services like Stripe or Svix where you control the signing key.

Verification schemes:

  • hmac-sha256 (default) — HMAC-SHA256 of the raw body. Default header: X-Webhook-Signature; change it with --signature-header.
  • stripe — HMAC-SHA256 of {timestamp}.{body}, parsed from the Stripe-Signature header. Use --signature-header for providers that use the same format with a different header (e.g. WorkOS). Requires --secret-name.
  • mercury — Same HMAC-SHA256 envelope as stripe with the Mercury-Signature header. Requires --secret-name.
  • svix — HMAC-SHA256 of {msg_id}.{timestamp}.{body}; reads the svix-* headers, falling back to webhook-* (standard-webhooks senders). Requires --secret-name.
  • slack — Slack Events API verification: HMAC-SHA256 of v0:{timestamp}:{body}, reading X-Slack-Request-Timestamp and X-Slack-Signature. Handles url_verification challenges automatically. Requires --secret-name.
  • static-token — Constant-time comparison of a bearer token.
  • none — No verification. Use for testing or trusted networks.

Use --delivery-key-header (e.g. X-GitHub-Delivery) or --delivery-key-path (e.g. event.id) to deduplicate deliveries by a unique ID.

# Default hmac-sha256 verification (auto-generates managed secret)
$ valet channels create webhook my-channel
Creating webhook channel on my-agent agent in acme org... done, my-channel
  type:     webhook
  url:      https://gateway.valet.dev/webhook/abc123
  secret:   whsec_abc123
  managed:  MY_CHANNEL_WEBHOOK_SECRET
  verify:   hmac-sha256 (X-Webhook-Signature)
  agent:    my-agent
  strategy: per_invocation
  prompt:   channels/my-channel.md

# Stripe webhooks with a managed secret reference
$ valet channels create webhook stripe-events \
    --verify stripe \
    --secret-name STRIPE_WEBHOOK_SECRET

Create a Slack channel

valet channels create slack [name] [--agent <agent>] [--org <org>] \
  [--bot-name <display-name>]

Setup is two-phase:

  • Per-org — run with --org to connect Slack to the org. The CLI prompts for a Slack app configuration token and refresh token (generate them at https://api.slack.com/apps under "Your App Configuration Tokens").
  • Per-agent — run with --agent (or from a linked project directory) to create a dedicated Slack app for that agent. Requires an existing org-level Slack connection. Use --bot-name to customize the bot display name (default: the agent name).
# Connect the org to Slack first (prompts for config token + refresh token)
$ valet channels create slack --org acme
Slack app configuration token: ********
Slack app refresh token: ********
Connected to Acme (T12345)

# Then create a per-agent Slack channel
$ valet channels create slack my-slack-bot --bot-name support-bot
Checking Slack connection... connected (Acme)
Creating slack channel on my-agent agent in acme org... done, my-slack-bot
  type:     slack
  bot:      @support-bot
  workspace: Acme (T12345)
  agent:    my-agent
  strategy: per_conversation
  prompt:   channels/my-slack-bot.md

Slack activity levels

valet channels slack [--agent <agent>]
valet channels slack add <#conversation>
valet channels slack set <#conversation> <level>
valet channels slack set-default <level>

An activity level controls what wakes the agent in a conversation: mentions (only @-mentions), emails (@-mentions and forwarded emails), or all (every message). The channel default is a template stamped onto each conversation when the bot joins it; changing the default never re-points conversations the bot is already in. Running valet channels slack with no subcommand lists the bot's conversations with each conversation's effective level.

Create a Telegram channel

valet channels create telegram [name] [--agent <agent>]

After creating the channel, connect it by visiting the Telegram deep link (a t.me URL) shown in the output.

Create an email channel

valet channels create email [name] [--agent <agent>] \
  [--local-part <part>] [--mailbox <address>] [--sender-policy open|allowlist|org]

Creates an email channel backed by a @valet.run mailbox. By default a new mailbox is minted with a randomly generated address in the org's namespace. Pass --local-part to choose the address's local part, or --mailbox to bind the exact existing address of an org-owned mailbox. The --sender-policy flag controls which senders may reach the agent: open (any sender passing the gates), allowlist, or org (org members only). An agent may have several named email channels, each bound to a distinct mailbox and prompt.

$ valet channels create email inbox
Creating email channel on my-agent agent in acme org... done, inbox
  type:     email
  address:  wandering-brook-7h3k@acme.valet.run
  policy:   open
  agent:    my-agent
  strategy: persistent
  prompt:   channels/inbox.md

Create an MCP channel

valet channels create mcp [name] [--agent <agent>]

Exposes the agent as an MCP server over Streamable HTTP at the endpoint URL shown in the output. It exposes a built-in ask_<agent> tool that takes a message and returns the agent's response. Define additional tools by adding mcp/<tool>.md files to the agent.

MCP channel tokens

valet channels token mint <channel> <token-name> \
  [--tools <names>] [--expires <duration|timestamp>] [--rate-limit <per-minute>]
valet channels token list <channel>
valet channels token revoke <channel> <token-name>

Clients authenticate with bearer credentials minted via valet channels token mint. The plaintext token is printed exactly once; store it securely. Use --tools to narrow the credential to specific tools, --expires to set an expiry, and --rate-limit to cap tool calls per minute. Revocation is immediate and permanent.

Create a cron channel

# Human-readable schedule
valet channels create cron daily-report --schedule "every day at 9:00am"

# Raw crontab expression
valet channels create cron weekly --cron "0 9 * * 1" --timezone US/Eastern

Provide either --schedule with a human-readable description or --cron with a raw crontab expression. The two flags are mutually exclusive. Use --timezone to specify an IANA timezone (default: UTC).

Create a heartbeat channel

valet channels create heartbeat health-check --every 5m

Triggers at a fixed interval. The --every flag is required and specifies the interval (e.g. 5m, 1h, 30s).

Attach and detach

valet channels attach <channel-name> [--agent <agent>] [--as <alias>] \
  [--events <types>] [--bot-name <name>]
valet channels detach <alias> [--agent <agent>] [--force]

Attach an org-scoped channel to an agent. Use --events to filter which event types are routed to the agent (comma-separated). The --as flag gives the attachment a custom alias. For Slack channels, attaching provisions a per-agent Slack bot with its own identity in the workspace; detaching destroys it (use --force to skip the confirmation prompt). Detaching never deletes the org channel itself.

Reauthorize a channel

valet channels reauthorize <name> [--agent <agent>] [--print-url]

Mints a fresh authorization URL for a channel that holds third-party credentials and opens it in your browser. For Slack channels, this pushes the current bot scopes to the Slack app's manifest and returns the reinstall URL. For Telegram channels, it regenerates the deep link. Channels with no credentials (cron, heartbeat, webhook) cannot be reauthorized. Use --print-url to print the URL instead of opening a browser (for SSH or CI).

List channels

valet channels [--org <org>] [--agent <agent>]

Lists channels visible to you. Resolution order when no flags are provided: linked agent in the current directory, then the default org.

Channel info

valet channels info <name> [-a <agent>] [-o <org>]

Shows the channel's type, webhook URL, signing secret, managed secret name, verification scheme, status, owning org or agent, session strategy, and prompt path.

Destroy a channel

valet channels destroy <name> [-a <agent>] [-o <org>]

Permanently removes the channel. Cannot be undone. Destroying an org-level Slack channel cascades: all per-agent Slack bots are destroyed first, then the org Slack connection is removed.