Send events to Slack, Discord or your server
Add a webhook per app in Cockpitify to post new users, purchases, low balances and more to Slack, Discord or a signed JSON endpoint.
Webhooks send the same events as push notifications to a Slack or Discord channel, or to your own server as signed JSON. Each app has its own webhooks, separate from push.
What it’s good for
- A
#signupsor#revenuechannel where the whole team sees new users and purchases as they happen. - An
#alertschannel for low balances, AI limit alerts, a project that stops answering and crash spikes. - Your own backend or automation tool, to react to events: add a user to your CRM, post to another tool, start a workflow.
Get a Slack webhook URL
- Go to api.slack.com/apps and click Create New App → From scratch. Name it, for example “Cockpitify”, and pick your workspace.
- Open Incoming Webhooks and turn on Activate Incoming Webhooks.
- Click Add New Webhook to Workspace, pick the channel and click Allow.
- Copy the Webhook URL. It starts with
https://hooks.slack.com/services/.
Get a Discord webhook URL
- In Discord, hover the channel and click Edit Channel (the gear).
- Open Integrations → Webhooks → New Webhook.
- Optionally rename it and give it an avatar, then click Copy Webhook URL.
Your own server
Any public https:// address that accepts a POST with a JSON body works. Private and local addresses are refused, and redirects aren’t followed. See JSON format below.
Add a webhook
- Open Settings → Notifications → Webhooks and tap the app.
- Tap Add webhook.
- Paste the Webhook URL. Cockpitify detects whether it’s Slack, Discord or JSON.
- Optionally give it a Name.
- Choose whether to Show user names. When off, messages say “A user”.
- Tap Save and send test.
Every event starts off. Check the channel for the test message, then turn on the events you want.
Choose the events
Each event can be Off or Instant; New users can also be Hourly:
- New users
- Premium
- Balance and budget
- Daily AI limit
- Project reachability
- Usage drop
- Stability
- Daily summary
What the messages look like
- Slack: a short message with a bold title and one line of detail, for example the new user, the plan they bought or the service that runs low.
- Discord: an embed with the title, the detail, a colour per event type and the time.
- With Show user names off, people appear as “A user” in Slack, Discord and JSON alike.
JSON format
Your server receives one POST per event:
{
"id": "…",
"type": "signup",
"app": { "id": "…", "name": "Studyloop" },
"at": "2026-10-07T09:12:44.000Z",
"user": { "id": "…", "label": null },
"data": { },
"title": "…",
"text": "…"
}
typeis the event, such assignup,premium_on,premium_cancel,low_balanceorai_limit. The same value is in theX-Ops-Eventheader.userisnullfor events that aren’t about a user, such as a low balance.labelis the user’s name or email only when Show user names is on.dataholds the event’s details;titleandtextare the same words as the push notification.- Answer with any
2xxstatus within 5 seconds.
Verify the signature
Every JSON request has an X-Ops-Signature header like t=1791393931,v1=5f2c…. v1 is the HMAC-SHA256, in hex, of the timestamp, a dot and the raw body, signed with the webhook’s Signing secret (copy it from the webhook’s page). In Node.js:
import { createHmac, timingSafeEqual } from 'node:crypto'
function verify(rawBody: string, header: string, secret: string) {
const parts = Object.fromEntries(header.split(',').map((p) => p.split('=')))
const expected = createHmac('sha256', secret).update(`${parts.t}.${rawBody}`).digest('hex')
const fresh = Math.abs(Date.now() / 1000 - Number(parts.t)) < 300
return fresh && timingSafeEqual(Buffer.from(expected), Buffer.from(parts.v1 ?? ''))
}
Use the raw request body exactly as received, before parsing it as JSON.
Paused webhooks
After 10 failed deliveries in a row, a webhook pauses itself. Fix the address, then tap Resume. Cockpitify sends a test first.
Limits and permissions
- Up to 5 webhooks per app.
- Only the workspace owner or an admin can add or change webhooks.