A user’s AI agent is a persona stored in TIE, shared across all apps on the platform. This page covers how to correctly create and manage that agent so your app never creates duplicates or forces users to redo setup they already completed in another app.
AI Coding Prompt
Drop this into Claude, Codex, or any AI coding assistant to implement correct agent creation for your app.
Implement TIE agent creation following these rules:
TIE is the cross-app source of truth for every user's AI agent. Each user has exactly
one non-preset persona on TIE, shared across all apps (BlinkLife, Vibrantly, etc).
BASE URL: https://your-tie-host
AUTH: All persona and voice endpoints use a Bearer token issued by TIE Auth for the
end user. Do NOT use a service key here — these are user-scoped endpoints.
---
STEP 1 — CHECK FOR EXISTING AGENT
GET /personas
Authorization: Bearer $USER_TOKEN
Response: array of persona objects. Each object has:
id: string (UUID)
name: string
system_prompt: string
archetype: string | null
description: string | null
is_preset: boolean ← presets are platform defaults; false = user's own agent
is_active: boolean ← true if currently active for this user
created_at: ISO datetime
updated_at: ISO datetime
Filter for is_preset: false. If one exists → GO TO "SKIP SETUP" below.
If none exists → GO TO STEP 2.
---
SKIP SETUP (persona already exists):
- Do NOT create another persona. Each user has exactly one non-preset persona.
- Do NOT prompt the user to choose a name, archetype, or voice.
- Voice is already stored on TIE (or falls back to gemini/Kore if never set).
- Mark this app's onboarding as complete in your own DB.
- Route the user directly into the app.
---
STEP 2 — CREATE THE PERSONA
POST /personas
Authorization: Bearer $USER_TOKEN
Content-Type: application/json
{
"name": "Aria",
"system_prompt": "You are Aria, a SAGE companion...",
"archetype": "SAGE",
"description": null // optional
}
Field rules:
name: required, 1–255 chars
system_prompt: required, min 1 char
archetype: optional, max 100 chars. Recommended values: SAGE, FRIEND, COACH, REBEL, NURTURER
description: optional
On success: 201 with the created persona object (same shape as GET /personas items).
Save the returned id — you need it for steps 3 and 4.
Errors:
422 VALIDATION_ERROR — invalid field values
503 PERSONA_SYSTEM_UNAVAILABLE — TIE persona store is down; surface an error to the user
---
STEP 3 — SET PERSONA AS ACTIVE
PUT /personas/active/{persona_id}
Authorization: Bearer $USER_TOKEN
Response: { "status": "active", "persona_id": "..." }
Errors:
404 PERSONA_NOT_FOUND — persona_id doesn't exist or isn't owned by this user
---
STEP 4 — SAVE VOICE PREFERENCE
PUT /v1/voice/preferences/{persona_id}
Authorization: Bearer $USER_TOKEN
Content-Type: application/json
{
"provider": "gemini",
"voice": "Kore",
"language": "en"
}
Field rules:
provider: string, default "gemini"
voice: required, one of: Kore, Puck, Charon, Aoede, Fenrir, Leda, Orus, Zephyr
language: required, BCP-47 code (e.g. "en")
Response: { "provider": "gemini", "voice": "Kore", "language": "en" }
If you skip this call, the persona falls back to gemini/Kore.
Errors:
404 PERSONA_NOT_FOUND — persona doesn't exist or not owned by user
503 SERVICE_UNAVAILABLE — voice store is down
---
VOICE PREVIEW (during voice selection UI):
POST /v1/audio/speech
Authorization: Bearer $USER_TOKEN
Content-Type: application/json
{
"model": "google/gemini-2.5-flash-tts",
"input": "Hi, I'm Aria. Nice to meet you.",
"voice": "Kore"
}
Returns PCM/WAV audio bytes. Play directly in browser or mobile.
---
GET SAVED VOICE (to display current selection):
GET /v1/voice/preferences/{persona_id}
Authorization: Bearer $USER_TOKEN
Returns: { "provider": "gemini", "voice": "Kore", "language": "en" }
Falls back to gemini/Kore/en defaults if never saved.
---
COMPLETE FLOW SUMMARY:
1. GET /personas → find is_preset: false
2a. Found → skip setup, mark app onboarding done, enter app
2b. Missing → POST /personas (save returned id)
→ PUT /personas/active/{id}
→ PUT /v1/voice/preferences/{id}
→ mark app onboarding done, enter appCore Rules
- One agent per user — each user has exactly one non-preset persona on TIE. Never create a second one.
- Always check first — call
GET /personasbefore creating. If a non-preset persona exists, skip setup entirely and mark this app’s onboarding complete. - TIE is source of truth — the agent and its voice preference live on TIE and are shared across every app.
archetypeis canonical — always set it on create so any app can read the personality type.- Voice is scoped to the persona — stored per-persona on TIE via
/v1/voice/preferences/{persona_id}. Collect it during setup; inherit it when skipping.
Flow
1. GET /personas → find existing non-preset persona
2a. Found → skip setup entirely, mark onboarding complete (voice already on TIE)
2b. Not found → run setup:
POST /personas to create
PUT /personas/active/{id}
PUT /v1/voice/preferences/{id} (selected Gemini voice for that persona)Step 1 — Check for existing agent
GET https://your-tie-host/personas
Authorization: Bearer $USER_TOKENFilter the response for is_preset: false. If one exists:
- The agent is already set up.
- The voice preference is also already set (or falls back to
gemini/Koreif never saved). - Skip setup entirely — do not prompt for name, archetype, or voice. Advance the user’s onboarding state past this step.
Step 2 — No agent: create one
POST https://your-tie-host/personas
Authorization: Bearer $USER_TOKEN
Content-Type: application/json
{
"name": "Aria",
"system_prompt": "You are Aria, a SAGE companion...",
"archetype": "SAGE"
}Set it as active immediately after:
PUT https://your-tie-host/personas/active/{persona_id}
Authorization: Bearer $USER_TOKENStep 3 — Save the chosen voice
PUT https://your-tie-host/v1/voice/preferences/{persona_id}
Authorization: Bearer $USER_TOKEN
Content-Type: application/json
{
"provider": "gemini",
"voice": "Kore",
"language": "en"
}Use the persona_id returned from POST /personas in Step 2. Accepted Gemini voice names: Kore, Puck, Charon, Aoede, Fenrir, Leda, Orus, Zephyr. If you skip this call, the persona falls back to the default (gemini/Kore).
For voice preview during selection, call POST /v1/audio/speech with { model: "google/gemini-2.5-flash-tts", input, voice } and play the returned PCM/WAV bytes. See Audio — Gemini TTS.