---
title: "Create a Persona"
method: POST
path: "/context/brands/{slug}/personas"
tags: ["Personas"]
---

# Create a Persona

`POST /context/brands/{slug}/personas`

## Request

- Base URL: `https://api.adcrunch.dev`
- URL: `https://api.adcrunch.dev/context/brands/{slug}/personas`
- Auth: HTTP bearer

Creates a Persona on a Brand. A Persona describes one audience that the brand speaks to. Every section and both ends of the age range are optional, because you write a Persona one part at a time. The slug is unique in the Brand, not in your organization. The answer is `409` if the slug already exists on this Brand.

## Path parameters

- `slug` string, required

## Request body

- object
  - `age_max` integer: Upper age bound. Omit for "and older".
  - `age_min` integer: Lower age bound. Omit for "and younger".
  - `description` string, required: One-line summary — what agents read to pick this Persona.
  - `frictions` string: What stops them: objections, doubts, inertia (markdown).
  - `language` string: The words the audience itself uses for the problem — verbatims and vocabulary, in their own register (markdown). Not a locale.
  - `motivations` string: What they want: jobs to be done, triggers, outcomes (markdown).
  - `name` string, required: Display name.
  - `profile` string: Who they are: life stage, situation, role, context (markdown).
  - `slug` string: Optional explicit slug handle; normalized to kebab-case. Unique within this Brand, so two Brands may each hold the same one. Defaults to one derived from the name.

## Response `201`

The Persona as created, at `revision` 1. Every unwritten section comes back `null`, which is a normal state rather than an error.

- object
  - `ageMax` number, nullable, required
  - `ageMin` number, nullable, required: Each bound is independently optional, so "35+" is expressible.
  - `brandId` string, required
  - `createdAt` number, required: Milliseconds since the Unix epoch, UTC.
  - `createdBy` string, required
  - `description` string, required
  - `frictions` string, nullable, required: Objections, doubts, what stops them.
  - `id` string, required
  - `language` string, nullable, required: The words *they* use for the problem, in their own register. Not a locale, despite the name.
  - `motivations` string, nullable, required: What they want, and what triggers them.
  - `name` string, required
  - `profile` string, nullable, required: Who they are, and their situation.
  - `revision` number, required: Increments on every write. Send it back as `base_revision` to edit; a stale one answers 409.
  - `slug` string, required
  - `updatedAt` number, nullable, required: Milliseconds since the Unix epoch, UTC.

## Other responses

- `400`: The request does not match the schema of this operation: a field is missing or has the wrong type, or the body is not valid JSON. `error` is `invalid_request`, and `issues` names each field. Nothing was changed.
- `401`: No API key, or one that does not resolve. See the security scheme. `error` is `unauthorized`.
- `402`: The organization has no active subscription. An owner subscribes at `paywallUrl`. `error` is `organization_unentitled`.
- `403`: The caller does not hold `brand:write`. `error` is `forbidden`.
- `404`: No Brand with that slug. `error` is `not_found`.
- `409`: This Brand already has a Persona with that slug. `error` is `slug_conflict`. Another Brand may hold the same one — the scope of the uniqueness is the Brand, not the organization.
- `422`: Two codes share this status. `invalid_slug`: the name and slug carry no letter or digit. `invalid_age_range`: the minimum is above the maximum.

---

[API](https://skmtc.dev/adcrunch/apis/adcrunch-api.md) · [All operations](https://skmtc.dev/adcrunch/apis/adcrunch-api/llms.txt) · [OpenAPI document](https://skmtc.dev/adcrunch/apis/adcrunch-api/revisions/a420ba80914f?raw)
