On this page

Alert your team when a target account appears

The job: you have a list of accounts you care about. When someone from one of them reads your site, you want to know within minutes — in the channel your team already watches, with enough context to act.

Who this is for: anyone with a named-account list. ABM, enterprise sales, partnerships.

What it costs you: one webhook endpoint and about thirty lines.


The shape of it

Signal pushes every newly identified visitor to your endpoint. You decide whether it matters. Most don't — that filter is the whole job, and it belongs on your side, because only you know your target list.

Signal  --POST-->  your endpoint  --filter-->  Slack

Register the endpoint once, from the dashboard (Exports & API → Webhooks) or by asking the assistant. You get a signing secret exactly once.

Receiving it

import hmac, hashlib, os, json
from flask import Flask, request, abort

app = Flask(__name__)
SECRET = os.environ["SIGNAL_WEBHOOK_SECRET"].encode()
TARGETS = {"acme.com", "globex.com", "initech.com"}
seen = set()  # use Redis in production

@app.post("/signal")
def receive():
    raw = request.get_data()
    sent = request.headers.get("X-Signal-Signature", "")
    expected = hmac.new(SECRET, raw, hashlib.sha256).hexdigest()
    if not hmac.compare_digest(sent, expected):
        abort(401)

    # Deliveries retry. X-Signal-Delivery is stable for a given event, so it
    # is the right dedupe key — see "What will go wrong" below.
    delivery = request.headers["X-Signal-Delivery"]
    if delivery in seen:
        return "", 200
    seen.add(delivery)

    v = json.loads(raw)
    if v.get("company_domain") in TARGETS:
        notify(v)
    return "", 200

Making the alert worth reading

A name and a domain is not enough for a rep to act. Add what they read and how warm they are, both of which you already have:

def notify(v):
    hot = v["intent_score"] >= 70
    lines = [
        f"*{v['company_name'] or v['company_domain']}* — {v['resolved_name'] or v['resolved_email']}",
        f"{v.get('resolved_title') or 'role unknown'} · intent {v['intent_score']}"
        + ("  🔥" if hot else ""),
        f"{v['visit_count']} visits, first seen {v['first_visit_at'][:10]}",
    ]
    post_to_slack("\n".join(lines))

Letting an LLM write the message

The fields above are a summary, not a briefing. If you want the alert to say why this person is worth a call, hand the record to a model and ask:

You are helping a salesperson decide whether to reach out right now.

Here is a visitor Signal just identified:
{visitor_json}

In no more than three sentences: who they appear to be, what their behaviour
suggests they are evaluating, and one specific opening line that references
something real from the data. If the data does not support a confident read,
say so instead of inventing one.

That last sentence matters more than it looks. Without it a model will write a confident opener from intent_score: 12 and a single pageview.

What will go wrong

Duplicate alerts. Deliveries retry — a failed POST is retried with doubling backoff for about 21 hours. X-Signal-Delivery is stable across those attempts; the payload is not a safe dedupe key. Store the delivery id.

A quiet channel, then a flood. Identification runs in batches, so twenty visitors can arrive in one minute after an hour of nothing. Batch your Slack posts or you will be rate-limited by Slack, not by us.

People who are not buyers. Consumer mailbox domains roll up under a single personal account. If your alert list is B2B, filter company_domain != "personal" or you will page your reps about gmail users.


Next: score and route inbound leads · receive identified visitors by webhook

This page is maintained next to the service it describes and rendered here. Read it as markdown if you are pointing an agent at it.