Runs — every generation is a tracked, replayable run.
Conversations — multi-turn chat against any persona, with drift detection.
You can call it three ways
Native REST + SSE — api.persona.dev/v1
OpenAI-compatible — drop in as a model provider.
MCP server — stream personas as tools to any MCP host.
All requests require a workspace-scoped API key. Personas, runs, and conversations are tenant-isolated; nothing crosses workspaces unless you publish to the marketplace.
{langPills}
{snippetsCreate[lang]}
Keys with pk_live_ prefix are unmasked once at creation and never again. Rotate within 90 days. Live keys cannot be used against test workspaces, and vice versa.
Create — request body
Free-form character brief (1–4000 chars). Required.>],
["language", "ISO-639-1", <>Persona's primary speaking language. Default en.>],
["visibility", "enum", <>One of private, workspace, public. Default private.>],
["pipeline", "string", <>Override the active pipeline (Team+). e.g. strict-audit.>],
["seed", "integer", <>Reproducibility seed. Pinned across the run.>],
["budget_usd", "number", <>Hard cost cap. Run aborts with 402 if exceeded.>],
["custom_fields","object", <>Workspace-defined fields registered via POST /v1/schema/fields.>],
]}/>
Persona object — top-level keys
{`curl https://api.persona.dev/v1/chat/conversations/conv_72/messages \\
-H "Authorization: Bearer $PERSONA_API_KEY" \\
-H "Content-Type: application/json" \\
-d '{
"persona_id": "psn_3kP9aLwX2v",
"message": "Sahafına yeni gelen bir kitabı anlatır mısın?",
"tools": ["web.search"],
"stream": true
}'`}
Response — assistant turn
{`{
"message_id": "msg_91xQ",
"role": "assistant",
"content": "Bu sabah elime, 1973 baskısı bir Yaşar Kemal geçti. Kapağı...",
"drift": { "voice": 0.08, "values": 0.04, "verdict": "in-character" },
"coherence": 4.6,
"tools_used": [],
"tokens": { "in": 1240, "out": 312 },
"cost_usd": 0.014
}`}
Base URL
{`POST /v1/openai/chat/completions
POST /v1/openai/embeddings
GET /v1/openai/models`}
Pass X-Persona-Conversation-Id to attach a completion to an existing conversation — drift and lineage will be tracked. Without it, each request is a one-shot.{`curl -N https://api.persona.dev/v1/personas?stream=true \\
-H "Authorization: Bearer $PERSONA_API_KEY" \\
-H "Accept: text/event-stream" \\
-d '{"intent": "..."}'`}
{`event: run.started
data: {"run_id":"run_a8K","step":"intent_parse"}
event: step.completed
data: {"step":"soul_draft","tokens":2104,"cost_usd":0.08}
event: layer.delta
data: {"path":"soul.core_desire","value":"To be seen ..."}
event: run.completed
data: {"persona_id":"psn_3kP9aLwX2v","total_cost_usd":0.79}`}
Event types
Verifying signatures
{`import crypto from "node:crypto";
export function verify(req, secret) {
const sig = req.headers["persona-signature"]; // "t=...,v1=..."
const [t, v1] = sig.split(",").map(p => p.split("=")[1]);
const payload = t + "." + req.rawBody;
const expected = crypto.createHmac("sha256", secret).update(payload).digest("hex");
if (!crypto.timingSafeEqual(Buffer.from(v1), Buffer.from(expected))) {
throw new Error("invalid signature");
}
if (Date.now() / 1000 - Number(t) > 300) throw new Error("stale");
}`}
{`# install
npm i -g @persona/cli
# log in (opens browser)
persona auth login
# bridge MCP over stdio for Claude Desktop / Cursor
persona mcp serve --workspace ws_acme_studio
# create a persona from a markdown brief
persona personas create -f ./brief.md --visibility workspace
# tail run events
persona runs tail run_a8K2zB
# pull a persona to a local YAML file (round-trips with edit + push)
persona personas pull psn_3kP9aLwX2v -o bookseller.yml`}{`{
"error": {
"code": "persona.cost_budget_exceeded",
"message": "Run aborted: $1.20 exceeds budget $1.00.",
"type": "billing",
"doc_url": "https://docs.persona.dev/errors#cost_budget_exceeded",
"request_id": "req_8f3c2e1a9b",
"run_id": "run_a8K2zB"
}
}`}
Status reference
Status
Code
When
400
request.invalid
Malformed body, unknown field, schema mismatch.
401
auth.invalid_key
Missing or revoked key.
402
persona.cost_budget_exceeded
Run hit hard budget cap.
403
moderation.intent_blocked
Intent blocked by moderation policy.
409
persona.slug_taken
Slug collision in workspace.
422
persona.schema_violation
Cross-layer consistency check failed.
429
rate_limit.exceeded
Slow down — see Retry-After.
503
provider.unavailable
Upstream model provider failure (fallback exhausted).