Real-time Personalization 2.0 is a data engine that provides instant, contextual information about customer profiles, including their attributes, behaviors, and segments. It is designed to power real-time personalization of web and mobile interfaces. You can learn more about Real-time Personalization here.
It is possible to implement Real-time Personalization 2.0 in either a client-side or server-side configuration. This guide is focused on a server-side implementation.
This step-by-step guide shows how to build and implement Real-Time Personalization 2.0 server-side use cases on a simple web application. The walkthrough covers the full process—from data ingestion and attribute configuration through audience segmentation, and downstream integration.
Real-Time Personalization 2.0 (RT 2.0) provides a modern, server-to-server push/pull API architecture. It enables both rapid ingestion of behavioral data and real-time evaluation against personalization rules and segments defined in Treasure Data’s Audience Studio. Users can design experiences that immediately react to events (such as signups, logins, product views) from the web, returning tailored payloads for personalization.
RT 2.0 Personalization API vs. RT 1.0 (Profiles API)
| Feature | RT 1.0 | RT 2.0 |
|---|---|---|
| Method | GET by sending lookup key | POST by ingesting data |
| Endpoint | cdp.in.treasuredata.com | us01.p13n.in.treasuredata.com |
| Access | SDK-client side | Server-to-server |
| Response & String-build | Segments, batch attributes | Batch & RT attributes + segments |
| SLA | Not guaranteed | 100ms |
| Token | UI created | UI + manual config |
| Latency: Batch Update | Token WF run + PS workflow run | Workflow run + RT sync run |
Compared to the Real-time Personalization 1.0 solution, the 2.0 solution (server-side) offers significantly more comprehensive and lower-latency responses. It is optimized for server-side use cases and near-real-time activation.
The following sections demonstrate how to configure Real-time Personalization 2.0 for a website application.
This step covers setting up data flows from your web app to the Treasure Data CDP, with a focus on core ingestion APIs and best practices in table/segment design.
You may already be sending data into your CDP tables, or you might be preparing new tables based on the desired personalization use cases. In either scenario, ensure that behavioral events are mapped to separate tables and leveraged as “parent segments” for downstream configuration. These segments serve as the foundation for RT 2.0 configuration.
Once RT 2.0 is set up, underlying tables and segments can be updated continuously—without needing to resubmit configuration requests.
SDK 4.1: Sends data from the browser/client-side; easier to implement, but may introduce security risks for sensitive PII or business logic.
Ingest API: Sends event data from a server or serverless function (e.g., AWS Lambda). Recommended for enhanced security and control.
Example ingestion with Lambda (server-side):
See Treasure Data documentation for a full list of region-specific baseURLs.
What’s the difference between the Ingest API and the Personalization API?
Ingest API: Sends event data to the CDP for storage and later segmentation; does NOT return a personalization payload in the response.
Personalization API: Both ingests event data AND immediately evaluates personalization logic, returning a real-time payload based on the rules defined in Audience Studio.
For example, submitting a newsletter signup event:
With Ingest API: You POST event data for future segment updates, then separately call an API (or downstream process) to retrieve segment or attribute info.
With Personalization API: One API call both ingests the event and returns real-time flags (e.g., user_status: subscribed, segment_membership: batch_subscribed_users) in the response.
Personalization API endpoints:
This dual capability streamlines integration, reduces latency, and enables instant experience activation from a single API call.
Example Personalization API request:
{
"td_client_id":"1986b2a4-3957-4863-be2c-7ef36a14afee",
"td_url":"https://treasuredemos.com/retail",
"td_path":"/retail"
// additional properties...
}Example of a Personalization API response.
{
"offers": {
"First-Time-Visitor": {
"attributes": {
"newsletter_section": "show"
},
"batch_segments": null
}
}
}This step describes how to request that Real-Time Personalization 2.0 be activated for your Treasure Data account, configure the key features in Treasure Data's UI, and manage Personalization tokens for API access.
Follow these steps to activate RT Personalization 2.0 on your account:
First, create your parent segment in Audience Studio.
Next, ask your Customer Success Manager (CSM) to enable the Real-Time Personalization 2.0 feature.
Provide your CSM with the following information:
AWS instance name
Environment (production, staging, etc.)
Parent Segment ID
After this feature has been enabled on your account, you should receive:
Before/after screenshots confirming feature enablement.
Reactor Instance Configuration details (e.g., a11874n4), which you’ll use to build your Personalization 2.0 token.
Follow these steps to configure Real-time Personalization 2.0 in the Treasure Data Console:
Select the appropriate parent segment.
On the Configuration Options screen, click the "Configure settings" button in the "Personalization Configuration" section.
You will be directed to a data grid that lists all the personalization services.
Click the "Create new personalized service" button.
Enter a name for your personalization service and, if desired, provide a description.
You may see a message about SSO/identity validation. If your account is username+password-based, try signing out and back in if prompted—SSO messages may be displayed even for non-SSO users.
On the right-side panel, click the "+ Add token" button.
Creating and managing personalization tokens:
Assign a name to each Personalization token.
Select either Public or Private as the service type.
Note that IP Whitelist applies to only Private service type; access will be restricted to only these IP address ranges. If no IP addresses are specified, then it will default to your TD account's IP whitelist.
You may restrict the token to certain IP address ranges (optional); if left blank, account/IP whitelist defaults apply.
Each API token follows the format: [YOUR_ACCOUNT_INSTANCE_ID]/[REACTOR_INSTANCE_ID]/[RT_PERSONALIZATION_TOKEN]
Example: 11874/3/YOUR_TOKEN
- For use in Postman or server code, format as header: "WP13n-Token": "11874/3/YOUR_TOKEN"
Note that it is possible to have multiple tokens per personalization instance. This can be useful for managing access in different environments (staging vs. production), or separating server- and client-side usage in the future. Treasure Data allows for up to 50 tokens per personalization service. The maximum number of tokens per Parent Segment is 200.
Validation Experience Notes:
If attempting to view tokens later, a security prompt to re-login is normal.
V4 and V5 UI differ: V4 uses password for validation, V5 may prompt for SSO.
After configuring the personalization service, the next step is to set up the event tables, event filtering, and all of the associated attributes.
In the Real-Time Configuration section, add all streaming event tables to be used.
Create new events by:
Clicking “Create new event”
Naming the event
Choosing a source table
- Optionally, filtering for specific actions (e.g., product page view via regular expression on td_path)
Example event filter for product page views:
- td_path regex: ^/retail/./product/.
Defining Attributes (Attributes Tab):
Add attributes by type with sensitivity:
Single: Stores the latest value (string/numeric) for a user. Note that this can be either sensitive or non-sensitive.
List: Stores up to 100 items in an array (e.g., products viewed), each expiring after 60 days. Note that this can be either sensitive or non-sensitive.
Counter: Tracks event counts or numeric sums; supports sliding (rolling window) or total (cumulative) modes. Note that this can only be non-sensitive.
Imported Batch: Pulls values from batch segments for reference during real-time events; not updated by real-time events, only via parent segment re-runs. Note that this can be either sensitive or non-sensitive.
Note: Users can classify attribute sensitivity as either sensitive or non-sensitive. Only non-sensitive attributes are exposed in the API response.
Understanding Single Attributes:
Used for holding the most recent event value (e.g., last product viewed).
Each attribute has a System ID (remains stable unless the attribute is renamed—then a new System ID is created).
For historical merge, use Batch Backfill (import batch data with timestamp alignment).
Attribute design should avoid name/alias conflicts between real-time and batch.
Understanding List Attributes:
Intended for session/recency use cases (“products viewed last X hours”); store arrays of strings/numbers.
No batch backfill—if needed, manage batch/real-time via two attributes.
Aggregation options: First, Last, Sum, Min, Max, Distinct List. Some aggregations are limited in return types (Sum/Min/Max are numeric only).
Use cases span recency, unique items, and most recent actions—with careful design for mapping and ingest format.
Known List Attribute Behavior:
If primary value_field_name is a string, lists are “flattened” for aggregation.
List attribute properties (e.g., price, URL) mapped as arrays preserve entire JSON lists, not just last item.
Workarounds include sending atomic events or mapping all item properties as buffer attributes.
Understanding Counter Attributes:
Count occurrences or increment totals within defined durations.
Sliding counters: Rolling window (last N minutes, hours, or days) with optional sub-durations (e.g., hourly within daily count). Up to 20 sub-durations allowed.
Total counters: Cumulative over fixed discard window (up to 60 days).
Durations must be chosen for both utility (granularity) and system constraints.
Understanding Imported Batch Attributes:
Available for inclusion in real-time payload when defined in parent segment.
Added or removed via RT configuration, but never updated by real-time ingestion (only by parent segment re-run).
Removing batch attributes from parent segment without updating RT config causes errors; to fix, re-add to parent segment, run the segment, or create a dummy table/attribute if needed.
Attribute Design Best Practices:
Plan attribute/system naming to avoid collisions.
Test attribute updates via both batch and real-time paths for consistency.
Define aggregation logic and window sizes up front.
ID Stitching ensures batch and real-time data are unified at the profile level, resolving multiple identifiers (emails, client IDs, etc.) into a single customer view.
How to Set Up ID Stitching:
Define “stitching keys” (e.g., td_client_id, email, canonical_id). Optionally, add regex filters or mark keys as “workflow only” for batch-only identifiers.
After saving configuration, select a primary key from eligible keys. Note that the primary key must be both unique and stable for every profile in the Parent Segment.
Re-launch after primary key set; otherwise, stitching remains disabled.
The system merges profiles by selecting the oldest ID as the primary.
Up to 200 stitching keys supported.
Best Practices and Caveats:
Plan which IDs your event data will provide and align across batch and real-time sources.
Be careful with primary key assignment; changing after launch requires system re-run.
Examples:
New event with email, but pre-existing profile by td_client_id: both merged, oldest ID becomes primary.
Excluding values or filtering enables ignoring bad or test identities.
With events and attributes set up, the next step is to build and manage the personalization logic in Audience Studio.
Creating a New Personalization:
Access your parent/master segment in the left sidebar.
Click “Create”, select “Personalization”, and provide a name.
Open the new personalization to enter the Personalization Canvas.
Add Sections for Criteria and Payload:
Each section contains:
Criteria: Rules for when personalization should trigger (e.g., "email is null" for unknown, "email is not null" for known).
Payload: Define the data to return if criteria match. Add batch and RT attributes, assign aliases, and include batch segments as needed.
Note that sensitivity icons appear next to the section name if the payload contains any sensitive real-time or imported batch attributes.
Considerations:
The event picked for personalization must match intended triggers.
Stitching keys must be fully configured—improper stitching causes attribute propagation issues.
Alias output for each payload field for downstream mapping.
Payload size should remain below 10kb; keep attribute count to 20 or fewer for 100ms SLA.
Re-run parent/master segment workflow after batch segment or attribute changes to ensure updated results.
UI Workflow Tips:
After clicking "Save payload," the button becomes inactive—click "Cancel" twice, then "Save & Launch" to finish.
UI loads assets with some latency—refresh as needed.
This section provides practical integration guidance for using the RT 2.0 Personalization API within application codebases and deployment environments.
API Endpoint Convention:
US endpoint: https://us01.p13n.in.treasuredata.com/{YOUR_DB_NAME}/{tableName}
YOUR_DB_NAME: Streaming event database
tableName: The table with desired events
Required Headers:
"Content-Type": "application/vnd.treasuredata.v1+json"
"Authorization": "TD1 [TD_API_KEY]"
"WP13n-Token": "[YOUR_ACCOUNT_INSTANCE_ID]/[REACTOR_INSTANCE_ID]/[RT_PERSONALIZATION_TOKEN]"
Request Body Example (JSON):
{
"td_client_id": "42a508e2-d9b1-4baa-9eb2-6c3fb8bd5e16",
"td_url": "https://treasuredemos.com/retail/shop/women",
"td_path": "/retail/shop/women",
"product_name": "women’s-tank-top",
"product_category": "women",
"product_list": ["women’s-running-shoes", "kids’-hoodie", "women’s-tank-top"],
"category_list": ["women", "kids"]
}Example of an API request using Node.js Lambda Example (server-side):
jsconst fetch = require("node-fetch");
exports.lambdaHandler = async (event) => {
try {
const tableName = event.pathParameters?.tableName || null;
const requestBody = event.body ? JSON.parse(event.body) : {};
if (!tableName) {
return { statusCode: 400, body: JSON.stringify({ error: "Missing tableName parameter" }) };
}
if (!requestBody.td_client_id) {
return { statusCode: 400, body: JSON.stringify({ error: "Missing td_client_id" }) };
}
const response = await fetch(`https://us01.p13n.in.treasuredata.com/{YOUR_DB_NAME}/${tableName}`, {
method: "POST",
headers: {
"Content-Type": "application/vnd.treasuredata.v1+json",
"Authorization": `TD1 ${process.env.TD_API_KEY}`,
"WP13n-Token": process.env.PERSONALIZATION_TOKEN
},
body: JSON.stringify(requestBody)
});
if (!response.ok) {
throw new Error(`Treasure Data API returned status: ${response.status}`);
}
const data = await response.json();
return { statusCode: 200, body: JSON.stringify(data) };
} catch (error) {
return { statusCode: 500, body: JSON.stringify({ error: error.message }) };
}
};Example of a Personalization API Response (including non-sensitive attributes):
{
"offers": {
"Welcome to our website!": {
"attributes": {
"welcome_message": "Welcome to our website! Enjoy your shopping!"
}
},
"Silver rewards just for you!": {
"attributes": {
"real-time_single_attribute_first_name": "John",
"real-time_counter_attribute_total_purchase": 28700,
"real-time_list_attribute_items_purchased": [],
"order_summary": "Here is your order summary!",
"thank_you_message": "Thanks you for shopping with us!",
"promotions_message": "Enjoy your 15% discount for your next purchase!"
}
},
"Golden membership offer!": {
"attributes": {
"customer_name": "John",
"order_total": 28700,
"list_reactor_offers_ak_1 - aggr1_path_list_reactor_offers_ak_1": [
"/06130004",
"/promotion",
"/silver"
],
"rewards_message": "You have spent $300 on eligible purchases. Your Rewards Certificate(s) are ready for you!"
}
}
}
}Example of a Personalization API Response (with sensitive attributes excluded):
{
"offers": {}
}Deployment Steps:
Install dependencies locally (npm install node-fetch).
Package and upload to Lambda.
Store API key and personalization token as Lambda environment variables.
Create an API Gateway POST endpoint to trigger the Lambda.
Client-Side Fetch Example:
jsconst apiUrl = "https://{your-api-id}.execute-api.{region}.amazonaws.com/dev/fetch-treasure-data/user_data";
fetch(apiUrl, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
td_client_id: "12345",
email: "user@example.com",
td_url: "https://example.com/",
td_path: "/"
})
})
.then(response => response.json())
.then(data => console.log("Personalization Response:", data));This concludes the demonstration of how to configure Real-time Personalization 2.0 (server-side) for a simple web application.