1 Sign in & grab your API key
Your tenant + first key are auto-provisioned on first sign-in.
- Open vilow.dev/dashboard/login.html
- Enter your work email → click Email me a link
- Click the magic link in your inbox → land in the dashboard
- Open the API keys tab — copy the
ck_live_…key
From now on every API request needs this header:
X-API-Key: ck_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
2 Big Five onboarding (optional)
Ask your end-user 15 short questions, get a personality vector you can drop straight into the character.
This step is optional — if you skip it, just leave big_five out of the create
request and we'll pick reasonable defaults (or generate them in auto-mode). But if your app
has a real onboarding flow, this is the cleanest way to ground the bot in real preferences.
Get the question list
# language can be 'en' or 'ru' — falls back to en for unknowns curl "https://api.vilow.dev/v1/onboarding/big-five/questions?language=en" \ -H "X-API-Key: ck_live_..."
You'll get back 15 items in this shape:
{
"language": "en",
"questions": [
{ "id": "o1", "trait": "openness",
"text": "I enjoy trying new things and experimenting." },
{ "id": "o2", ... },
...
]
}
Render each question to your user, collect a 1–5 score per question (1 = strongly disagree, 5 = strongly agree), then submit:
Score the answers
curl -X POST "https://api.vilow.dev/v1/onboarding/big-five/score" \ -H "X-API-Key: ck_live_..." \ -H "Content-Type: application/json" \ -d '{ "answers": { "o1": 4, "o2": 5, "o3": 2, "c1": 3, "c2": 4, "c3": 2, "e1": 4, "e2": 3, "e3": 3, "a1": 5, "a2": 5, "a3": 2, "n1": 2, "n2": 3, "n3": 4 } }'
You'll get a normalised big_five dict ready to pass to the character creator:
{
"big_five": {
"openness": 0.833,
"conscientiousness": 0.667,
"extraversion": 0.583,
"agreeableness": 0.917,
"neuroticism": 0.333
}
}
3 Register your end-user
Vilow uses an opaque external_id — pick whatever string you already use internally.
curl -X POST "https://api.vilow.dev/v1/users" \ -H "X-API-Key: ck_live_..." \ -H "Content-Type: application/json" \ -d '{ "external_id": "alice-42", "display_name": "Alice" }'
Response:
{
"id": 1,
"external_id": "alice-42",
"display_name": "Alice",
"created_at": "2026-04-26T15:00:00+00:00"
}
(tenant, external_id) is unique. Calling this twice with the same
ID returns 409. You can safely call it on every sign-in and ignore conflicts.
4 Create the character
Two modes. Pick one.
Option A — auto-generation
Give us the gender, locale, optional hint and the big_five from step 2.
We ask Grok to generate a name, persona, backstory and city in one call.
curl -X POST "https://api.vilow.dev/v1/users/alice-42/characters" \ -H "X-API-Key: ck_live_..." \ -H "Content-Type: application/json" \ -d '{ "generation_mode": "auto", "gender": "female", "locale": "ru", "default_language": "ru", "hint": "warm watercolor artist who lives in Tallinn", "big_five": { "openness": 0.83, "conscientiousness": 0.67, "extraversion": 0.58, "agreeableness": 0.92, "neuroticism": 0.33 } }'
Option B — manual
You pass everything; we just persist it. Useful for game studios with a fixed cast.
curl -X POST "https://api.vilow.dev/v1/users/alice-42/characters" \ -H "X-API-Key: ck_live_..." \ -d '{ "name": "Лена", "gender": "female", "persona": "warm and curious watercolor artist", "backstory": "lives in Tallinn, paints mornings, runs a small studio", "default_language": "ru", "big_five": {"openness": 0.83, "conscientiousness": 0.67, "extraversion": 0.58, "agreeableness": 0.92, "neuroticism": 0.33} }'
Either way you get back a Character with an integer id — keep it. You'll need it for every chat call.
Optional fields you can mix in
| Field | Purpose |
|---|---|
custom_traits | Free-form text from your end-user — quirks, fears, favourite quotes. Injected verbatim into the prompt. |
intimate_persona | Adult persona block. Only used after the user gives explicit intimate consent. Skipped on tenants with intimate_mode=off. |
default_language | auto mirrors the user, ISO codes pin the bot's language. |
5 Send your first chat message
One call. One round-trip. Reply, extracted facts, emotions, relationship deltas — all in the response.
curl -X POST "https://api.vilow.dev/v1/chat/alice-42/{character_id}/send" \ -H "X-API-Key: ck_live_..." \ -H "Content-Type: application/json" \ -d '{ "message": "Привет! Как у тебя сегодня настроение?" }'
What you get back
{
"reply": "Привет! *улыбается, откладывая кисть* Сегодня прекрасное настроение...",
"conversation_id": 1,
"message_id": 42,
// extracted automatically — saved to memory
"user_facts_saved": 0,
"self_facts_saved": 1,
// safety signals
"user_intimacy_level": 0.0,
"reply_intimacy_level": 0.0,
"intimate_blocked": false,
// relationship dynamics
"trust": 1.15,
"friendship": 1.30,
"relationship_stage": "strangers",
"relationship_events": [
{ "event": "first_meeting", "timestamp": "..." }
],
// LLM usage (already billed)
"usage": { "prompt_tokens": 600, "completion_tokens": 80, "total_tokens": 680 }
}
That's the loop. Keep calling /send with the same external_id and character_id — memory, relationship, emotions, life events all carry over automatically.
What if the user replies in another language?
If default_language is auto, the bot mirrors them. Otherwise the bot stays in the configured language. You can override on a per-call basis with "language": "en" in the payload.
What about *actions*?
By default the bot weaves small physical actions (*nods*, *smiles softly*) into 60-70% of replies. To turn this off (e.g. for a therapy bot), set "reply_style": "plain" in your tenant feature flags.
6 Where to go next
Once the basic loop works, layer in the features you need.
| Feature | Endpoint |
|---|---|
| Voice mode (TTS-clean text, optional audio) | POST /v1/chat/{ext}/{cid}/send-voice |
| Browse what the bot remembers | GET /v1/users/{ext}/characters/{cid}/memory |
| Forget a specific memory (right-to-be-forgotten) | DELETE …/memory/{id} |
| Trust / friendship / conflict state | GET …/relationship |
| Emotions, needs, bio cycle, secrets | GET …/personality |
| Background life — what the bot is up to | GET …/life |
| Diary — "while you were away" | GET …/diary |
| Pending bot promises | GET …/promises |
| Should the bot reach out unprompted? | POST …/proactive/check |
| Intimate mode consent | POST …/intimate/consent |
| Token usage / billing | GET /v1/usage/summary |
Full schema with request/response shapes lives at api.vilow.dev/docs — auto-generated, always in sync with the running service.
Per-tenant configuration
Most behaviour is tunable via feature flags on your tenant — relationship pace, life-event intensity, allowed themes, intimate mode, voice add-on, proactivity quiet hours, etc. Full reference of every flag. Open the Dashboard → Settings tab to edit them as JSON, or hit PATCH /v1/me/feature-flags programmatically.
Quotas & billing
Free covers 200 messages/month. After that you'll get HTTP 402 with one of these codes — your client can use them to drive UX:
| code | meaning |
|---|---|
monthly_quota_exhausted | Free plan ran out — upgrade or wait for next month. |
character_limit_reached | Tried to create more than the plan allows. |
character_over_plan_limit | Plan was downgraded; this character is no longer in the active slot. |
balance_too_low | Paid plan in overage — top up the balance. |
voice_addon_unavailable | Audio rendering needs Pro plan. |
subscription_inactive | Card declined and grace period expired. |