Your deploy is one card, not five alerts

A deploy has five steps, so a naive pipeline sends you five notifications. By the third one you have muted the channel, and the day something actually fails, the failure is buried between a checkout ping and a build ping that never mattered.
Workflow notifications collapse the whole process into one card. Every POST that carries the same runId updates the same alert: the steps fill in live, the card turns green or red at the end, and your phone buzzes exactly twice, once when the run starts and once when it lands.
One runId, as many POSTs as you like
Send the full steps array on every POST, Zenhook replaces the card content each time. First POST with a new runId creates the card and pings you.
curl -X POST "$ZENHOOK_URL" -H "Content-Type: application/json" -d '{
"title": "api deploy",
"workflow": {
"runId": "deploy-8f2c41",
"state": "running",
"steps": [
{ "name": "checkout", "state": "done", "seconds": 4 },
{ "name": "gate", "state": "done", "seconds": 81, "info": "512 tests" },
{ "name": "build", "state": "running" },
{ "name": "deploy", "state": "pending" },
{ "name": "verify", "state": "pending" }
]
}
}'curl -X POST "$ZENHOOK_URL" -H "Content-Type: application/json" -d '{
"title": "api deploy",
"level": "success",
"workflow": {
"runId": "deploy-8f2c41",
"state": "passed",
"steps": [ /* all five, state done, with seconds */ ]
}
}'The terminal POST flips the card to its outcome and pings you once more. Two notifications for the whole run, whatever the number of steps. Updates to an existing run are not billed as new events.
Read the workflow API referenceFailure carries its own context
When a step fails, put the log tail in the workflow error field: it renders on the card, so the alert on your phone is enough to diagnose without opening a laptop. Out-of-order updates are handled too: a stale running POST that arrives after the outcome, a retry for instance, can never downgrade a finished card.
Same card everywhere
The card renders on web, iOS and Android with the same contract, and the web feed pins active runs above the day groups. If your pipeline can send an HTTP POST, it can have this: no SDK, no CI plugin.

Free to start. One channel, no card.