Skip to content
indraft
Start free

Cookbook

Notify a channel when a deal moves

The most-loved feature of every CRM and the one people notice within an hour of it breaking. Two ways to build it without writing a handler, and the reason the simpler one is usually right.

The situation

Somebody wins a deal and the team should know. Somebody moves a large deal backwards and you should know sooner than the forecast meeting. This is the canonical automation, it belongs nowhere near an agent, and it wants to be identical every time.

What must be true when you are done

  • The channel gets a message when a deal moves, within seconds.
  • You can prove the message came from Indraft and not from anyone else.
  • A failing endpoint is visible as a failing endpoint, not as silence.
  • An outage means late notices, not missing ones.

Subscribe to what you care about

A subscription is a URL and a list of operations. You get a signing secret back once, at creation, and it is not retrievable afterwards.

Agent

POST /v1/webhooks { "url": "https://your.app/indraft", "events": ["advance_opportunity", "create_opportunity"] }endpoint whk_79398881684a4f7fbcc6f74634e93021secret whsec_6af95c5360bc44629e90c35480a2269e

Subscribe narrowly. It is tempting to take every event and filter in your workflow, and it works, and then a year later your endpoint is being woken thousands of times a day to decide it does not care. The events are cheap to add later.

The refusal that saves you a fortnight

Event names are a published list, not free text. Subscribe to something that does not exist and you find out immediately.

Agent

refused: events.0 Invalid enum value. Expected 'advance_opportunity' | 'advance_custom_record' | 'archive' | 'restore' | 'archive_custom_record' | 'cancel_task' | 'complete_task' | ...

Every CRM webhook integration has a story about subscribing to a plausible-looking event name, waiting, and eventually discovering it was never a thing. An unconstrained string field is what makes that story possible.

Where the message actually gets built

You need something on the other end of that URL. You do not need to write it. In n8n the whole notifier is three nodes: a Webhook node that gives you the URL to register, an IF node to decide whether this one is worth saying out loud, and a Slack node.

Agent

Webhook node POST, path /indraft -> gives you the URLIF node amountMinor > 5000000Slack node post to #deals

The IF node is the one people leave out and then add three weeks later. Every deal movement in the channel is noise within a fortnight, and a channel people mute is worse than no channel.

The public URL problem, and the two honest answers

That webhook URL is on the internet. Anything can post to it, so without a check, anyone who learns the address can announce a closed deal in your Slack. Indraft signs every delivery so you can tell, and there are two ways to use that.

Check the signature. The right answer, and the one that needs a little work. The signature is computed over the raw bytes we sent, so your Webhook node has to be set to hand you the raw body rather than parsed fields, and then a Crypto node computes the same thing with your signing secret and an IF node compares them. Most n8n builds of this need one small Code node in the middle to turn the raw body into text. That is the honest cost: one node of code, once, in a recipe that otherwise has none.

Or do not take a webhook at all. Point a scheduled workflow at the change feed instead and ask what has moved since last time. There is no public URL, so there is nothing to secure, no signature to verify, and no Code node. You trade seconds for minutes.

For a Slack channel, that trade is usually the right one, and it is the version we would suggest first for anyone who does not already run webhooks. Notifications want to be timely; they rarely want to be instant, and the polled version has a property the webhook does not, which the next two sections are about.

What happens when your endpoint is down

This is the part worth understanding before you need it, and you can watch it happen rather than take it on faith.

Agent

GET /v1/webhooks/deliveriesstatus pendingattempts 1responseStatus 530error endpoint returned 530nextAttemptAt 2026-08-24T14:07:00.057Z

A delivery is queued, attempted, and retried with the reason recorded. A broken endpoint shows up as a list of failures with a status code on each, which is the difference between "Slack has been quiet" and "Slack has been quiet because our workflow has been returning 530 since Tuesday".

There is also a replay, so once you have fixed the workflow you can push an event through again rather than reconstructing what you missed.

Why the polled version is more complete, not less

Retries are bounded. If your endpoint is down long enough, some deliveries stop being retried, and a notifier that silently drops events is one you will eventually be misled by. That is true of every webhook system and it is why the simpler option above is not a downgrade.

The change feed carries a watermark. You ask what has happened since the last thing you saw, so a workflow that was off for two hours asks for two hours of changes the next time it runs and catches up by construction. Nothing is lost by being late, which is the opposite of a missed delivery.

So: webhook when seconds matter, the feed when completeness matters, and both when it is genuinely worth the effort. Notifications can usually live with fast-and-mostly. Anything feeding a report cannot. Putting the feed version on a timer is its own recipe.

Verify it

  • Move a deal and watch the channel. The obvious test, and the one people skip.
  • Post to your own URL yourself, with nonsense in it. If a message appears in the channel, anyone who finds that address can put one there too. This is the test that tells you which of the two answers above you actually built.
  • Take the endpoint down for ten minutes and bring it back. The deliveries list should show the failures, the retries, and then the success.

Run it again

Check the deliveries list monthly even when nothing seems wrong. A workflow that started returning errors after a deploy is invisible from the Slack side, because the absence of a message looks exactly like a quiet week.