---
title: "Create a persona"
method: POST
path: "/v1/personas"
tags: ["personas"]
---

# Create a persona

`POST /v1/personas`

## Request body

- PersonaCreate — Create a persona.
  - `name` string, required
  - `description` string, nullable
  - `is_active` boolean
  - `body` string, required
  - `traits` object
  - `behavior_config` PersonaBehaviorConfig — Advanced simulation knobs attached to a persona. Stored as JSONB on ``personas.behavior_config``; per-run overrides will reuse this shape on ``SimulationRun`` (ENG-474).
    - `first_speaker` 'persona' | 'agent'
    - `cut_in` PersonaCutInConfig — Persona-as-interrupter — when the agent is speaking, when does the persona cut in? Mirrors voice ``interruption_*`` knobs on ``VoicePipelineConfig``. ``min_words`` / ``min_duration_ms`` are **overrides on the ``interruption_mode`` preset**, not standalone thresholds. ``None`` (the default) means "use whatever the preset says"; an explicit value replaces the preset's floor for that dimension while the preset keeps supplying the per-turn jitter — the persona sets the floor, the mode sets the spread. They are nullable on purpose. Every persona write round-trips through this model and stores all defaults, so a non-null default would be indistinguishable from an author deliberately choosing that number, and every existing persona would silently become an explicit low-threshold interrupter. Setting either has no effect when ``interruption_mode == "off"`` — an override tunes interrupting, it doesn't switch it on.
      - `min_words` integer, nullable
      - `min_duration_ms` integer, nullable
      - `backoff_ms` integer
    - `language` string, nullable
    - `background_noise` 'none' | 'office' | 'cafe' | 'city' | 'keyboard_typing'
    - `background_noise_volume` number
    - `wait_seconds` number
    - `yield_on_overlap` PersonaYieldConfig — Persona-as-yielder — when the persona is speaking and the agent starts talking, when does the persona stop? Whichever threshold fires first triggers the yield. ``None`` disables that dimension.
      - `after_words` integer, nullable
      - `after_seconds` number, nullable
    - `interruption_mode` 'off' | 'low' | 'medium' | 'high'
    - `backchannel_style` 'neutral' | 'impatient' | 'polite'
    - `backchannel_overrides` string[], nullable
    - `reaction_pause_ms_min` integer
    - `reaction_pause_ms_max` integer
    - `text_burst` TextBurstConfig — Text-channel multi-message ("burst") behavior. ``enabled=False`` (default) ⇒ the persona sends one message per turn. When enabled, the persona is instructed to fragment a turn into several short bubbles (one per line). ``policy`` decides how the agent-under-test sees them: - ``live`` — each bubble is fed as its own turn; the agent replies to each (mirrors the production HTTP chat channel: no debounce). - ``debounced``— bubbles are newline-joined into ONE turn; the agent replies once (models a typing-debounce frontend).
      - `enabled` boolean
      - `policy` 'live' | 'debounced'

## Response `201`

Successful Response

- PersonaResponse
  - `id` string, uuid, required
  - `organization_id` string, uuid, required
  - `name` string, required
  - `description` string, nullable, required
  - `is_active` boolean, required
  - `body` string, required
  - `traits` object, required
  - `behavior_config` PersonaBehaviorConfig, required — Advanced simulation knobs attached to a persona. Stored as JSONB on ``personas.behavior_config``; per-run overrides will reuse this shape on ``SimulationRun`` (ENG-474).
    - `first_speaker` 'persona' | 'agent'
    - `cut_in` PersonaCutInConfig — Persona-as-interrupter — when the agent is speaking, when does the persona cut in? Mirrors voice ``interruption_*`` knobs on ``VoicePipelineConfig``. ``min_words`` / ``min_duration_ms`` are **overrides on the ``interruption_mode`` preset**, not standalone thresholds. ``None`` (the default) means "use whatever the preset says"; an explicit value replaces the preset's floor for that dimension while the preset keeps supplying the per-turn jitter — the persona sets the floor, the mode sets the spread. They are nullable on purpose. Every persona write round-trips through this model and stores all defaults, so a non-null default would be indistinguishable from an author deliberately choosing that number, and every existing persona would silently become an explicit low-threshold interrupter. Setting either has no effect when ``interruption_mode == "off"`` — an override tunes interrupting, it doesn't switch it on.
      - `min_words` integer, nullable
      - `min_duration_ms` integer, nullable
      - `backoff_ms` integer
    - `language` string, nullable
    - `background_noise` 'none' | 'office' | 'cafe' | 'city' | 'keyboard_typing'
    - `background_noise_volume` number
    - `wait_seconds` number
    - `yield_on_overlap` PersonaYieldConfig — Persona-as-yielder — when the persona is speaking and the agent starts talking, when does the persona stop? Whichever threshold fires first triggers the yield. ``None`` disables that dimension.
      - `after_words` integer, nullable
      - `after_seconds` number, nullable
    - `interruption_mode` 'off' | 'low' | 'medium' | 'high'
    - `backchannel_style` 'neutral' | 'impatient' | 'polite'
    - `backchannel_overrides` string[], nullable
    - `reaction_pause_ms_min` integer
    - `reaction_pause_ms_max` integer
    - `text_burst` TextBurstConfig — Text-channel multi-message ("burst") behavior. ``enabled=False`` (default) ⇒ the persona sends one message per turn. When enabled, the persona is instructed to fragment a turn into several short bubbles (one per line). ``policy`` decides how the agent-under-test sees them: - ``live`` — each bubble is fed as its own turn; the agent replies to each (mirrors the production HTTP chat channel: no debounce). - ``debounced``— bubbles are newline-joined into ONE turn; the agent replies once (models a typing-debounce frontend).
      - `enabled` boolean
      - `policy` 'live' | 'debounced'
  - `cloned_from_id` string, uuid, nullable
  - `created_by` string, nullable, required
  - `created_at` string, date-time, required
  - `updated_at` string, date-time, required

## Other responses

- `400` — Bad request
- `401` — Authentication required
- `422` — Validation Error

---

[API](https://skmtc.net/oneloop-hq/apis/feather-api.md) · [All operations](https://skmtc.net/oneloop-hq/apis/feather-api/llms.txt) · [OpenAPI document](https://skmtc-service-staging.skmtc.workers.dev/v1/apis/oneloop-hq/feather-api/revisions/5a5597ebb2d6/schema)
