Send Events¶
For developers integrating a UI with an already-deployed engine. You capture visitor behavior with the RudderStack JavaScript SDK; RudderStack forwards it to the engine, which builds the user model. You do not run any infrastructure here, you only need two values from whoever deployed the system:
- a Write Key
- a Data Plane URL
(These come from the RudderStack source set up during deployment. See Deployment, RudderStack.)
Set up the RudderStack SDK¶
RudderStack is a customer-data pipeline. Its browser SDK buffers events client-side and ships them to the data plane, which fans them out to destinations (here, the engine's ingest webhook). Load the SDK and initialise it with your two values:
<script>
!function(){var e=window.rudderanalytics=window.rudderanalytics||[];e.methods=["load","page","track","identify","reset","group","alias"];e.factory=function(t){return function(){var r=Array.prototype.slice.call(arguments);return r.unshift(t),e.push(r),e}};for(var t=0;t<e.methods.length;t++){var r=e.methods[t];e[r]=e.factory(r)}e.load=function(t,r,n){var o=document.createElement("script");o.src="https://cdn.rudderlabs.com/v3/rudder-analytics.min.js",o.async=!0;var a=document.getElementsByTagName("script")[0];a.parentNode.insertBefore(o,a),e._loadOptions=n||{},e._writeKey=t,e._dataPlaneUrl=r}}();
rudderanalytics.load(WRITE_KEY, DATA_PLANE_URL);
</script>
Full SDK reference and framework packages (React, Vue, Next): RudderStack JavaScript SDK docs.
Identify the user¶
Recommendations are per visitor, so every event needs to be tied to a stable identity. Call
identify once you know who the visitor is (a returning user_id, or the id assigned at
survey time). Until then the SDK uses an anonymous id, and the engine still accepts it
(anonymousId). Traits passed here seed the cold-start profile.
Use the same userId later when you read recommendations.
Allowed events¶
The engine only understands the events defined in the Event catalog, the authoritative, versioned list of every allowed event name and its exact payload shape. Anything not in the catalog, or sent with the wrong shape, is ignored. The current set:
| Domain | Allowed events |
|---|---|
| Content | CONTENT_VIEW_STARTED, CONTENT_VIEW_ENDED, CONTENT_VIEW_CHANGED, COLLECTION_VIEW_STARTED, COLLECTION_VIEW_ENDED, PANEL_VIEW_STARTED, PANEL_VIEW_ENDED |
| Survey | SURVEY_PRESENTED, SURVEY_ANSWERED, SURVEY_SUBMITTED, SURVEY_DISMISSED |
| Input | CONTENT_LOOKUP, POSITION_UPDATED |
| Session | SESSION_STARTED, SESSION_STATE_CHANGED, SESSION_ENDED |
| Global | GLOBAL_STARTED, GLOBAL_ENDED |
Plus the RudderStack identify call (above), which the engine reads as IDENTIFY. The
Event catalog is the source of truth; always check it for the exact per-event fields.
Track events¶
Emit one track call per visitor action. The examples below cover the events that most directly drive
recommendations; see the Event catalog for the full list above and every field. Keep a
single session_id per visit and reuse it across a view's start and end so the engine can pair them into
dwell.
const SESSION = crypto.randomUUID();
// Visitor opens a story (the other items on screen are impressions)
rudderanalytics.track("CONTENT_VIEW_STARTED", {
content: { content_id: "content_841" },
context: {
session_id: SESSION,
candidates: [{ content_id: "content_842" }, { content_id: "content_843" }]
}
});
// Visitor leaves the story
rudderanalytics.track("CONTENT_VIEW_ENDED", {
content: { content_id: "content_841" },
context: { session_id: SESSION },
details: {
reason: "next_button", dwell_seconds: 42.5, // next_button|link|close_button|abandon
request_id: REQUEST_ID // echo the rec's request_id (see below)
}
});
// Visitor searches and clicks a result
rudderanalytics.track("CONTENT_LOOKUP", {
details: { query_text: "Bergen-Belsen 1944", clicked_id: "content_841" }
});
// Visitor answers the survey (a rating answer feeds engagement)
rudderanalytics.track("SURVEY_ANSWERED", {
answers: [{ question_id: "satisfaction", question_type: "rating", answer_value: 4 }]
});
The event names, the exact nested property shapes, and what each field becomes are in the Event catalog. Get those right and the engine does the rest.
Echo the request_id¶
/api/recommend returns a request_id with each recommendation list (see
Consume Recommendations). When a visitor finishes an item that came from that
list, echo the same request_id on the CONTENT_VIEW_ENDED track call, under
details.request_id:
// store the request_id when you render a recommendation list
const { result } = await (await fetch(`${API}/api/recommend?user_id=${userId}`)).json();
const REQUEST_ID = result.request_id;
// ...then echo it when the visitor finishes a recommended item (see the ENDED example above)
This joins the served context (the feature vector the engine scored) to the outcome (dwell, end-reason), which is how the contextual bandit earns its reward signal. Without it, the learned ranker gets no feedback. Detail: Bandit / online learning.
Multi-tenancy¶
On a multi-tenant deployment, every event must carry X-Tenant-Id so it lands in the
right client's slice (its own user models, events, and impressions). Configure the RudderStack
webhook destination to send the header, the same id the UI uses when it
reads recommendations. No id means the default tenant. See
Multi-tenancy.
Next: Consume Recommendations.