A full-stack explorer for Salesforce Data 360 (formerly Data Cloud) segments, their unified individuals, and related DMO records — built with React, Express, and native Node.js TypeScript.
Note: This is a demo / reference implementation. It reads segments, segment members, and related Data Model Objects via the Data 360 REST APIs. It is not hardened for production multi-tenant use.
- Salesforce Data 360 Segment Membership
- License
- Disclaimer
This application exposes Salesforce Data 360 segment membership through a modern browsable UI. It connects to a Data 360 org via OAuth 2.0 client credentials, lists all segments, resolves each segment's unified individuals, and joins related DMO records (page views, email addresses, etc.) into a single detail view.
The demo showcases two operational modes:
Segment Browsing Mode — High-level catalog of every segment in a Data 360 org:
- Lists all segments via the Data 360
ssot/segmentsREST endpoint - Displays segment status, last published date, member count, and publish interval at a glance
- Supports client-side search across display name, API name, and description
- Parses and surfaces the HTML-encoded
includeCriteriaJSON so downstream code can understand the segment definition
Individual Resolution Mode — Drills into a single segment and materializes unified profiles:
- Calls
ssot/segments/{apiName}/membersto fetch the member set - Joins
UnifiedssotIndividualInd__dlmandUnifiedLinkssotIndividualInd__dlmwith the sourcessot__Individual__dlmvia the Query API - Inspects
includeCriteriato discover related DMOs used by aggregation filters, then queries each one in parallel - Groups related records under the unified individual and exposes them in a sortable, searchable, column-configurable table
Segment Browsing Flow:
- Client boots: React mounts inside
<BrowserRouter>and routes the default path toSegmentsPage - Fetch request: the client calls
GET /api/v1/segmentswith thex-api-keyheader - API key check:
apiKeyMiddlewarecompares the header against the server'sAPI_KEYenv var and rejects with401on mismatch - Token resolution:
salesforceAuthservice returns a cached token or requests a new one viagrant_type=client_credentials - Data 360 call:
segmentsServicehits/services/data/{API_VERSION}/ssot/segmentswith the bearer token - Criteria decoding: the server decodes
"-escaped HTML entities inincludeCriteriaandJSON.parses the result - Response: segments are returned as JSON, cached in the client for the session
- Render:
SegmentsPagerendersSegmentCardcomponents with status pills, member counts, and click-through navigation
Individual Resolution Flow:
- Navigation: user clicks a segment card, routing to
/segments/:segmentApiName - Parallel fetch: server kicks off three calls concurrently — token, segment members, full segment (to inspect criteria)
- Base query: server runs a Data 360 SQL query joining
UnifiedssotIndividualInd__dlm→UnifiedLinkssotIndividualInd__dlm→ssot__Individual__dlm, scoped by the unified IDs returned from the member endpoint - Related DMO discovery:
extractRelatedFilterswalksincludeCriteria.filtersand collects uniquecontainerObjectApiNames fromNumberAggregation/DateAggregationfilters - Join key inference:
getJoinKeyinspects the last step of each filter'spath— if the left subject is the unified DMO, it joins on unified ID, otherwise it joins on source record ID - Parallel related queries: each related DMO is queried in parallel with
Promise.allSettledso one failing DMO doesn't poison the whole response - Client-side grouping: the server builds a
Map<unifiedId, Record<DMO, rows[]>>and attaches it to each individual asrelatedData - Detail page: the user drills into a single individual via
IndividualDetailPage, which renders aRelatedDataExplorerfor sortable, searchable, column-configurable tables over every related DMO
Note: All the information in here is from a demo org and all the individuals are fictional.
All non-health endpoints are gated by an API key passed in the x-api-key request header. The key is validated by apiKeyMiddleware against the API_KEY env var on the server.
- Header:
x-api-key: <value> - Obtaining it: generate a random 32-byte hex string (see the Configuration section) and share it between the server and the client build
- Failure response:
401 Unauthorizedwith body{ "error": "Unauthorized" }
| Middleware | Applies To | Purpose |
|---|---|---|
cors |
all routes | Restricts cross-origin requests to the CORS_ORIGIN env value |
express.json |
all routes | Parses JSON request bodies |
requestLoggerMiddleware |
all routes | Emits a structured log on every response with method, URL, status, and duration |
apiKeyMiddleware |
/api/v1/segments/* routes only (inline) |
Validates the x-api-key header; 401 on mismatch. Never applied to static assets. |
GET /api/v1/health
- Auth required: no
- Description: Liveness probe for uptime checks
- Response:
200 OK{ "status": "ok", "timestamp": "2026-01-01T00:00:00.000Z" }
GET /api/v1/segments
- Auth required: yes
- Description: Returns every segment in the connected Data 360 org with decoded
includeCriteria - Request headers:
x-api-key: <value> - Response:
200 OK{ "batchSize": 50, "offset": 0, "totalSize": 1, "segments": [ { "apiName": "Example_Segment__c", "displayName": "Example Segment", "description": "Example description", "publishStatus": "SUCCESS", "segmentStatus": "ACTIVE", "lastPublishedEndDateTime": "2026-01-01T00:00:00Z", "lastSegmentMemberCount": 1, "publishInterval": "NO_REFRESH", "includeCriteria": { "type": "LogicalComparison", "operator": "and", "filters": [] } } ] } - Error responses:
401 Unauthorized— missing or invalid API key500 Internal Server Error— Data 360 upstream failure or token acquisition failure
GET /api/v1/segments/:segmentApiName/individuals
- Auth required: yes
- Description: Resolves all unified individuals in a segment and joins related DMO records driven by the segment's criteria
- Request headers:
x-api-key: <value> - Path params:
segmentApiName— the segment's API name (e.g.Example_Segment__c) - Response:
200 OK{ "totalCount": 1, "individuals": [ { "id": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", "unifiedId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", "firstName": "Example", "lastName": "User", "personName": "Example User", "salutation": null, "birthDate": null, "titleName": null, "primaryAccountId": null, "currentEmployerName": null, "dataSourceId": null, "dataSourceObjectId": null, "photoUrl": null, "relatedData": { "Page_View__dlm": [{ "ssot__Id__c": "1", "ssot__CreatedDate__c": "2026-01-01T00:00:00Z" }] } } ] } - Error responses:
401 Unauthorized— missing or invalid API key500 Internal Server Error— Data 360 query failure, segment not found, or token acquisition failure
| Layer | Technology | Description |
|---|---|---|
| Frontend | React 19, Vite, Tailwind CSS v4 | UI library, dev server / bundler, utility-first CSS |
| Routing | react-router v7 | Client-side routing |
| Icons | Lucide React | Icon set |
| Backend | Node.js 24, Express 5 | JavaScript runtime with native TypeScript execution, minimal web framework |
| Data Platform | Salesforce Data 360 | Source of segments, unified individuals, and related DMO records |
| Hosting | Heroku | Runs the Express app which serves the built React client |
To run this application locally, you will need the following:
- Node.js version 24 or later installed (type
node -vin your terminal to check). Node 24 is required because the server uses native TypeScript execution — notscbuild step. Follow instructions if you don't have Node.js installed - npm version 11 or later installed (type
npm -vin your terminal to check). Node.js includesnpm - git installed. Follow the instructions to install git
- A Salesforce Data 360 org with at least one published segment
- An External Client App in the Data 360 org with the
client_credentialsOAuth flow enabled and a user assigned as the run-as identity - The unified and unified-link DMO API names for the
Individualsubject area — these are org-specific and must be copied out of the Data 360 setup UI
-
Clone the repository
git clone https://github.com/mvrzan/salesforce-data360-segment-membership.git cd salesforce-data360-segment-membership -
Configure Salesforce Data 360
Important: The External Client App's
client_credentialsflow must have a run-as user assigned, and that user must have the Data 360 API permission sets to read segments and run Query API SQL.Follow the Salesforce External Client App documentation to:
- Create an External Client App with OAuth scopes covering Data 360
- Enable the
client_credentialsflow and assign a run-as user - Copy the Consumer Key and Consumer Secret
- Note the unified Individual DMO API name and unified link DMO API name from the Data 360 Data Model setup
-
Configure Environment Variables
Server:
cp server/.env.example server/.env
Open
server/.envand fill in all required values.Client:
cp client/.env.example client/.env
Open
client/.envand fill inVITE_API_KEY. It must matchAPI_KEYinserver/.env— the client sends it asx-api-keyand the server compares byte-for-byte.See Environment Variables for a full description of every variable.
Generate a secure API key:
node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"⚠️ Note: The Vite dev server loadsVITE_*vars at build/dev time. If you changeVITE_API_KEY, restartnpm run devin the client. -
Install Dependencies
Install in both workspaces:
(cd server && npm install) (cd client && npm install)
-
Start the Application
Open two terminals.
Terminal 1 — server (port 3000):
cd server npm run devTerminal 2 — client (port 5173, proxies
/apito the server):cd client npm run dev -
Access the Application
Once both servers are running, open your browser and navigate to
http://localhost:5173.
Server (server/.env):
| Variable | Required | Description |
|---|---|---|
PORT |
No | Port the Express server listens on. Defaults to 3000. |
API_KEY |
Yes | Shared secret validated against the x-api-key header on every /api/v1/segments/* request. |
SF_INSTANCE_URL |
Yes | Salesforce org instance URL (e.g. https://example.my.salesforce.com). No trailing slash. |
SF_CLIENT_ID |
Yes | External Client App Consumer Key. |
SF_CLIENT_SECRET |
Yes | External Client App Consumer Secret. |
API_VERSION |
Yes | Salesforce REST API version (e.g. v66.0). |
UNIFIED_INDIVIDUAL_DMO |
Yes | API name of the unified Individual DMO (e.g. UnifiedssotIndividualInd__dlm). |
UNIFIED_LINK_INDIVIDUAL_DMO |
Yes | API name of the unified link DMO that connects the unified Individual to source records. |
Client (client/.env):
| Variable | Required | Description |
|---|---|---|
VITE_API_KEY |
Yes | Copied into the x-api-key header on every API request. Must match the server's API_KEY. |
Create an External Client App in the target Salesforce org and enable the OAuth client_credentials grant. Assign a run-as user with permissions to read segments and run Data 360 SQL queries. External Client Apps are the modern replacement for Connected Apps for server-to-server integrations.
Official instructions.
The UNIFIED_INDIVIDUAL_DMO and UNIFIED_LINK_INDIVIDUAL_DMO env vars are org-specific — they are generated when identity resolution is configured against the Individual subject area. Find them in Data 360 under Data Model → filter by __dlm objects prefixed with Unified.
The project is configured to deploy to Heroku from the repo root. The root package.json defines a start script that runs end-to-end:
- Installs the client and builds the React bundle
- Moves the
client/distoutput toserver/public/ - Installs server dependencies and boots the Express server via
node src/index.ts
Heroku runs npm install and npm start at the root automatically — no Procfile, no manual build step.
Set every server environment variable as a Heroku config var:
heroku config:set \
CORS_ORIGIN=https://your-app.herokuapp.com \
API_KEY=$(node -e "console.log(require('crypto').randomBytes(32).toString('hex'))") \
SF_INSTANCE_URL=https://example.my.salesforce.com \
SF_CLIENT_ID=xxxxxxxx \
SF_CLIENT_SECRET=xxxxxxxx \
API_VERSION=v66.0 \
UNIFIED_INDIVIDUAL_DMO=UnifiedssotIndividualInd__dlm \
UNIFIED_LINK_INDIVIDUAL_DMO=UnifiedLinkssotIndividualInd__dlmBecause the client bundle is built on the Heroku dyno, VITE_API_KEY must be exposed at build time as well:
heroku config:set VITE_API_KEY=<same value as API_KEY>git push heroku mainHeroku runs npm install and npm start from the root. The start script handles the client build, copies assets into server/public, and boots the server, which serves the SPA and the API from a single dyno.
ISC
This software is to be considered "sample code", a Type B Deliverable, and is delivered "as-is" to the user. Salesforce bears no responsibility to support the use or implementation of this software.





