Conversation Rules
The 11 rules that make or break avatar behavior in verification flows.
These rules are load bearing. Violating them causes the avatar to hallucinate, loop, double speak, or fail silently. Every rule is the result of real debugging time across real integrations. Treat them as gospel until you understand them deeply enough to bend them.
1. Always author prompts in English; choose Say: vs descriptive by verbatim-control needs
Author every prompt and outcome condition in English — regardless of which languages your deployment supports. The FaceSign runtime translates avatar speech into the resolved session language at runtime; mixing authoring languages across nodes (some in English, some in the target language) causes the avatar to flip languages mid-flow.
Within that constraint, the prompt style is a separate choice driven by whether you need verbatim control:
Descriptive prompts — the default. The LLM paraphrases at runtime and translates cleanly into any session language. Use for multilingual flows and whenever exact wording doesn't matter.
prompt: "Warmly greet the user and tell them you need to verify their identity. Ask if they're ready."Say: <exact text> — a verbatim-wording directive. The avatar stays close to the source sentence; the runtime still translates the text into the resolved session language, but the result tends to be stiffer than a fresh paraphrase. Use when wording is non-negotiable (legal disclaimers, brand copy).
prompt: "Say: Hi! I just need to quickly verify your identity. Ready?"For multilingual flows, prefer descriptive prompts — they produce more natural target-language output.
2. Never put conditional logic in a single prompt
This is the single most common cause of broken demos. Do not write "If recognized, do X. If not, do Y." The avatar will ignore the condition, hallucinate, or pick the wrong branch.
Instead, use the flow's structural branching:
{ id: 'post_id', prompt: "If recognized, greet by name. If not, ask for their name." }recognition.outcomes = {
recognized: 'greet_known', // "Hey [name], great to see you!"
newUser: 'ask_name', // "Welcome! What's your name?"
noFace: 'ask_name'
}3. Outcome conditions: "" for unconditional, natural language for branching
Every conversation-node outcome takes one of two forms:
- Single outcome (unconditional) —
condition: "". No evaluation happens; the flow moves to the target regardless of what the user says. Use for informational turns that don't branch — greetings, explanations, closings, and every node withdoesNotRequireReply: true. - Multi-outcome (conditional) — each branch gets a natural-language
conditiondescribing its trigger. Always include an explicit stall or refusal branch so the conversation can't get stuck.
{
id: 'doc_prep',
type: 'conversation',
prompt: "Say: Please have your ID ready. The scanner will appear in a moment.",
outcomes: [{ id: 'next', targetNodeId: 'doc_scan', condition: '' }]
}outcomes: [
{ id: 'ready', condition: 'User said yes or confirmed they are ready', targetNodeId: 'next' },
{ id: 'refused', condition: 'User declined or refused to continue', targetNodeId: 'end' },
{ id: 'stall', condition: 'Dialog reached 3 exchanges with no clear answer', targetNodeId: 'end' }
]4. doesNotRequireReply: true only on the last node before end
The flag marks a final monologue — the avatar delivers a closing message and the session ends without waiting for a reply. The node must have a single outcome that targets an end node with condition: "".
It has no meaning on mid-flow nodes. If you want a mid-flow node that speaks and continues without waiting for a reply, use a single unconditional outcome (condition: "") and leave doesNotRequireReply unset — see Rule 3.
5. One question per node
Do not ask multiple questions in a single conversation node. Outcome conditions will match prematurely after the first answer. Split into separate nodes.
prompt: "Say: What's your date of birth? And what's your zip code?"{ id: 'ask_dob', prompt: "Say: Can you confirm your date of birth?" }
{ id: 'ask_zip', prompt: "Say: And what's the zip code on file?" }6. The greeting must be long enough for video accumulation
Video-analysis nodes (liveness_detection, face_scan, face_compare, recognition) need at least 5 seconds of camera feed before they can run. A conversation node with actual avatar speech must precede the first video-analysis node in the flow to establish that buffer. Once buffered, subsequent video-analysis nodes can run back-to-back without another conversation in between.
Short greetings cause the first video-analysis node to fail — for recognition this typically surfaces as newUser even for returning users.
"Say: Ready?""Say: Hi! I just need to quickly verify your identity to keep your account secure.
It only takes a few seconds. Ready to get started?"7. Do not reference invisible steps
liveness_detection, recognition, face_scan, and face_compare run silently in the background. Do not have the avatar say "That's done!" or "Looking good!" after one of these — the user saw nothing happen, so the acknowledgment breaks the illusion. Skip straight to the next meaningful interaction.
8. Use the full name for recognized users
When greeting recognized users, instruct the avatar to use the full name: "Hey Ada Lovelace" not "Hey Ada". Add "Use their full name, not just first name." to the prompt.
9. The avatar does not need to introduce herself
Do not waste time with "I'm your verification assistant." Go straight into the purpose.
10. Combine the greeting with the first question for recognized users
This removes an awkward extra conversational turn.
prompt: "Say: Hey [full name], great to see you! Can you quickly confirm your date of birth?"11. No self-loops in conversation node outcomes
The flow validator rejects any outcome whose targetNodeId equals its own node's id. If you need retry-on-unclear-input behavior, fold it into the prompt text rather than using a self-looping outcome:
outcomes: [
{ id: 'retry', condition: 'Response is blank', targetNodeId: 'ask_name' },
{ id: 'done', condition: 'User gave name', targetNodeId: 'next' }
]prompt: "Ask for their full name. If they respond unclearly, ask once more gently.",
outcomes: [
{ id: 'done', condition: 'User responded, or the conversation has exceeded two exchanges', targetNodeId: 'next' }
]