SemiLayerDocs

Chat notifications

SemiLayer can post any of its system notifications — ingest completions, quota warnings, billing changes, member invites, and more — directly into a Slack channel or Discord channel. Opt-in only. Installing the bot does nothing until somebody runs /semilayer notify on <type> inside a specific channel.

This page covers what the surface looks like, how to wire it up, and the rules around mandatory types and channel cleanup.

ℹ️

This is the second product surface in the SemiLayer ChatOps integration. The first is identity linking (/semilayer login), which most teams set up once. After that, every notification subscription is per-channel and admin-gated.

What you can subscribe to

16 notification types are supported in chat today. Their data shapes are identical to email + in-app, only the rendering differs.

TypeWhen it firesMandatory?
welcomeMember joins SemiLayeryes
member.invitedAdmin invites a new memberyes
member.joinedInvited member accepts
ingest.completedIngest run finishes successfully
ingest.failedIngest run dead-letters
quota.warningOrg crosses 80% of a metered quota
quota.graceQuota exceeded but in grace period
quota.exceededQuota fully exceeded — hard stopyes
quota.resetCycle rollover restored quota
quota.restoredAdmin lifted a per-org override
feedback.receivedCustomer submitted feedbackyes
admin.daily_digestDaily ops summary for platform adminsyes
billing.payment_failedStripe charge failedyes
billing.plan_changedOrg switched plan tier
billing.subscription_canceledSubscription canceledyes
billing.cycle_resetNew billing cycle started

Mandatory types fire on critical account state. They cannot be turned off individually — /semilayer notify off quota.exceeded is rejected with a pointer at notify none (which clears every subscription on the channel, mandatory or not).

Run /semilayer notify types from any channel to print the same list inline.

Setup

The end-to-end story takes three steps.

1. Connect a workspace

In Console → Integrations, click Add Slack workspace or Add Discord workspace and complete the OAuth popup. The chosen workspace is bound to the current org. Only org admins can install or revoke.

From any channel where the bot is installed, run:

/semilayer login

You'll get a one-time link to your browser, sign in with the same identity you use for Console, and the chat user is bound to your SemiLayer member. This is per-user, per-workspace, and only needs to happen once. Required for every command including notify.

3. Subscribe a channel

Invite the bot to the channel (Slack: /invite @semilayer; Discord: add to the channel role), then run any of:

/semilayer notify on ingest.failed
/semilayer notify list
/semilayer notify off ingest.failed
/semilayer notify all              ← preview-only by default
/semilayer notify all confirm      ← actually subscribes
/semilayer notify none             ← clears every subscription on this channel
/semilayer notify types

That's it — from this point the channel receives a Block Kit (Slack) or embed (Discord) every time a matching notification fires for the org bound to the workspace. The bot only posts in channels that have an explicit subscription. No auto-add, no shadow channels.

Permission rules

/semilayer notify has a three-layer permission gate, in order:

  1. Workspace must be installed. Bot in a channel with no workspace row returns "this workspace isn't connected — run the install flow in Console first."
  2. Chat user must be linked. Anyone unlinked gets the "Run /semilayer login" pointer.
  3. State changes need admin role. notify on/off/all/none require the linked SemiLayer user to be owner or admin on the org. notify list and notify types are read-only and any linked member can run them.

DMs are explicitly rejected with "run this inside a channel, not in DMs" — there's no concept of a "DM subscription" in v1.

Multiple channels, multiple subscriptions

Two channels in the same workspace can subscribe to the same type — both will receive the post. One channel can subscribe to many types. There's no de-dup across channels: if #alerts and #ingest-ops both subscribe to ingest.failed, both get the message.

Channel renames & deletes

  • Renames are picked up by a weekly background sweep that asks the provider for the canonical channel name. The freshest name is what shows in notify list. If you renamed the channel today, the next sweep will reflect it within 7 days; the subscription continues working in the meantime because it's keyed on the channel ID, not the name.
  • Deletes self-heal: the first post that comes back with channel_not_found (Slack) or 10003/Unknown Channel (Discord) drops the subscription rows for that channel. The notification itself isn't lost — it still goes to email + in-app per your preferences.

Workspace revoke

Disconnecting a workspace from Console → Integrations → ⋯ → Disconnect sets revokedAt on the workspace row. The fanout query joins on revokedAt IS NULL, so revoking a workspace silences every channel subscription under it instantly — no need to clear them one by one.

How it interacts with email & in-app preferences

Chat is a separate channel from email + in-app. Toggling /semilayer notify on ingest.failed for #alerts does not affect your personal email or in-app preferences for that type, and vice versa. The fanout to chat is gated only by chat_subscriptions rows; the per-user notification_preferences table is consulted only for email + in-app.

Brand & rendering

Each type renders with a fixed severity color so the channel signal is scannable at a glance:

SeverityColor hexUsed by
Error#dc2626ingest.failed, quota.exceeded, billing.*
Warning#f59e0bquota.warning, quota.grace
Success#10b981ingest.completed, quota.reset, quota.restored
Primary#8B5CF6welcome, member.invited, admin.daily_digest
Secondary#3B82F6member.joined, feedback.received

Slack messages prefix the header with a severity emoji (Block Kit's modern equivalent of the deprecated attachments color). Discord embeds set the embed color field to the integer form of the same hex. Every message ends with an "Open in Console" deep-link to the relevant route (e.g. an ingest.failed post links to the failed ingest run).

FAQ

Can two channels in different workspaces subscribe to the same type? Yes — each workspace binds to its own org, so the subscription is org-scoped.

Can I subscribe to a notification for a different org than the one bound to the workspace? No. v1 ties the workspace to one org; notifications fire org-scoped and only post into that org's workspaces.

What happens if the bot is in the channel but I run notify on without linking first? You get the "Run /semilayer login" pointer. The subscription is not created until you're linked + admin.

Does notify on count against my API quota? Each post the bot makes counts as one billable request against the org's metered API quota — same as email and webhook deliveries.

Can I see who subscribed a channel? Today the audit trail lives on chat_subscriptions.created_at. A first-class admin view of "who subscribed what" lands with Phase 5.

Troubleshooting

  • /semilayer notify on returns "Only org admins can manage…" — Your chat user is linked but maps to a non-admin SemiLayer member. Ask an org admin to either run the command or promote you in Console → Team.
  • Bot doesn't post in a private channel. Slack requires the bot to be invited (/invite @semilayer). After inviting, retry the notification trigger — the next post will land.
  • notify list shows a stale channel name. Names refresh weekly. The subscription itself works; only the displayed name lags.
  • Posts stopped arriving after I renamed/archived the channel. The next failed post drops the subscription. Re-run notify on <type> from the new channel.