MaveraMavera Docs
Audiences
Checking…
Use or share this pageSend the page itself, print it, or open its complete context in your AI workspace.

Chat with a persona

1:1 conversations with memory.

3 min readDocs build Updated Jul 26, 2026

Endpoint: POST /api/v1/chat (alias of POST /api/v1/tools/chat_with_audience). Runs on the CONVERSATION tier (Opus) for realism. Every turn meters tokens → credits.

The full input schema

FieldTypeWhat it does
message (required)stringWhat you say to the persona this turn.
audienceIdstringDraw a respondent from this audience. (Provide this or personaId.)
personaIdstringTalk to one specific persona (sustained interviews, diary studies).
chatIdstringThe memory handle. Returned on turn 1; pass it back on every later turn.
audienceVersionIdstringPin a specific audience version for reproducibility.
seedintegerDeterministic respondent draw + sampling — same seed, same person.
interviewGuidestring[]Topics/questions to steer toward; also focuses world-awareness.
branchFromMessageIdstringFork a new line of inquiry from an earlier turn without disturbing the main thread.
effortlow·medium·highReasoning depth (adaptive thinking budget). Higher = more considered, more tokens.
verbosityterse·natural·detailedAnswer length.
worldAwarebooleanPrime the persona with current, dated, sourced real-world facts (live web search).
awarenessTopicstringFocus the world-awareness lookup (e.g. "SMB accounting tools 2026").
showGroundingPerTurnbooleanReturn the evidence refs that shaped each reply.
modelTierstringOverride the model tier (defaults to CONVERSATION/Opus).

The response shape

Response
{ "ok": true, "data": {
  "chatId": "chat_abc123",                     // ← keep this; it's the memory handle
  "reply": {
    "messageId": "msg_7",
    "content": "Honestly? Month-end is a mess of spreadsheets...",
    "abstained": false,                         // true when the persona declined (out of role/knowledge)
    "groundingRefs": [ { "sourceId": "src_cadence_stripe", "quote": "..." } ]
  },
  "transcript": [
    { "messageId": "msg_6", "role": "user", "content": "Walk me through your month-end close." },
    { "messageId": "msg_7", "role": "persona", "content": "Honestly? ..." }
  ],
  "honesty": {
    "synthetic": true,                          // ALWAYS true — this is a synthetic respondent
    "groundingStrength": "grounded",            // ungrounded | weak | moderate | grounded
    "worldAwareness": { "asOf": "2026-06-30", "sources": ["..."] }   // present when worldAware:true
  }
} }

Turn-by-turn: the one rule that matters

Open — no chatId

POST /api/v1/chat
{ "audienceId": "aud_f1_1", "message": "Walk me through your month-end close.", "seed": 42 }
# → data.chatId = "chat_abc123"

Continue — always pass chatId back

POST /api/v1/chat
{ "audienceId": "aud_f1_1", "chatId": "chat_abc123",
  "message": "You said spreadsheets — what breaks first as you grow?" }

No chatId = no memory. A call without it starts a brand-new conversation every time. It'll sound coherent (the persona is re-anchored) but it will not remember anything you said before. 90% of "the persona forgot what I told it" bugs are a missing chatId.

Branch — explore a tangent without losing the thread

POST /api/v1/chat
{ "audienceId": "aud_f1_1", "chatId": "chat_abc123",
  "branchFromMessageId": "msg_7",
  "message": "Hypothetically, if pricing doubled, does your answer change?" }

The branch forks from that message; your main line stays intact. For automated divergence exploration see Branching & flip-tests.

Realism controls, in depth

effort — how hard the persona thinks

low for quick gut reactions (cheaper, faster), high for considered, multi-factor reasoning (a CFO weighing a purchase). Maps to an adaptive thinking-token budget.

verbosity — how much they say

terse = one or two sentences (great for surveys-as-chat), detailed = a fuller narrative (great for discovery interviews).

worldAware — break the training-cutoff frozen-in-time problem

By default a model persona doesn't know what happened after its training cutoff. With worldAware:true, Mavera runs an out-of-character web search before the conversation, distills dated, sourced facts a person in that role would plausibly know, and injects them into the persona's context. The persona references the substance naturally and never says "I looked it up."

POST /api/v1/chat
{ "audienceId": "aud_f1_1",
  "message": "What worries you about the market right now?",
  "worldAware": true,
  "awarenessTopic": "SMB fintech and interest rates, 2026" }
# → honesty.worldAwareness = { asOf: "2026-06-30", sources: ["...", "..."] }

The facts are real and cited; the opinions stay synthetic. World-awareness changes what the persona knows, never fabricates what they think.

Abstention — a feature, not a failure

When you ask something outside what that person would credibly know or decide, the reply comes back with abstained: true:

{ "reply": { "content": "That's not my call — finance owns the budget. I can tell you what I'd push for, but I'm not signing it.", "abstained": true } }

This is the bright line protecting you: a synthetic founder won't invent their CFO's exact budget. Treat a cluster of abstentions as a signal you're probing past this persona's role — switch to a personaId who would own that decision, or a different audience.

Talking to one specific person

Pass personaId instead of audienceId to hold a sustained relationship with a single synthetic individual — essential for depth interviews and diary studies where continuity of character matters across many turns.

Cost & headers

Each turn meters tokens → credits (CONVERSATION tier). The response carries X-Credits-Used and X-Tokens-Total; your running balance is on /account and GET /api/v1/usage. Use verbosity:"terse" + effort:"low" for high-volume fielding to control spend.

Recipe: a 4-turn discovery interview

# turn 1 — open (capture chatId)
POST /api/v1/chat { "audienceId":"aud_f1_1", "message":"What's the hardest part of your week?", "verbosity":"detailed" }
# turn 2 — ladder down (reuse chatId)
POST /api/v1/chat { "audienceId":"aud_f1_1", "chatId":"chat_x", "message":"Why does that matter to you?" }
# turn 3 — probe the objection
POST /api/v1/chat { "audienceId":"aud_f1_1", "chatId":"chat_x", "message":"What would have to be true for you to switch tools?" }
# turn 4 — pressure-test with world context
POST /api/v1/chat { "audienceId":"aud_f1_1", "chatId":"chat_x", "worldAware":true, "message":"Given where rates are headed, does that change your timeline?" }
# then: feed the transcript to /docs/insights to mine themes + divergences

Common pitfalls

SymptomCause / fix
"Persona forgot the previous turn"Missing chatId. Pass the one from turn 1 on every turn.
"audience not found"Build the audience first (Creating an audience); use the returned id.
Answers feel generic/averagedAudience is weakly grounded — check honesty.groundingStrength; ground it (ground_audience) or build from richer sources.
Persona keeps abstainingYou're past its role. Use a personaId who owns that decision.
Persona seems stuck in the pastSet worldAware:true with an awarenessTopic.

On this page