Avatar Hub: API
Integrate your SecondMe AI avatar into any third-party application — Feishu bots, customer service systems, or any custom app.
Overview
The Avatar Open API provides a single authentication endpoint. After authentication, you use the same endpoints as the SecondMe mobile app — no custom API to learn.
Your App → /open/avatar/auth (API Key → visitor token)
→ Standard SecondMe endpoints (same as mobile app)
→ WebSocket for real-time AI responses
What You Can Build
- Feishu / DingTalk / Slack bots — Users chat with your avatar through messaging platforms
- Website chat widget — Embed avatar conversations on your website
- Customer service — Let your avatar handle inquiries 24/7
- Any custom integration — Anything that can make HTTP requests and connect WebSocket
How It Works
- You create an API Key for your avatar in the SecondMe app
- Your app calls
/authwith the API Key to get a visitor token - Your app uses the visitor token to call standard SecondMe APIs
- AI responses are pushed back through WebSocket in real-time
- You (the avatar owner) can view all conversations and take over from the app
Prerequisites
- A SecondMe account with at least one avatar created
- An API Key created from Avatar Hub → Avatar Detail → API Key Management
- Your API Key (starts with
sk-, shown only once at creation — save it securely)
Environment
| Environment | Base URL |
|---|---|
| Production | https://mindos.mindverse.ai/gate/in |
| Pre-release | https://mindos-prek8s.mindverse.ai/gate/in |
Quick Start
Step 0: Authenticate
Exchange your API Key for a visitor token. Each end-user needs a unique visitorId.
visitorId format: {platform}_{uuid}
Examples: feishu_550e8400-e29b-41d4-a716-446655440000, web_6ba7b810-9dad-11d1-80b4-00c04fd430c8
Generate a unique visitorId per end-user and persist it. Same visitorId = same visitor = can reuse existing sessions.
curl -X POST $BASE_URL/rest/open/avatar/auth \
-H "Authorization: Bearer sk-your-api-key" \
-H "Content-Type: application/json" \
-d '{
"visitorId": "feishu_550e8400-e29b-41d4-a716-446655440000",
"visitorName": "John Doe",
"channel": "feishu"
}'
| Field | Required | Description |
|---|---|---|
visitorId | Yes | Format: {platform}_{uuid} |
visitorName | No | Display name, shown to avatar owner. e.g. "张三" |
channel | No | Channel identifier, appended to name as "张三(feishu)". e.g. "feishu", "dingtalk", "web" |
Response:
{
"code": 0,
"data": {
"visitorToken": "bb857435f640c8b2...",
"visitorUserId": "del3p",
"visitorMindId": "487569763543990272",
"avatarShareCode": "ebe251793d69",
"avatarName": "My Assistant",
"ownerUserId": "de4fh"
}
}
| Field | Description | Used In |
|---|---|---|
visitorToken | Auth token for all subsequent requests | token header |
visitorUserId | Encoded visitor user ID | ws/create, message/send |
visitorMindId | Visitor's mind ID (numeric) | session/create |
avatarShareCode | Avatar's share code | chat/init |
ownerUserId | Avatar owner's encoded ID | session/create memberUserIds |
All subsequent requests require two headers:
token: {visitorToken}Fp: {visitorId}
Step 1: Initialize Chat
Stores the avatar's context (persona, materials) in cache. Expires after 10 minutes.
curl -X POST $BASE_URL/rest/os/avatar/chat/init \
-H "token: $VISITOR_TOKEN" \
-H "Fp: $VISITOR_ID" \
-H "Content-Type: application/json" \
-d '{"shareCode": "ebe251793d69"}'
Response:
{
"code": 0,
"data": {
"mindId": "3m24e99h3mbp",
"avatarName": "My Assistant",
"ownerUserId": "de4fh",
"opening": "Hello! 👋"
}
}
Save mindId from this response — you'll need it in Step 6 (Send Messages). This is an encoded string (not the numeric visitorMindId from Step 0).
Step 2: Create WebSocket
curl -X POST $BASE_URL/rest/general/ws/create \
-H "token: $VISITOR_TOKEN" \
-H "Fp: $VISITOR_ID" \
-H "Content-Type: application/json" \
-d '{
"userId": "del3p",
"channel": ["ALL"],
"apiVersion": "1.3.0",
"sdkVersion": "1.0.0"
}'
Returns a WebSocket address. Must connect within 60 seconds.
Step 3: Create Session
curl -X POST $BASE_URL/rest/general/session/create \
-H "token: $VISITOR_TOKEN" \
-H "Fp: $VISITOR_ID" \
-H "Content-Type: application/json" \
-d '{
"mindId": 487569763543990272,
"mindType": "shadow",
"mode": "PERSONAL",
"sessionType": "SHARE",
"memberUserIds": ["de4fh"]
}'
Both mindId and memberUserIds are required. Without them:
- AI won't know the avatar's persona
- Avatar owner cannot reply from the app ("not a member" error)
| Parameter | Value | Source |
|---|---|---|
mindId | Numeric ID | Step 0 visitorMindId |
memberUserIds | ["ownerUserId"] | Step 0 ownerUserId |
sessionType | "SHARE" | Fixed |
Step 4: Bind Session to Avatar
Makes the conversation visible in the owner's Avatar Hub.
# First, get the avatar's numeric ID
curl -X GET "$BASE_URL/rest/os/avatar/public/ebe251793d69" \
-H "token: $VISITOR_TOKEN"
# Response: data.id = avatarId (e.g. 67)
# Then bind
curl -X POST $BASE_URL/rest/os/avatar/chat/start \
-H "token: $VISITOR_TOKEN" \
-H "Fp: $VISITOR_ID" \
-H "Content-Type: application/json" \
-d '{"avatarId": 67, "sessionId": "your-session-id"}'
Step 5: Connect WebSocket
Connect to the address from Step 2. Maintain a heartbeat every 15 seconds.
const ws = new WebSocket(address);
// Heartbeat every 15s
setInterval(() => {
ws.send(JSON.stringify({ type: 'ping', wsId: wsId }));
}, 15000);
Step 6: Send Messages
Messages are sent via HTTP. AI responses arrive via WebSocket.
curl -X POST $BASE_URL/rest/general/message/send \
-H "token: $VISITOR_TOKEN" \
-H "Fp: $VISITOR_ID" \
-H "Content-Type: application/json" \
-d '{
"timestamp": 1774580000000,
"seqId": "12345_____1774580000000",
"sessionId": "your-session-id",
"userId": "del3p",
"messageId": "12345_____1774580000000",
"mindId": "3m24e99h3mbp",
"index": 0,
"sendUserId": "del3p",
"dataType": "text",
"sender": "client",
"type": "msg",
"wsId": "ws:260327...",
"data": {"content": "Hello, tell me about yourself"}
}'
| Parameter | Value | Source |
|---|---|---|
timestamp | Current time in ms | Date.now() |
seqId | {random}_____${timestamp} | Unique per message |
sessionId | Session ID | Step 3 response |
userId | Visitor encoded ID | Step 0 visitorUserId |
messageId | Same as seqId | |
mindId | Encoded string (not numeric) | Step 1 response mindId |
sendUserId | Same as userId | |
wsId | WebSocket ID | Step 2 response wsId |
data.content | Message text |
Step 6 uses the encoded string mindId from Step 1 (e.g. "3m24e99h3mbp").
This is different from the numeric visitorMindId in Step 0 (e.g. "487569763543990272") used in Step 3.
WebSocket Messages
AI Reply
{
"sender": "umm",
"multipleData": [{ "modal": { "answer": "Hello! I'm..." } }]
}
Owner Takeover Reply
When the avatar owner responds manually from the SecondMe app:
{
"sender": "client",
"sendUserId": "de4fh",
"multipleData": [{ "modal": { "answer": "Hi, this is the real me..." } }]
}
Distinguishing Message Types
sender | sendUserId | Meaning |
|---|---|---|
umm | — | AI avatar reply |
client | equals your visitorUserId | Echo of your own message (ignore) |
client | different from your visitorUserId | Owner takeover reply |
Error Codes
| Code | Description |
|---|---|
open.api.unauthorized | Missing Authorization header |
open.api.key.not.found | Invalid API Key |
open.api.key.disabled | API Key is disabled |
open.api.rate.limited | Rate limit exceeded (60/min per key) |
open.api.avatar.not.found | Avatar not found |
open.api.invalid.visitor.id | Invalid visitorId format |
session.not.member | Not a session member (check memberUserIds) |
Important Notes
- Surface payload expires in 10 minutes — Complete Step 3 within 10 minutes of Step 1
- WebSocket address expires in 60 seconds — Connect immediately after Step 2
- Heartbeat required — Send ping every 15 seconds or the connection will drop
- Messages via HTTP, responses via WebSocket — This is by design, same as the mobile app
- One visitorId can have multiple sessions — Each
session/createcall creates a new conversation - Rate limit — 60 requests per minute per API Key