Act on live customer behaviour.
Signals tells you what your customer is doing right now so your product can respond while they are still on the page.
trackPageView(); // browser tracker, already on this pageAttribute(name="pages_last_5_min", events=["page_view"], aggregation="category_count", property="page_urlpath", period=timedelta(minutes=5))
await signals.getServiceAttributes({ name: "signals_site_session_service", attribute_key: "domain_sessionid", identifier: "…", // your session id, once set });
6ms p50 · 10ms p95, in-region.
Runs in our cloud or yours, AWS or GCP. Scales with your traffic.
Track a shopper, a session, a basket, a store, whatever you decide. How often, how recently, over any window. One definition, live and historical.
Your data knows. Your product doesn't.
Behaviour is collected. Nothing computes it, serves it, or flags it fast enough to act inside the session. Every personalisation project has two halves, knowing and acting. Signals does the knowing, in milliseconds, and hands it to whatever you use for acting.
"Our recommender doesn't know what the user did thirty seconds ago."
"We built a profile API. Now two engineers maintain it forever and it pages us."
"Our data scientists build features in notebooks that never make it to production."
"Our support agent has amnesia. It can't see the customer has been stuck on the same page for four minutes."
One event in. One answer out. Same session.
One definition runs in a streaming engine for now and a batch engine for warehouse history, so production features match what data science trained on. Managed SaaS or private deployment in AWS, GCP or Azure, identical architecture.
Views, searches, adds, abandons. The SDK you already run sends the event.
view_price · 3× in 10 minutesRecomputes what you defined, on the stream. One definition, live and against history.
showing_price_hesitation = trueReads it mid-render, or a trigger pushes it wherever acts on it.
Ranker moves value options upThe knowing half of three projects that usually stall on real-time data.
In-session personalisation and recommendations
Rank against what this customer is doing now, not last night's segment. Signals supplies the attributes; your ranker decides.
showing_price_hesitation = true02Acting on live behaviour
A condition fires the instant it is met: high-value basket idle ten minutes, third failed search. Delivered to your app or wherever acts on it.
basket_idle_seconds > 600 → fire03Customer-aware agents
Hand an agent this customer's recent activity in a form a model can use, so it already knows they have been stuck on the same page for four minutes.
agentic context: JSON or narrativeCreate your first live attribute today.
Add the Signals plugin or MCP server. Run one prompt.
Claude Code, Cursor or any MCP client. The agent adds the SDK, defines the attribute and wires the read into your app.
$ claude mcp add snowplow-signals -- npx @snowplow/signals-mcp> Add Snowplow Signals to this app. Track product views, define showing_price_hesitation as 3+ view_price events in 10 minutes keyed on user_id, and read it back before the ranker runs.
Add the SDK and Signals starts collecting.
Behavioural events flow in with schema validation. No event stream to build first.
Say what you want to know: how often, how recently, in what order.
An attribute is a computed property of whatever you key on. Console or code, deployed through CI/CD.
# illustrative
Attribute(
name="showing_price_hesitation",
events=["view_price", "remove_from_basket"],
window="10m", key="user_id",
)Your app asks Signals. Or a rule fires the moment it happens.
Attributes served at 6ms p50. Real-time triggers evaluate conditions continuously and deliver to your app, Braze, Kafka, webhooks or Pub/Sub.
See an attribute updating against your own traffic in the first session.
14 days. no card, no sales call. Add the SDK, define one attribute, watch it change while you click around your own product.
Talk to an engineer means an engineer: architecture, latency boundary, failure modes, how it runs in your cloud. Not a demo script.