Skip to content

Notifications ​

Zenavo emails your team when something needs attention, so you don't have to keep the console open, and can post the same alerts to Slack, Discord, Telegram or your own webhook.

Events ​

EventWhen it is sent
Deployment failedA deploy or rollback did not complete. The email says whether it failed while building a service or while rolling out.
Service suspendedA service restarted more often than its crash limit and was stopped.
Server offlineA server missed 3 status checks in a row (about 3 minutes). A second email follows when it answers again.
Disk or memory almost fullA server's disk is above 90%, or its memory stays above 90% for 3 checks in a row. It can alert again after usage drops below 85%.

Each alert goes out at most once an hour for the same project, service or server. A short blip of one or two missed checks sends nothing.

Security emails about your own account (new-device sign-ins, approved CLI sign-ins, new API keys) are described in Security. They go only to you, are on by default for every role, and appear under Security on the Notifications page.

Who gets them ​

Emails go to members of the team that owns the project or server:

  • Owners and admins get every event until they turn it off.
  • Members get nothing until they turn events on.
  • Viewers never get notifications.

Choose your events under Account settings → Notifications. The choice is per account and applies to every team you belong to.

What an email contains ​

The project, service or server name, the time (UTC) and a link to it in the console. Emails never contain variable values, secrets, commands or log text. Open the console to read the logs.

Channels ​

Team admins can send the team's notifications to chat apps and webhooks as well: Account settings → Notifications → Team channels, for the team selected in the sidebar. Each channel chooses its events; besides the ones above, channels can receive Deployment succeeded (never emailed). Channels get the same alerts as email, with the same once-an-hour limit, and don't depend on anyone's email settings.

ChannelWhat you need
SlackAn incoming webhook URL for the channel (https://hooks.slack.com/services/…).
DiscordA channel webhook: channel settings → Integrations → Webhooks (https://discord.com/api/webhooks/…).
TelegramA bot token from @BotFather and the chat ID (a number such as -1001234567890, or a public channel's @username). Add the bot to the chat first.
WebhookAny https:// URL. Zenavo POSTs JSON to it, signed (see below).

Test sends a message right away and shows whether it arrived. Each channel shows its last delivery, or why it failed; failed deliveries are retried three times over about 13 minutes. Up to 20 channels per team.

Webhook URLs and bot tokens are secrets: they are stored encrypted, and the console only shows their host or chat. Zenavo posts only to hosts whose addresses are all public, and does not follow redirects.

Webhook payload ​

json
{
  "type": "deploy_failed",
  "team_id": "team_…",
  "subject": "Deployment failed: shop",
  "text": "The deployment of project \"shop\" failed at 2026-10-19 08:12 UTC.\nIt failed while building service \"web\".\n",
  "url": "https://zenavo.app/projects/proj_…/deployments/job_…",
  "time": "2026-10-19T08:12:03Z"
}

type is one of deploy_failed, deploy_succeeded, service_suspended, server_offline (also sent when the server is back), server_resources, usage_limit, or test. Any 2xx answer counts as delivered.

Every request carries X-Zenavo-Event, X-Zenavo-Timestamp (Unix seconds) and X-Zenavo-Signature: v1=<hex>, the HMAC-SHA256 of <timestamp>.<raw body> with the channel's signing secret. The secret (whsec_…) is shown once when you add the webhook; Edit can't show it again, but you can rotate it through the API. Verify the signature over the raw bytes, and reject old timestamps:

js
import crypto from 'node:crypto'

function verify(rawBody, headers, secret) {
  const ts = headers['x-zenavo-timestamp']
  const expected = 'v1=' + crypto.createHmac('sha256', secret).update(`${ts}.${rawBody}`).digest('hex')
  const given = headers['x-zenavo-signature'] ?? ''
  const fresh = Math.abs(Date.now() / 1000 - Number(ts)) < 300
  return fresh && given.length === expected.length && crypto.timingSafeEqual(Buffer.from(given), Buffer.from(expected))
}

Self-hosting ​

Notifications use the same mail provider as password reset emails: set ZENAVO_SMTP_URL, or ZENAVO_RESEND_API_KEY with ZENAVO_MAIL_FROM. Without a mail provider no emails are sent, and the Notifications page says so; team channels still work. Set ZENAVO_PUBLIC_URL so emails can link to the console.

Emails and channel posts are queued and sent in the background, with retries, so a slow provider never delays deployments or status checks. The control plane needs outbound HTTPS to reach Slack, Discord, Telegram and webhook hosts.

Not affiliated with Zeabur.