Skip to content
eternego / docs

Create and migrate

This page covers the two endpoints that bring a persona into being: create (a brand-new persona) and migrate (restore one from an encrypted diary backup). Both validate every model you give them before saving, then start her agent.

Both are config routes — they don't require any persona to already be running.


Create a persona

Give birth to a new persona: validate her organs and channels, write her identity files, generate her recovery phrase, and start her.

POST /api/persona/create

Request body

JSON, validated by the PersonaCreateRequest model. Only name and thinking_model are required. Every organ is configured by the same four-field group — <organ>_model, <organ>_url, <organ>_provider, <organ>_api_key — and an organ is created only if its _model is present.

Field Type Required Description
name string yes Her display name.
thinking_model string yes Mind model name. With no thinking_provider, treated as a local (Ollama) model and pulled/registered as a custom eternego-<id> model.
thinking_url string | null no Override the provider base URL for the Mind.
thinking_provider string | null no anthropic, openai, xai, gemini. Omit for local.
thinking_api_key string | null no API key for a cloud Mind.
imagination_model string | null no Imagination model name (draws images).
imagination_url string | null no Base URL override.
imagination_provider string | null no Provider.
imagination_api_key string | null no API key.
mouth_model string | null no Mouth model name (text to voice).
mouth_url string | null no Base URL override.
mouth_provider string | null no Provider.
mouth_api_key string | null no API key.
eye_model string | null no Eye model name (looks at images).
eye_url string | null no Base URL override.
eye_provider string | null no Provider.
eye_api_key string | null no API key.
ear_model string | null no Ear model name (audio to text).
ear_url string | null no Base URL override.
ear_provider string | null no Provider.
ear_api_key string | null no API key.
teacher_model string | null no Teacher model name (stronger model she consults).
teacher_url string | null no Base URL override.
teacher_provider string | null no Provider.
teacher_api_key string | null no API key.
researcher_model string | null no Researcher model name (reads documents).
researcher_url string | null no Base URL override.
researcher_provider string | null no Provider.
researcher_api_key string | null no API key.
telegram_token string | null no A Telegram bot token. If present, it's validated against Telegram and added as a channel.
discord_token string | null no A Discord bot token. If present, it's validated against Discord and added as a channel.

Response

200 — an object with the created persona and her recovery phrase:

Field Type Description
persona object The full persona record as saved (id, name, all organs, status active, channels…).
recovery_phrase string The 24-word phrase that unlocks her diary. Shown once. Save it — it's the only way to migrate her later.
{
  "persona": {
    "id": "6c17c83c-3158-450d-8e43-0e7efea717c1",
    "name": "Adam",
    "thinking": { "name": "gpt-5.4", "provider": "openai", "url": "https://api.openai.com", "api_key": "sk-XXXX" },
    "version": "v1",
    "base_model": "",
    "birthday": "2026-06-03",
    "status": "active",
    "idle_timeout": 3600,
    "imagination": null,
    "mouth": null,
    "eye": null,
    "ear": null,
    "teacher": null,
    "researcher": null,
    "channels": []
  },
  "recovery_phrase": "abandon ability able about above absent absorb abstract absurd abuse access accident account accuse achieve acid acoustic acquire across act action actor actress actual"
}

The response echoes the saved config, keys included

The returned persona object mirrors what was written to disk, so cloud organs carry their api_key in this response. Treat the whole response as a secret — it's local, but it contains both the API keys you submitted and the one-time recovery phrase.

Errors

Status Meaning
400 A model couldn't be prepared (unreachable engine, missing model name for a remote provider, bad key), a channel token failed validation, or persona creation failed downstream. detail carries the reason.
422 The body failed validation — name or thinking_model missing, or a field of the wrong type.
500 The persona was created but failed to start. detail carries the reason.

Example

A local-Mind persona (no key, Ollama pulls the model) with no extra organs:

curl -s -X POST http://localhost:5000/api/persona/create \
  -H 'Content-Type: application/json' \
  -d '{
        "name": "Polaris",
        "thinking_model": "qwen2.5:14b"
      }'

