---
title: "Classify the session without a frontier model in the render path | Snowplow Signals"
description: "A large model over the raw clickstream is slow, costly and inconsistent. Signals and Jev classify the session in a fraction of a second, with a probability."
source: https://signals.snowplow.io/lp/classify-in-session
---

Real-time session classification

# Classify every session, without a frontier model in the render path. 

Signals computes what this visitor is doing and hands it over as compact state in 6ms. Jev answers a typed question about it in a fraction of a second, with a probability you can gate on. No prompt over the raw clickstream, no seconds of latency, no answer that changes each time you ask.

* **6ms** p50 read, in-region
* **<1s** event to state current
* **Free** tier, no card

## Start free

5M events a month · unlimited attributes · no card 

Work email Continue

A work address, please. We cannot provision on a personal domain.

Or [talk to an engineer](https://signals.snowplow.io/start?intent=engineer&lp=classify-in-session).

[ Try it on yourself ↓ ](#live-panel) 

Below is your own visit to this page, computed by Signals as you read.

### Signals, about you 

connecting 

See live events firing as you browse this page on the left; what Signals computed about you on the right, refreshed every few seconds.

Implement this

this page · events 0 writes 

Product Solutions Developers Pricing Start free 

Start free Engineer 

this panel

the rest of the page

nothing sent yet

write 

attribute store · your session no read yet 

reading\_now 

pages\_last\_5\_min 

seconds\_since\_last\_action 

sections\_read 

seconds\_engaged 

pricing\_views 

menus\_explored 

features\_wanted 

cta\_clicks 

arrived\_from 

waiting for the first computed value… 

Your first events are in flight through a real Snowplow pipeline. Attributes appear as the stream computes them, usually within a few seconds.

hand it to your agent one prompt 

Add the Signals MCP server to Claude Code, Cursor or any MCP client, then run this. The agent adds the SDK, defines the attribute and wires the read into your own app.

$ npx plugins add snowplow/skills

> Use the Snowplow MCP server and Signals skill, then help me plan and implement a use case. Make sure everything is tested and works as intended.

Copy the prompt[How to connect the MCP server](https://docs.snowplow.io/docs/llms-support/snowplow-mcp/) 

or 

write it yourself three steps 

01 Track the event once, in your app 

trackPageView(); // browser tracker, already on this page

02 Define the attribute once, in Signals 

Attribute(name="pages_last_5_min", events=["page_view"],
  aggregation="category_count", property="page_urlpath",
  period=timedelta(minutes=5))

03 Read it back every request 

await signals.getServiceAttributes({
  name: "signals_site_session_service",
  attribute_key: "domain_sessionid",
  identifier: "…", // your session id, once set
});

Why the first attempt gets pulled

## The prototype classified sessions well. The bill and the latency killed it. 

Before 

### A general model over the clickstream

Put the session's events in a prompt, ask which kind of visitor this is, wait seconds, pay per token for history the question did not need, and get a different answer when you ask again. Fine for a demo. Not for a render.

`latency in seconds, cost per session` 

With Signals 

### Compact state, typed question

Signals keeps a named context current as of this click, holding only the activity the classification needs. Jev reads it as a state object and returns a choice with a probability, in a fraction of a second.

`intent = comparing (0.88)` 

Result 

### On the render path, at render cost

The classification runs on every page, inside the budget the page already has, and the same state gives the same answer. Gate on the probability and act in your code.

`every render, same session` 

What changes

## Four things the prototype could not give you. 

A classifier earns a place on the render path only if it is fast enough to sit there, cheap enough to run on every session and stable enough to build a rule on.

### Latency you can render behind

The state is precomputed by Signals and read in 6ms. The typed question is answered by a model built for that shape of call. Together they fit inside a render, which a general model over raw history never did.

`p95 render time` 

### Cost that scales with decisions, not history

The context holds the activity the question needs, current as of this click, rather than the whole clickstream re-sent on every call. A visitor who did a lot does not cost more to classify than one who did a little.

`cost per classified session` 

### The same answer for the same state

A typed choice with a calibrated probability lets you set the threshold your code acts on. Below it, the default. Above it, act. No parsing prose, no answer that drifts between calls.

`precision at threshold · rule stability` 

### A decision you can read back

When a classification looks wrong you can read the exact state it was given, because it is a named context with a handful of fields rather than a prompt nobody kept.

`time to debug a decision` 

How it works

## Precompute the state. Ask a typed question. Act on the probability. 

Signals does the knowing, ahead of the request. Jev does the classifying, inside it. Your code does the acting.

1. 01 **Collect.** Add a Snowplow SDK to your app and behavioral events start flowing in, validated on the way.
2. 02 **Read.** Name the activity the classification needs. Signals keeps it current within <1s of the event and returns it as JSON in 6ms p50.
3. 03 **Classify.** Pass it as state, ask a typed choice or a yes-or-no question, gate on the probability and act in the same render.

Signals → your app 6ms p50 

name session\_state 

format json 

events last 30 this session 

seconds\_since\_last\_action 12 

failed\_searches\_session 2 

Trigger stuck\_high\_intent delivered to your app 

Questions

## Before you sign up

### Why not keep the general model and cache its answer?

Caching helps for a visitor who does nothing. The reason to classify in the session is that the state changes on every click, so a cached answer is last minute's answer. Precomputing the state and asking a small typed question is what makes a fresh answer affordable on every render.

### Why not send the raw event history?

Accuracy and cost. TypeSafe's documentation says accuracy falls as state fills with material the question does not need, and to retrieve and filter in code first. A named context is that filter, applied before the call, and it is also why the call stays small.

### Does this only work with Jev?

No. The context comes back as JSON over the REST API, the Node.js or Python SDK, or the MCP server, and as a plain string in narrative form. Anything that takes a state object or a prompt can read it. Jev is the model this page is written for because a typed question with a probability is the shape a render-path classifier needs.

### Does Signals classify anything?

No. Signals computes what the visitor is doing and hands it over. The classification is the model's answer, and what happens next is your code's call.

### Do I need an event stream already?

No. Collection is part of Signals. Add a Snowplow SDK to your web, mobile or server app and events start flowing in. If you already run Snowplow, Signals reads the pipeline you have.

### What does the free tier include?

The full product against your own traffic, up to 5 million events a month, with no card and no sales call. You should see an attribute updating against your own traffic in the first session.

## Classify your own session, on this page, today.

Add the SDK, define one context, ask one typed question from the code that renders. The panel at the top of this page is the state that question would run on, computed from you as you read.

[Start free](https://signals.snowplow.io/start?lp=classify-in-session) [Talk to an engineer](https://signals.snowplow.io/start?intent=engineer&lp=classify-in-session) 

The real engine · no card · no sales call 

Or hand it to your coding agent 

$ `npx plugins add snowplow/skills` Copy Copied 

Then: Add Snowplow Signals to this app. Find where we classify or would classify a visitor, define an agentic context holding only the activity that classification needs, read it as JSON and pass it as the state on a Jev typed question. Gate on the probability before acting.
