Digital Twin Survey
This guide walks you through running an audience survey using digital twins — from authentication to interpreting group opinions.
Digital twins are AI personas grounded in real demographic data. You can ask them questions individually or as a group, and get responses that reflect their demographic profile, values, and perspectives.
What you'll build
By the end of this guide, you will:
- Authenticate with the neuroflash API
- Fetch your workspace
- Browse available demographic groups
- List twins in a group
- Ask a single twin a question
- Get group opinions from multiple twins
Prerequisites
- A neuroflash account with API access
- Your
client_idandclient_secret(see Authentication)
Step 1: Authenticate
First, obtain an access token:
- cURL
- Python
- Node.js
- Go
curl -X POST https://id.neuroflash.com/oauth/v2/token \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=client_credentials" \
-d "client_id=YOUR_CLIENT_ID" \
-d "client_secret=YOUR_CLIENT_SECRET" \
-d "scope=openid"
import requests
BASE_URL = "https://app.neuroflash.com/api"
# Authenticate
auth_response = requests.post(
"https://id.neuroflash.com/oauth/v2/token",
data={
"grant_type": "client_credentials",
"client_id": "YOUR_CLIENT_ID",
"client_secret": "YOUR_CLIENT_SECRET",
"scope": "openid",
},
)
token = auth_response.json()["access_token"]
headers = {"Authorization": f"Bearer {token}"}
const BASE_URL = "https://app.neuroflash.com/api";
// Authenticate
const authResponse = await fetch("https://id.neuroflash.com/oauth/v2/token", {
method: "POST",
headers: { "Content-Type": "application/x-www-form-urlencoded" },
body: new URLSearchParams({
grant_type: "client_credentials",
client_id: "YOUR_CLIENT_ID",
client_secret: "YOUR_CLIENT_SECRET",
scope: "openid",
}),
});
const { access_token } = await authResponse.json();
const headers = { Authorization: `Bearer ${access_token}` };
package main
import (
"encoding/json"
"fmt"
"net/http"
"net/url"
"strings"
)
const baseURL = "https://app.neuroflash.com/api"
func main() {
// Authenticate
data := url.Values{
"grant_type": {"client_credentials"},
"client_id": {"YOUR_CLIENT_ID"},
"client_secret": {"YOUR_CLIENT_SECRET"},
"scope": {"openid"},
}
resp, _ := http.Post(
"https://id.neuroflash.com/oauth/v2/token",
"application/x-www-form-urlencoded",
strings.NewReader(data.Encode()),
)
defer resp.Body.Close()
var authResult struct {
AccessToken string `json:"access_token"`
}
json.NewDecoder(resp.Body).Decode(&authResult)
token := authResult.AccessToken
}
Step 2: Get Your Workspace
Fetch your available workspaces and select the first one. The workspace ID is required for most API calls:
- cURL
- Python
- Node.js
- Go
curl "https://app.neuroflash.com/api/workspace-service/v1/workspaces" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN"
workspaces = requests.get(
f"{BASE_URL}/workspace-service/v1/workspaces",
headers=headers,
).json()
workspace_id = workspaces["_embedded"]["workspaces"][0]["id"]
print(f"Using workspace: {workspace_id}")
const workspaces = await fetch(
`${BASE_URL}/workspace-service/v1/workspaces`,
{ headers }
).then((r) => r.json());
const workspaceId = workspaces._embedded.workspaces[0].id;
console.log(`Using workspace: ${workspaceId}`);
req, _ := http.NewRequest("GET", baseURL+"/workspace-service/v1/workspaces", nil)
req.Header.Set("Authorization", "Bearer "+token)
wsResp, _ := http.DefaultClient.Do(req)
defer wsResp.Body.Close()
var wsResult struct {
Embedded struct {
Workspaces []struct {
ID string `json:"id"`
} `json:"workspaces"`
} `json:"_embedded"`
}
json.NewDecoder(wsResp.Body).Decode(&wsResult)
workspaceID := wsResult.Embedded.Workspaces[0].ID
fmt.Printf("Using workspace: %s\n", workspaceID)
Step 3: Browse Static Groups
neuroflash provides pre-built demographic groups with ready-to-use digital twins. List the available groups:
- cURL
- Python
- Node.js
- Go
curl "https://app.neuroflash.com/api/digital-twin-service/v1/static-groups" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN"
groups = requests.get(
f"{BASE_URL}/digital-twin-service/v1/static-groups",
headers=headers,
).json()
for group in groups:
print(f"{group['key']}: {group['label']} — {group['description']}")
const groups = await fetch(
`${BASE_URL}/digital-twin-service/v1/static-groups`,
{ headers }
).then((r) => r.json());
groups.forEach((g) => console.log(`${g.key}: ${g.label} — ${g.description}`));
req, _ := http.NewRequest("GET", baseURL+"/digital-twin-service/v1/static-groups", nil)
req.Header.Set("Authorization", "Bearer "+token)
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
var groups []struct {
Key string `json:"key"`
Label string `json:"label"`
Description string `json:"description"`
}
json.NewDecoder(resp.Body).Decode(&groups)
for _, g := range groups {
fmt.Printf("%s: %s — %s\n", g.Key, g.Label, g.Description)
}
Response:
[
{
"key": "gen_z",
"label": "Gen Z",
"description": "Generation Z (born 1997-2012)"
},
{
"key": "millennials",
"label": "Millennials",
"description": "Millennials (born 1981-1996)"
},
{
"key": "gen_x",
"label": "Generation X",
"description": "Generation X (born 1965-1980)"
},
{
"key": "baby_boomers",
"label": "Baby Boomers",
"description": "Baby Boomers (born 1946-1964)"
},
{
"key": "gen_alpha",
"label": "Generation Alpha",
"description": "Generation Alpha (born 2010+)"
}
]
Step 4: List Twins in a Group
Pick the first group and get the individual digital twins within it:
- cURL
- Python
- Node.js
- Go
curl "https://app.neuroflash.com/api/digital-twin-service/v1/workspaces/{workspace_id}/static-groups/gen_z/twins?page=1&size=5" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN"
group_key = groups[0]["key"]
twins = requests.get(
f"{BASE_URL}/digital-twin-service/v1/workspaces/{workspace_id}/static-groups/{group_key}/twins",
headers=headers,
params={"page": 1, "size": 5},
).json()
for twin in twins["data"]:
print(f"{twin['name']} — {twin['title']} (Age: {twin['age']}, {twin['location']})")
const groupKey = groups[0].key;
const twins = await fetch(
`${BASE_URL}/digital-twin-service/v1/workspaces/${workspaceId}/static-groups/${groupKey}/twins?page=1&size=5`,
{ headers }
).then((r) => r.json());
twins.data.forEach((t) =>
console.log(`${t.name} — ${t.title} (Age: ${t.age}, ${t.location})`)
);
groupKey := groups[0].Key
twinsURL := fmt.Sprintf("%s/digital-twin-service/v1/workspaces/%s/static-groups/%s/twins?page=1&size=5",
baseURL, workspaceID, groupKey)
req, _ := http.NewRequest("GET", twinsURL, nil)
req.Header.Set("Authorization", "Bearer "+token)
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
var twinsResult struct {
Data []struct {
ID string `json:"id"`
Name string `json:"name"`
Title string `json:"title"`
Age int `json:"age"`
Location string `json:"location"`
} `json:"data"`
}
json.NewDecoder(resp.Body).Decode(&twinsResult)
for _, t := range twinsResult.Data {
fmt.Printf("%s — %s (Age: %d, %s)\n", t.Name, t.Title, t.Age, t.Location)
}
Response:
{
"data": [
{
"id": "a1b2c3d4-...",
"name": "Jens Bauer",
"title": "Tech-Savvy Millennial",
"age": 34,
"gender": "male",
"location": "Munich",
"jobTitle": "Software Engineer",
"staticGroupKey": "millennials",
"_links": {
"avatarUrl": "https://storage.googleapis.com/..."
}
}
],
"page": {
"size": 5,
"totalElements": 20,
"totalPages": 4,
"currentPage": 1
}
}
Step 5: Ask a Single Twin
Now ask an individual twin a question. The twin responds in character, based on its demographic profile:
- cURL
- Python
- Node.js
- Go
curl -X POST "https://app.neuroflash.com/api/digital-twin-service/v1/workspaces/{workspace_id}/twins/{twin_id}/chat-completions" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"messages": [
{
"role": "user",
"content": "What do you think about electric vehicles? Would you buy one?"
}
]
}'
twin_id = twins["data"][0]["id"]
response = requests.post(
f"{BASE_URL}/digital-twin-service/v1/workspaces/{workspace_id}/twins/{twin_id}/chat-completions",
headers=headers,
json={
"messages": [
{
"role": "user",
"content": "What do you think about electric vehicles? Would you buy one?",
}
]
},
).json()
print(f"Answer: {response['answer']}")
print(f"Reason: {response['reason']}")
const twinId = twins.data[0].id;
const response = await fetch(
`${BASE_URL}/digital-twin-service/v1/workspaces/${workspaceId}/twins/${twinId}/chat-completions`,
{
method: "POST",
headers: { ...headers, "Content-Type": "application/json" },
body: JSON.stringify({
messages: [
{
role: "user",
content:
"What do you think about electric vehicles? Would you buy one?",
},
],
}),
}
).then((r) => r.json());
console.log(`Answer: ${response.answer}`);
console.log(`Reason: ${response.reason}`);
twinID := twinsResult.Data[0].ID
chatURL := fmt.Sprintf("%s/digital-twin-service/v1/workspaces/%s/twins/%s/chat-completions",
baseURL, workspaceID, twinID)
body, _ := json.Marshal(map[string]any{
"messages": []map[string]string{
{"role": "user", "content": "What do you think about electric vehicles? Would you buy one?"},
},
})
req, _ := http.NewRequest("POST", chatURL, bytes.NewReader(body))
req.Header.Set("Authorization", "Bearer "+token)
req.Header.Set("Content-Type", "application/json")
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
var chatResult struct {
Answer string `json:"answer"`
Reason string `json:"reason"`
}
json.NewDecoder(resp.Body).Decode(&chatResult)
fmt.Printf("Answer: %s\n", chatResult.Answer)
fmt.Printf("Reason: %s\n", chatResult.Reason)
Response:
{
"answer": "I'm definitely considering it for my next car. The tech has gotten really good, and with charging infrastructure improving in Munich, it makes sense both financially and environmentally.",
"reason": "As a tech-savvy millennial software engineer, Jens is drawn to innovative technology and is environmentally conscious. His urban Munich location provides good charging infrastructure, making EVs practical."
}
By default, useVerbalizedSampling is true. This means the twin generates 5 probability-weighted candidate responses and selects the most representative one, producing more consistent and reliable answers.
Step 6: Get Group Opinions
The most powerful feature — ask multiple twins the same question and get a summary with individual responses:
- cURL
- Python
- Node.js
- Go
curl -X POST "https://app.neuroflash.com/api/digital-twin-service/v1/workspaces/{workspace_id}/twin-group-chat-opinions" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"twinIds": ["twin-id-1", "twin-id-2", "twin-id-3", "twin-id-4", "twin-id-5"],
"question": "Would you switch to a plant-based diet if it were more affordable?"
}'
twin_ids = [t["id"] for t in twins["data"][:5]]
opinions = requests.post(
f"{BASE_URL}/digital-twin-service/v1/workspaces/{workspace_id}/twin-group-chat-opinions",
headers=headers,
json={
"twinIds": twin_ids,
"question": "Would you switch to a plant-based diet if it were more affordable?",
},
).json()
print(f"Summary: {opinions['summary']}\n")
for twin_id, response in opinions["twinResponses"].items():
print(f"Twin {twin_id}:")
print(f" Answer: {response['answer']}")
print(f" Reason: {response['reason']}\n")
const twinIds = twins.data.slice(0, 5).map((t) => t.id);
const opinions = await fetch(
`${BASE_URL}/digital-twin-service/v1/workspaces/${workspaceId}/twin-group-chat-opinions`,
{
method: "POST",
headers: { ...headers, "Content-Type": "application/json" },
body: JSON.stringify({
twinIds,
question:
"Would you switch to a plant-based diet if it were more affordable?",
}),
}
).then((r) => r.json());
console.log(`Summary: ${opinions.summary}\n`);
for (const [twinId, response] of Object.entries(opinions.twinResponses)) {
console.log(`Twin ${twinId}:`);
console.log(` Answer: ${response.answer}`);
console.log(` Reason: ${response.reason}\n`);
}
twinIDs := make([]string, 0, 5)
for _, t := range twinsResult.Data[:5] {
twinIDs = append(twinIDs, t.ID)
}
opinionsURL := fmt.Sprintf("%s/digital-twin-service/v1/workspaces/%s/twin-group-chat-opinions",
baseURL, workspaceID)
body, _ := json.Marshal(map[string]any{
"twinIds": twinIDs,
"question": "Would you switch to a plant-based diet if it were more affordable?",
})
req, _ := http.NewRequest("POST", opinionsURL, bytes.NewReader(body))
req.Header.Set("Authorization", "Bearer "+token)
req.Header.Set("Content-Type", "application/json")
resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()
var opinions struct {
Summary string `json:"summary"`
TwinResponses map[string]struct {
Answer string `json:"answer"`
Reason string `json:"reason"`
} `json:"twinResponses"`
}
json.NewDecoder(resp.Body).Decode(&opinions)
fmt.Printf("Summary: %s\n\n", opinions.Summary)
for id, r := range opinions.TwinResponses {
fmt.Printf("Twin %s:\n Answer: %s\n Reason: %s\n\n", id, r.Answer, r.Reason)
}
Response:
{
"summary": "The group shows mixed sentiment toward plant-based diets. Younger, urban twins are more open to the switch, citing environmental concerns and health benefits. Those in suburban or rural settings express more hesitation, preferring gradual changes. Price is a key factor for all — 4 out of 5 said affordability would significantly increase their willingness.",
"twinResponses": {
"twin-id-1": {
"answer": "Yes, I'd try it. I already eat less meat than my parents did.",
"reason": "As a health-conscious urban millennial, Jens is open to dietary changes..."
},
"twin-id-2": {
"answer": "I'd consider reducing meat, but going fully plant-based is a big step.",
"reason": "..."
}
}
}
Next Steps
- Learn about group chat conversations for multi-turn discussions
- Explore the Digital Twins API reference for all endpoints
- See Audiences to create custom target audiences