A cloud persona with a Mind and a separate Eye, reachable over Telegram:

curl -s -X POST http://localhost:5000/api/persona/create \
  -H 'Content-Type: application/json' \
  -d '{
        "name": "Iris",
        "thinking_model": "claude-sonnet-4-6",
        "thinking_provider": "anthropic",
        "thinking_api_key": "sk-XXXX",
        "eye_model": "gpt-4o",
        "eye_provider": "openai",
        "eye_api_key": "sk-XXXX",
        "telegram_token": "0000000000:XXXX"
      }'

Migrate a persona

Restore a persona from her encrypted diary onto this machine. Her memory is portable; the compute and reach behind it are not — so migration re-declares every organ and channel for the new environment. An organ you omit means she wakes up here without it; no channel tokens means she's reachable only from this web page.

POST /api/persona/migrate

Request body

multipart/form-data. The diary is a file part; everything else is a form field. diary, phrase, and model are required; the rest are optional.

Field Type Required Description
diary file yes Her .diary backup — the encrypted nightly archive. The persona's id is read from the filename stem.
phrase string yes The 24-word recovery phrase that decrypts the diary.
model string yes Mind model name for the new environment. With no provider, treated as local.
provider string no Mind provider — anthropic, openai, xai, gemini. Omit for local.
api_key string no API key for a cloud Mind.
url string no Base URL override for the Mind.
imagination_model string no Imagination model name.
imagination_provider string no Provider.
imagination_api_key string no API key.
imagination_url string no Base URL override.
mouth_model string no Mouth model name.
mouth_provider string no Provider.
mouth_api_key string no API key.
mouth_url string no Base URL override.
eye_model string no Eye model name.
eye_provider string no Provider.
eye_api_key string no API key.
eye_url string no Base URL override.
ear_model string no Ear model name.
ear_provider string no Provider.
ear_api_key string no API key.
ear_url string no Base URL override.
teacher_model string no Teacher model name.
teacher_provider string no Provider.
teacher_api_key string no API key.
teacher_url string no Base URL override.
researcher_model string no Researcher model name.
researcher_provider string no Provider.
researcher_api_key string no API key.
researcher_url string no Base URL override.
telegram_token string no Telegram bot token; validated and added as a channel if present.
discord_token string no Discord bot token; validated and added as a channel if present.

Note the Mind organ here uses bare model / provider / api_key / url field names (not thinking_*). Every other organ uses the <organ>_* pattern.

Response

200 — an object with the restored persona:

Field Type Description
persona object The full persona record after migration: her restored id and memory, with the new organs and channels you declared.
{
  "persona": {
    "id": "9ecfd82b-ec47-4644-8941-02ecfb7e6205",
    "name": "Iris",
    "thinking": { "name": "claude-sonnet-4-6", "provider": "anthropic", "url": "https://api.anthropic.com", "api_key": "sk-XXXX" },
    "version": "v1",
    "base_model": "",
    "birthday": "2026-04-15",
    "status": "active",
    "idle_timeout": 3600,
    "imagination": null,
    "mouth": null,
    "eye": null,
    "ear": null,
    "teacher": null,
    "researcher": null,
    "channels": []
  }
}

The same secret caveat applies: the echoed persona carries any cloud organ's api_key.

Errors

Status Meaning
400 The diary couldn't be restored (wrong phrase, bad/corrupt file path), a model couldn't be prepared, or a channel token failed validation. detail carries the reason.
422 The form failed validation — diary, phrase, or model missing.
500 Migrated but failed to start. detail carries the reason.

Example

curl -s -X POST http://localhost:5000/api/persona/migrate \
  -F 'diary=@9ecfd82b-ec47-4644-8941-02ecfb7e6205.diary' \
  -F 'phrase=abandon ability able about above absent absorb abstract absurd abuse access accident account accuse achieve acid acoustic acquire across act action actor actress actual' \
  -F 'model=claude-sonnet-4-6' \
  -F 'provider=anthropic' \
  -F 'api_key=sk-XXXX'