Channels

Channels are the inbound sources — webhooks, email, Slack, or cron schedules — that feed events into your project.

A channel is an inbound source attached to a project. Each channel has a type, a label, and a unique address or configuration Nevo manages for it. Every channel type emits events through the same SDK + endpoint surface.

Types

webhook

Nevo generates https://<token>.in.nevo.sh. Point any third-party webhook there (Stripe, GitHub, Linear, whatever). Produces webhook.received events.

  • Secret: each channel gets a signing secret; Nevo HMAC-signs outbound delivery to your endpoints with it.
  • Auth on inbound: an API key bearer token is required on POST.

email

Nevo generates <token>@in.nevo.sh. Forward any mailbox there (e.g. a support alias) and the inbound mail becomes email.received events.

  • Enrichment: the full body (text + HTML) is fetched and attached before the event lands in your handler.
  • Replies thread into the original conversation via RFC 5322 In-Reply-To + References.

slack

A Slack channel is a subscription against a Connection — the OAuth install to a Slack workspace. One connection can back many subscriptions; each subscription carries a kind describing which slice of the workspace’s events it listens to.

  • Install first, subscribe second. Connect a Slack workspace from the Connections page, then add one or more channels against it from Channels → new channel → slack.
  • Subscription kinds today:
    • { "kind": "mention" } — only events where the bot is @mentioned. One per connection.
    • { "kind": "channel_messages", "slack_channel_id": "C01…" } — every non-bot message in one specific Slack channel. One per (connection, slack channel) pair. Requires inviting the bot to the channel in Slack (/invite @nevo).
  • One workspace per project. The underlying connection enforces this globally — installing the same workspace into two Nevo projects returns a “workspace taken” error.
  • Replies. await event.reply(text="…") posts a threaded message via chat.postMessage back into the original channel/thread.
  • Scope upgrade. The default install grants @mention scope only. Creating a channel_messages subscription needs additional Slack permissions; the dashboard detects the gap and offers a one-click reauthorize. See Connections → Reauthorize.
  • Delete. Deleting a Slack channel (subscription) just removes that listener; the connection and bot token stay so other subscriptions keep working. To actually uninstall the bot, delete the connection from the connections page.

cron

A schedule that fires events on a cadence — useful for waking an agent on an interval. Produces cron.fired events.

  • Expression: standard 5-field Unix cron (minute hour dom month dow). Seconds are not supported — minute resolution only. @hourly, @daily, @weekly descriptors are accepted.
  • Presets: the dashboard ships quick-pick presets — every hour, every day at 9am, weekdays at 9am, every thursday 2pm, first of month 00:00 — for people who don’t want to reach for the five-field syntax.
  • Timezone: IANA name (America/New_York, Africa/Lagos, etc.). 0 14 * * THU in America/New_York fires at 14:00 NYC time, which is 18:00 UTC — Nevo stores the computed UTC instant and recomputes the next occurrence in your timezone after each firing.
  • Payload: an optional static JSON object attached to every firing. Reachable in your handler via event.data.payload.
  • Payload templates: string values in the payload can contain {{token}} markers that are substituted at fire time. See Template tokens below.
  • Minimum interval: 1 minute today. Scheduler ticks once a minute; sub-minute cadence isn’t meaningful at this resolution.
  • Concurrency: each tick fires exactly once across workers. If your handler takes longer than the interval, the next firing still arrives on schedule. Use the event id as an idempotency key if that matters to you.

Cron template tokens

Embed any of these tokens inside a string value in the payload JSON and Nevo will substitute them when the schedule fires:

TokenWhat gets substituted
{{fired_at}}RFC 3339 UTC timestamp when the scheduler ran.
{{scheduled_at}}RFC 3339 UTC timestamp the tick was scheduled for.
{{iso_date}}YYYY-MM-DD in UTC.
{{iso_time}}HH:MM:SS in UTC.
{{local_iso_date}}YYYY-MM-DD in the schedule’s timezone.
{{local_iso_time}}HH:MM:SS in the schedule’s timezone.
{{epoch_seconds}}Unix timestamp (integer).
{{epoch_ms}}Unix milliseconds (integer).
{{uuid}}A fresh UUID v4 — handy as an idempotency key.

Example payload:

{
  "run_id":    "daily-digest-{{local_iso_date}}",
  "key":       "{{uuid}}",
  "t_scheduled": "{{scheduled_at}}",
  "t_fired":     "{{fired_at}}"
}

Only string values are substituted. Tokens inside numbers/booleans aren’t touched. Unknown tokens are left verbatim, so {{my_literal}} passes through untouched if it doesn’t collide with a reserved name.

Coming soon

  • discord — Discord bot events
  • telegram — Telegram bot events
  • s2 streams — S2 stream subscriptions
  • Custom domains — bring your own ingest.yourdomain.com for webhook and email channels (enterprise)

Common fields

All channels share these fields on the dashboard + API:

  • id — UUID; stable across the channel’s lifetime.
  • type — one of the supported kinds above.
  • label — display name. Shows on events in the dashboard; for Slack channels this is the workspace name.
  • statusactive or disabled. Disabling pauses both inbound ingest and (for cron) scheduled firings.
  • lastEventAt / eventCount30d — stats for the dashboard.

Type-specific fields

TypeExtra fields
webhookurl, signingSecret
emailemailAddress
slackconnectionId, subscription (see Connections)
croncronExpr, cronTimezone, cronNextFireAt

url, emailAddress, and signingSecret are server-minted at create time and stable for the channel’s lifetime. If you need a different inbound URL or address, create a new channel and delete the old.

Rotating a signing secret is done from the channel’s page in the dashboard: open the channel, click Rotate secret, and the new value appears once on screen. Copy it into your HMAC-verification code before closing the dialog — the previous secret stops working immediately, and Nevo doesn’t retain the plaintext.

Plan availability

Tierwebhookemailcronslack
Hobby
Hacker
Pro
Scale
Enterprise

Slack is a Pro+ feature — it needs an OAuth-installed workspace app, which is a heavier-weight surface than the single-URL channels. See Billing for full plan details.