Personalization
User profiles, segments, session reranking, and recommendations in Relevance Studio. How personalize=true + sessionId wire profile-boost into ranking, and best practices for PII.
The Personalization area of Relevance Studio turns anonymous and logged-in user signals into ranking adjustments. It is built on four layers, each one optional and independently togglable per index:
- User profiles — per-user feature vectors derived from purchase and click history.
- Segments — coarse cohorts (e.g. "frequent buyers", "discount hunters") that group profiles for editor-tunable boosts.
- Session reranking — short-lived, in-session reordering based on the last few queries and clicks.
- Recommendations —
related products,also-bought,frequently-viewed-togetherblocks fed by the same profile store.
When to enable personalization
Turn personalization on when all three of the following hold:
- Your search has a real concept of a returning user (logged-in or stable anonymous ID).
- You have at least ~10k click events per index per week — below that, the signal is too noisy.
- Editor curations alone are not enough to differentiate the same query for different users.
If you are unsure, start with session reranking only — it requires no historical data and degrades to a no-op for first-time visitors.
How personalize=true + sessionId wire into the search call
Every public search request can opt in by sending two extra fields:
personalize: trueon the request body — turns the layer on for this query.sessionId: "<stable-id>"— a UUID or hashed-cookie value that ties this request to the user's profile (or, for anonymous users, their session).
The server resolves these fields in the read path, before the Typesense
multi_search call, and AND-combines a profile-boost expression into the
existing ranking. The flow is the same five gates as a normal search — auth,
rate limit, tenant filter, scoped filter, multi_search — with one extra step
between rate-limit and tenant-filter: applyPersonalization.
Stable IDs and PII
The sessionId MUST be opaque to AACsearch. Do not send raw emails,
phone numbers, or user IDs from your CRM. The recommended pattern is:
sessionId = sha256(your_internal_user_id + BETTER_AUTH_SECRET_FRAGMENT).slice(0, 32)The server-side history that backs each profile caps at 100 entries per
session. Older clicks and views are evicted FIFO. This is intentional: it
keeps the profile small, fast to load on the hot path, and easy to expire
for GDPR right-to-be-forgotten requests (a single DELETE /personalization/profile/<sessionId>
clears it).
Example: curl
curl -X POST https://api.aacsearch.com/api/search/public/multi \
-H "Authorization: Bearer ss_search_pub_XXXXXX" \
-H "Content-Type: application/json" \
-d '{
"searches": [
{
"collection": "products",
"q": "running shoes",
"query_by": "title,brand,description",
"personalize": true,
"sessionId": "9f3c1b2e8a7d4f6c0b1a2d3e4f5a6b7c"
}
]
}'The response shape is unchanged. If personalize=true cannot be applied —
for example the profile is empty, the index has personalization disabled,
or the Scale plan entitlement is missing — the request gracefully falls back
to the standard ranking and a personalization_applied: false flag is
included in the response metadata.
Example: TypeScript SDK / oRPC client
import { client } from "@repo/api/client";
const res = await client.search.public.multi.call({
searches: [
{
collection: "products",
q: "running shoes",
query_by: "title,brand,description",
personalize: true,
sessionId: visitorSessionId, // sha256(uid + secret).slice(0, 32)
},
],
});For browser usage, prefer the scoped search token flow described in
Architecture → Read path — the personalize
and sessionId fields are forwarded through the scoped token unchanged.
Wiring profile-boost into ranking
Inside Studio → Relevance → Personalization, an admin chooses how profile features influence ranking. Three modes are supported:
| Mode | Effect on ranking | When to use |
|---|---|---|
off | No-op (default for new indexes). | Establishing baselines. |
boost | Adds a _personalization_score boost to the existing ranker. | Conservative roll-out, keeps editor curations dominant. |
rerank | Re-sorts the top-K results from the base ranker by the personalization score. | High-traffic indexes with strong profile coverage. |
The boost weight is a single number 0–10. Start at 1.0, watch CTR on the Click feedback panel for a week, and bump in 0.5 increments. Anything above 5.0 typically dominates curations — only go there if you have validated it in an A/B test.
Segments
A segment is a saved filter over the user-profile space. Segments are defined in Studio and evaluated server-side on every personalized request. Examples shipped out of the box:
frequent_buyers— profiles with ≥ 3 purchase events in the last 30 days.cart_abandoners— profiles with ≥ 1add_to_cartand 0purchasein 7 days.discount_hunters— profiles whose clicked products haddiscount_pct > 0in ≥ 50% of recent clicks.
Each segment has its own boost weight. A user in two segments receives the max of the two weights (not the sum) — this keeps the math predictable when segments overlap.
Recommendations
The same profile store powers the recommendation endpoints. They are
exposed as their own oRPC procedures and do not require personalize=true
on the search call:
recommendations.related— content-based: similar to the input productId.recommendations.alsoBought— collaborative: from the click-and-buy graph.recommendations.frequentlyViewedTogether— co-view within a session.
See the dedicated Recommendations section for the full request and response shape.
Best practices on PII
- Never send raw email / phone / customer ID in
sessionId. Hash it first with a server-side secret. - Treat
sessionIdas a token, not an identifier. It should rotate when the user logs out. - Cap history at 100 entries. This is the server-side default and the only supported configuration — it is documented here so customers can reason about retention.
- Wire a GDPR delete hook. When a user invokes right-to-be-forgotten,
call the
personalization.profile.deleteprocedure with their stablesessionId. The wipe is synchronous and idempotent. - Do not personalize logged-out admin sessions. Inspect-mode queries
from Studio should pass
personalize=falseso the ranking explainer sees the unbiased ranking.
Related
- Personalization API reference — full request
and response shape for
personalize=trueand therecommendations.*procedures. - Learning to Rank — the orthogonal feedback loop that learns from clicks regardless of personalization.
- Architecture → Read path — where the
applyPersonalizationstep sits in the request flow. - Recommendations — the surface that consumes the same profile store.
Relevance Studio Overview
What Relevance Studio is, the 16 admin panels organized into 5 areas (Relevance, LTR, Health & Scale, Cross-region, Analytics & Debug), and when to use Studio.
Learning to Rank
The LTR pipeline in Relevance Studio — click feedback, position-bias correction, training, model versioning, A/B tests, and activation. How to interpret z-test significance and pick a winner.