# Personas

A **persona** is the idea: what an agent says and how it sounds saying it. An **agent** is one way of putting that idea on a telephone. Point three agents at one persona and you have an inbound line, an outbound campaign and a web widget running the same script — with one prompt between them, edited once.

## The problem this splits

An agent carries everything on one record: the prompt *and* the carrier, the voice *and* the caller IDs, the knowledge *and* the direction. Because those sit together, one idea can only be deployed one way. An account that wants its collections script to answer the support line, chase callbacks *and* run in the widget ends up keeping three copies of the prompt — and the day somebody edits two of them, the third starts saying something else.

## What goes where

| The persona owns — the idea | The agent owns — the telephone |
| --- | --- |
| `languages` (prompt, greeting, voice, vendors) | `mode` — conversation, keypad, normal, webrtc |
| `language_detection` | `autonomy` — who holds its end of the call |
| `timezone`, `voice_engine` | `direction`, `carrier`, caller IDs |
| `knowledge_base_ids`, `tools` | `transfer_number`, `ring_seconds` |
| `flow` | `record_calls`, `transcribe_calls`, `webhook_url` |
| `conversation`, `keypad`, `no_match`, `no_input` | `project_id`, `group_id` |
| `post_call` | its own `name` and `description` |
| `model`, `models`, `temperature`, `max_tokens`, `speed`, `pitch`, `filler_phrase` |  |

A persona has no number, no carrier, no direction and no mode, because none of those are facts about the idea.

## Nothing changes until you ask

An agent that names no persona carries its idea inline and behaves exactly as it always has. This is additive: no migration runs, and no existing integration changes behaviour.

Adopting the split never means retyping a prompt. One request lifts an agent's whole idea — prompt, languages, knowledge, tools, flow, conversation settings, post-call fields, model and voice — into a new persona and points the agent at it.

`POST /v1/agents/{id}/persona`

**Shell**

```bash
curl -s -X POST https://voice.sphoro.com/v1/agents/$AGENT_ID/persona \
  -H "Authorization: Bearer $SPHORO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"name": "Collections script"}'
```

**Response201 Created**

```json
{
  "id": "psn_4a19c8e0b7d23f61",
  "name": "Collections script",
  "agent_count": 1
}
```

The agent keeps its inline copy, which is what lets a persona that cannot be loaded degrade to "this agent's own settings" rather than to a line with no prompt at all.

## Deploying it three ways

`PATCH /v1/agents/{id}`

**Shell**

```bash
# The inbound support line.
curl -s -X PATCH https://voice.sphoro.com/v1/agents/$INBOUND_AGENT \
  -H "Authorization: Bearer $SPHORO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"persona_id": "psn_4a19c8e0b7d23f61"}'

# The outbound chase campaign — same idea, different telephone.
curl -s -X PATCH https://voice.sphoro.com/v1/agents/$OUTBOUND_AGENT \
  -H "Authorization: Bearer $SPHORO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"persona_id": "psn_4a19c8e0b7d23f61", "carrier": "twilio"}'
```

Setting `persona_id` to `""` gives the agent its own idea back. The inline fields are still on the record, so detaching needs no restore step.

## The endpoints

| Endpoint | Does |
| --- | --- |
| `POST /v1/personas` | Create an idea from scratch. |
| `GET /v1/personas` | List them. |
| `GET /v1/personas/{id}` | Read one. |
| `PATCH /v1/personas/{id}` | Edit it — **changes every agent using it**, from the next call. |
| `DELETE /v1/personas/{id}` | Refused while any agent still names it. |
| `GET /v1/personas/{id}/agents` | Which lines are built on it. Read this before editing. |
| `POST /v1/agents/{id}/persona` | Lift an agent's inline idea out into one. |

Personas cost the same scope as agents — `agents:read` and `agents:write`. A persona *is* an agent's configuration, and a credential that may rewrite a prompt through `/v1/agents` is not made safer by being refused the endpoint holding the same prompt.

## Two rules worth knowing

> **The persona wins outright.** A field on a persona is not a default the agent may override. [Agent groups](https://voice.sphoro.com/docs/agent-groups) already provide a per-field override axis, and a second one would make "what does this agent actually run on" a three-way puzzle. If a line needs a different greeting, it needs a different persona — they are free.

**The pair is validated where it meets.** A persona is checked by the agent validator, as the agent it would produce, so the two can never disagree about what a valid prompt or language list is. That also catches combinations that are individually fine and jointly wrong — a keypad line whose persona defines no menu would answer, offer nothing, and hang up. The problem is reported against `persona.<field>`, naming the record that can fix it.

## Writing an idea field to an agent that has a persona

It is **refused**, not ignored. There the write could never take effect at read time, and a `200` for a change that can never happen is the worst of the available answers. On an agent that carries its idea inline, the same write still works and comes back with a deprecation header.

## Persona or group?

|  | Persona | Agent group |
| --- | --- | --- |
| Holds | The whole idea — prompt, voice, knowledge, flow | A handful of fleet-wide defaults |
| Members may differ | **No.** It wins outright | **Yes**, per field, via `overrides` |
| Reach for it when | One script on several telephones | Forty lines that should share a model and a transfer number |

They compose: an agent can name a persona *and* belong to a group. The persona settles what it says; the group settles the fleet-wide plumbing around it.
