Build with the AI

Personas

Attach one of your AI personas to completions, and manage personas over the API.

A persona is a named, reusable system prompt (plus optional sampling defaults) that shapes how the model responds. There are two things you can do with personas through the API:

Attach a persona to completions

When a persona is in effect, its system_prompt is inserted as a leading system message ahead of the messages you send to /api/v1/chat/completions. The persona text is only ever sent upstream to the model — it is never echoed back in the response body. There are two ways to select the persona for a request, in priority order.

Pin a persona to your app

Open the Developer console at /ops/dev, edit your app, and set its AI persona. Once pinned, that persona is applied to every /chat/completions call made with any key belonging to that app — no per-request field required. Leave it unset for no persona (the default).

If a pinned persona is later deleted, calls do not fail — the stale default is silently skipped and the request runs with no persona. Only an explicit per-request persona that isn’t usable is an error (see below).

Override per request

Add a persona field to the completion body, set to the integer id of the persona. A per-request persona overrides whatever is pinned to the app for that one call.

curl https://exe.krallo.com/api/v1/chat/completions \
  -H "Authorization: Bearer krallo_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "model": "krallo-chat",
    "persona": 42,
    "messages": [{"role": "user", "content": "Draft a one-line release note."}]
  }'

The response is a standard OpenAI chat.completion object, unchanged in shape whether or not a persona is attached. See Chat completions for the full request and response reference.

Which personas you can use

A persona is usable in a completion only if it is one of the following:

Guild-scoped personas (owned by a server rather than a user) are deliberately not usable through the developer API. If you send a persona id that doesn’t exist or isn’t one of the above, the request returns 400 and the model is never run (and you are not billed for it):

{
  "error": {
    "message": "Persona '42' was not found or does not belong to you.",
    "type": "invalid_request_error"
  }
}

What attaching a persona changes

Attaching a persona affects only the system prompt. Specifically:

BehaviorEffect of attaching a persona
System promptThe persona’s system_prompt is prepended as a leading system message. If your first message is already a system message, the persona text is merged in ahead of it.
LengthThe prompt is clamped to PROMPT_MAX_PERSONA_SYSTEM_CHARS characters (default 6000) at completion time. Anything longer is truncated with a short marker, so a giant persona can’t starve your conversation budget. You can still store a system_prompt up to 12,000 characters (see limits below).
Sampling (temperature, max_tokens, …)Not applied. A persona’s stored sampling fields are for the persona record itself; developer-API completions do not inherit them. Set temperature / max_tokens on the completion request body if you want to tune sampling.
ModelUnaffected — the model is chosen by the request’s model field only.

Manage personas over the API

The native /api/v1/personas resource is standard REST returning the Krallo envelope ({ "data": …, "meta": { "request_id": … } }), not the OpenAI wire format. Reads require the personas:read scope; writes require personas:write. Each call is flat-metered against your wallet — see Billing & quotas.

Method & pathScopeDescription
GET /api/v1/personaspersonas:readList the personas you own.
POST /api/v1/personaspersonas:writeCreate a persona.
GET /api/v1/personas/{id}personas:readRetrieve one persona.
PATCH /api/v1/personas/{id}personas:writePartially update a persona (PUT is also accepted).
DELETE /api/v1/personas/{id}personas:writeDelete a persona.

“Usable” is broader than “manageable.” The CRUD endpoints operate only on personas you own. Built-in SYSTEM and global personas are usable in completions but do not appear in GET /api/v1/personas and cannot be updated or deleted — attempting to do so returns 404.

The persona object

Every persona endpoint returns objects with this shape:

{
  "id": 128,
  "name": "Release Notes Writer",
  "description": "House voice for changelog entries",
  "system_prompt": "You write concise, factual release notes...",
  "icon_emoji": "📝",
  "temperature": 0.4,
  "max_tokens": 1200,
  "top_p": null,
  "frequency_penalty": null,
  "presence_penalty": null,
  "locked": false,
  "message_count": 0
}
FieldTypeDescription
idintegerPersona id. Read-only; use it as the persona value on completions and in the path.
namestringDisplay name.
descriptionstring · nullableShort human note. Not sent to the model.
system_promptstringThe instruction text prepended to completions.
icon_emojistring · nullableEmoji used in the console UI.
temperaturenumber · nullableStored sampling default (0–2). Not applied by developer-API completions.
max_tokensinteger · nullableStored default (1–32000). Not applied by developer-API completions.
top_pnumber · nullableStored nucleus-sampling default (0–1).
frequency_penaltynumber · nullableStored default (−2–2).
presence_penaltynumber · nullableStored default (−2–2).
lockedbooleanRead-only. A locked persona cannot be edited or deleted (returns 409).
message_countintegerRead-only usage counter.

Create a persona

POST/api/v1/personas

name and system_prompt are required; every other field is optional. The request is validated before it is billed, so a rejected create costs nothing. On success you get 201 with the full persona object.

curl https://exe.krallo.com/api/v1/personas \
  -H "Authorization: Bearer krallo_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Release Notes Writer",
    "system_prompt": "You write concise, factual release notes. No hype.",
    "description": "House voice for changelog entries",
    "icon_emoji": "📝",
    "temperature": 0.4,
    "max_tokens": 1200
  }'
{
  "data": {
    "id": 128,
    "name": "Release Notes Writer",
    "description": "House voice for changelog entries",
    "system_prompt": "You write concise, factual release notes. No hype.",
    "icon_emoji": "📝",
    "temperature": 0.4,
    "max_tokens": 1200,
    "top_p": null,
    "frequency_penalty": null,
    "presence_penalty": null,
    "locked": false,
    "message_count": 0
  },
  "meta": { "request_id": "req_..." }
}

Update a persona

PATCH/api/v1/personas/{id}

Updates are partial: send only the fields you want to change, and any field you omit is left unchanged. (There is no way to reset an advanced field back to null through the API — omitting it means “keep the current value.”) The same length and range rules as create apply to whatever you do send. PUT behaves identically. On success you get 200 with the updated object.

curl -X PATCH https://exe.krallo.com/api/v1/personas/128 \
  -H "Authorization: Bearer krallo_live_..." \
  -H "Content-Type: application/json" \
  -d '{ "temperature": 0.2, "system_prompt": "You write terse, factual release notes." }'

The model, locked, and message_count fields are read-only. Any model value in the body is ignored — model selection for completions is controlled by the request’s model field, not the persona.

List, retrieve, and delete

GET /api/v1/personas returns an array (in data) of the personas you own; GET /api/v1/personas/{id} returns a single object, or 404 if the id is unknown or not yours. Deleting returns a small confirmation:

DELETE/api/v1/personas/{id}
{
  "data": { "id": 128, "deleted": true },
  "meta": { "request_id": "req_..." }
}

Locked personas

A persona with "locked": true is protected. A PATCH/PUT or DELETE against it returns 409 with code persona_locked and leaves the persona untouched. Locked personas are still usable in completions.

Field limits and validation

Validation runs before billing on both create and update. A violation returns 400 with code invalid_request and a message naming the offending field. A present-but-wrong-typed value (for example a string where a number is expected) is rejected rather than silently dropped.

FieldTypeRequiredConstraint
namestringYes (create)Non-blank, at most 120 characters.
system_promptstringYes (create)Non-blank, at most 12,000 characters stored. Clamped to ~6,000 at completion time.
descriptionstringNoAt most 600 characters.
icon_emojistringNoAt most 16 characters.
temperaturenumberNoBetween 0 and 2.
max_tokensintegerNoBetween 1 and 32,000.
top_pnumberNoBetween 0 and 1.
frequency_penaltynumberNoBetween −2 and 2.
presence_penaltynumberNoBetween −2 and 2.

Errors

Persona endpoints can return the following. See Errors for the full catalog and envelope details.

StatusCodeWhen
400invalid_requestCRUD validation failed (missing/blank name or system_prompt, a field over its length limit, or a value out of range / wrong type).
400invalid_request_errorCompletions only: an explicit persona id that doesn’t exist or isn’t usable by you.
401unauthorized · invalid_tokenMissing or invalid bearer key. See Authentication.
403insufficient_scopeThe key lacks personas:read (reads) or personas:write (writes). See Scopes.
404not_foundThe persona id is unknown, or it isn’t one you own (including SYSTEM/global personas on write).
405method_not_allowedUnsupported method for the path.
409persona_lockedAttempted to edit or delete a locked persona.
429rate_limitedPer-key rate limit exceeded. See Rate limits.
500internal_errorUnexpected server error; the metered call is refunded.

Related pages