Chat
POST /v1/users/:user_id/chat/completion is a stateless AI chat inference endpoint. The caller provides the user's message, conversation history, and personality type. The intelligence layer returns a reply without storing anything.
Your application is responsible for storing conversation history and passing it in history on each call.
How it works​
- Your app loads the user's conversation history from your database
- Your app calls
POST /v1/users/{user_id}/chat/completionwith message, history, and personality type - The intelligence layer returns an AI reply
- Your app stores both the user message and the reply
- On the next call, include the updated history
POST /v1/users/:user_id/chat/completion​
Send a message and receive a stateless AI reply.
Request body​
| Field | Type | Required | Description |
|---|---|---|---|
message | string | Yes | User's message. Max 2000 characters. |
history | array | No | Prior conversation turns [{ role, content }] |
personality_type | string | No | HOTSHOT | STOCKPILER | GIVER | INDEPENDENT — shapes tone |
language | string | No | Override response language for this request |
context | string | No | Conversation context hint, e.g. "financial_planning" |
user_context | object | No | Rich user snapshot — see user_context fields below |
user_context fields​
All fields inside user_context are optional. Pass as many as you have available — richer context produces more personalized responses.
| Field | Type | Description |
|---|---|---|
name | string | User's display name |
language | string | User's preferred language ("en" | "pt" | "es") |
personality_type | string | HOTSHOT | STOCKPILER | GIVER | INDEPENDENT |
plan_tier | string | Plan tier from the last plan generation, e.g. "20/80" |
plan_speed | string | Plan speed from the last plan generation: "conservative" | "moderate" | "aggressive" |
monthly_income | number | User's gross monthly income |
goal_progress | object | Goal snapshot — see below |
goal_progress.target_amount_cents | number | Goal target in cents |
goal_progress.total_contributed_cents | number | Total contributed so far in cents |
goal_progress.percentage | number | Contribution percentage (0–100) |
next_move | object | Upcoming passo details |
next_move.date | string | ISO date of the next money move, e.g. "2026-05-20" |
next_move.amount_cents | number | Next move amount in cents |
completed_moves_count | number | Total passos completed by the user |
total_saved_cents | number | Total amount saved across all passos in cents |
last_confirmation_note | string | Free-text note the user left on their last passo confirmation |
entry_point | string | Where the user arrived from — shapes Ozzie's opening message. See entry_point values |
spending_alert_envelope | string | Envelope name for a spending alert, e.g. "lifestyle" (used with entry_point: "spending_alert") |
spending_alert_ratio | number | Ratio of actual to allocated spending, e.g. 1.25 means 25% over budget |
rag_context | string | Free-text retrieval context injected into the AI prompt, e.g. behavioral patterns from your database |
entry_point values​
entry_point tells Ozzie where the user arrived from. Ozzie uses it to generate a context-aware opening message returned as entry_point_opener in the response.
| entry_point | When to use | Ozzie opens with |
|---|---|---|
weekly_expense | User arrives from a WhatsApp weekly expense prompt | Asks for expenses with input options |
spending_alert | User arrives from a push notification about overspending | References the specific envelope (spending_alert_envelope) and asks about recalibration |
monthly_review | User arrives from a WhatsApp monthly review link | Summarizes last month and asks about this month |
move_reminder | User arrives from a WhatsApp money move reminder | Returns entry_point_opener: null — the channel renders the passo confirmation card instead |
unprompted | User opens the app directly (default when no entry_point is provided) | Personalized greeting referencing last passo and total saved |
When no entry_point is provided, entry_point_opener is null in the response.
- curl
- Node.js
- Python
curl -X POST https://channel.ozzieapp.com/v1/users/ozz_usr_01HX9KZMR4P5JQNBVT7YCW3DE/chat/completion \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"message": "How am I doing with my savings goal?",
"personality_type": "STOCKPILER",
"language": "en"
}'
const res = await fetch(
'https://channel.ozzieapp.com/v1/users/ozz_usr_01HX9KZMR4P5JQNBVT7YCW3DE/chat/completion',
{
method: 'POST',
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
message: 'How am I doing with my savings goal?',
personality_type: 'STOCKPILER',
language: 'en',
history: previousTurns, // your stored conversation history
}),
}
);
const { data } = await res.json();
console.log(data.content);
import requests, base64
token = base64.b64encode(b'your_client_id:your_client_secret').decode()
response = requests.post(
'https://channel.ozzieapp.com/v1/users/ozz_usr_01HX9KZMR4P5JQNBVT7YCW3DE/chat/completion',
headers={
'Authorization': f'Bearer {token}',
'Content-Type': 'application/json',
},
json={
'message': 'How am I doing with my savings goal?',
'personality_type': 'STOCKPILER',
'language': 'en',
}
)
print(response.json()['data']['content'])
Response​
| Field | Type | Description |
|---|---|---|
object | string | Always "chat_completion" |
data.content | string | AI reply text |
data.entry_point_opener | string | null | Pre-built opening message for the session. Not null when entry_point was provided and is not move_reminder. Null for move_reminder (channel renders the passo card) and when no entry_point was sent. |
data.context | string | Active conversation context |
{
"object": "chat_completion",
"data": {
"role": "assistant",
"content": "You're on track. Your STOCKPILER profile means consistent, automated saving is your edge — keep that monthly transfer running and you'll hit your monthly target comfortably.",
"entry_point_opener": null,
"context": "general",
"created_at": "2026-05-15T10:10:00Z"
}
}
entry_point examples​
unprompted — app opened directly​
Request:
{
"message": "oi",
"user_context": {
"name": "Ramon",
"language": "pt",
"personality_type": "STOCKPILER",
"completed_moves_count": 8,
"total_saved_cents": 125000,
"next_move": {
"date": "2026-05-20",
"amount_cents": 20000
},
"entry_point": "unprompted"
}
}
Response:
{
"object": "chat_completion",
"data": {
"content": "Oi Ramon! Você completou 8 passos e já guardou $1,250. Seu próximo passo é dia 2026-05-20. Sobre o que você quer falar hoje?",
"entry_point_opener": "Oi Ramon! Você completou 8 passos e já guardou $1,250. Seu próximo passo é dia 2026-05-20. Sobre o que você quer falar hoje?",
"context": "general"
}
}
weekly_expense — arrived from WhatsApp prompt​
Request:
{
"message": "",
"user_context": {
"name": "Ramon",
"language": "pt",
"entry_point": "weekly_expense"
}
}
Response:
{
"object": "chat_completion",
"data": {
"content": "Ramon, vamos registrar seus gastos da semana! Me conta o que você gastou — pode ser no formato livre ou escolha uma categoria para começar.",
"entry_point_opener": "Ramon, vamos registrar seus gastos da semana! Me conta o que você gastou — pode ser no formato livre ou escolha uma categoria para começar.",
"context": "weekly_expense"
}
}
spending_alert — arrived from overspending push notification​
Request:
{
"message": "",
"user_context": {
"name": "Ramon",
"language": "pt",
"entry_point": "spending_alert",
"spending_alert_envelope": "lifestyle",
"spending_alert_ratio": 1.25
}
}
Response:
{
"object": "chat_completion",
"data": {
"content": "Ramon, seu envelope lifestyle está 25% acima do planejado este mês. Quer recalibrar ou conversar sobre o que aconteceu?",
"entry_point_opener": "Ramon, seu envelope lifestyle está 25% acima do planejado este mês. Quer recalibrar ou conversar sobre o que aconteceu?",
"context": "spending_alert"
}
}
monthly_review — arrived from WhatsApp monthly review link​
Request:
{
"message": "",
"user_context": {
"name": "Ramon",
"language": "pt",
"entry_point": "monthly_review",
"goal_progress": {
"target_amount_cents": 500000,
"total_contributed_cents": 125000,
"percentage": 25
}
}
}
Response:
{
"object": "chat_completion",
"data": {
"content": "Ramon, o mês passou! Você contribuiu $1,250 e está 25% do caminho para sua meta. O que você quer priorizar no próximo mês?",
"entry_point_opener": "Ramon, o mês passou! Você contribuiu $1,250 e está 25% do caminho para sua meta. O que você quer priorizar no próximo mês?",
"context": "monthly_review"
}
}
move_reminder — arrived from WhatsApp passo reminder​
Request:
{
"message": "",
"user_context": {
"name": "Ramon",
"language": "pt",
"entry_point": "move_reminder",
"next_move": {
"date": "2026-05-20",
"amount_cents": 20000
}
}
}
Response:
{
"object": "chat_completion",
"data": {
"content": null,
"entry_point_opener": null,
"context": "move_reminder"
}
}
entry_point_opener is null for move_reminder because the channel (WhatsApp or the app) is responsible for rendering the passo confirmation card. Ozzie does not generate an opening text for this flow.
Errors​
| Code | HTTP | When |
|---|---|---|
UNAUTHORIZED | 401 | Missing or invalid credentials |
NOT_FOUND | 404 | User does not exist |
VALIDATION_ERROR | 400 | message missing or exceeds 2000 characters |
RATE_LIMIT_EXCEEDED | 429 | Too many chat requests |
Personality-aware coaching​
The AI coach adapts its responses based on the personality_type you pass. Examples:
STOCKPILER asking about savings progress:
"You've built $750 of your $1,500 monthly target — exactly on pace. Your STOCKPILER profile means this consistency compounds. Keep the automation running and you'll hit your emergency fund milestone in 8 months."
HOTSHOT asking about the same:
"You're at 50% of your monthly savings target. That's solid, but if you redirect your $300 lifestyle envelope surplus this month, you'd hit 70% — which puts your investment timeline 3 weeks ahead of schedule."
GIVER asking about spending on family:
"Your people envelope has $285 left this month — well within your $375 allocation. Spending on family is part of your GIVER profile, not a deviation from your plan."
GET /v1/users/:user_id/chat/messages​
Returns the full conversation history for a user. This is an alias for the channel-stored chat log — the same messages the channel injects into history on each POST /chat/completion call.
curl https://channel.ozzieapp.com/v1/users/external:usr_8821/chat/messages \
-H "Authorization: Bearer YOUR_TOKEN"
Response (200 OK):
{
"object": "chat_history",
"data": {
"messages": [
{
"role": "user",
"content": "Como estou indo?",
"created_at": "2026-05-16T10:00:00Z"
},
{
"role": "assistant",
"content": "Você está no caminho certo.",
"created_at": "2026-05-16T10:00:01Z"
}
]
}
}
Errors:
| Code | HTTP | When |
|---|---|---|
UNAUTHORIZED | 401 | Missing or invalid credentials |
NOT_FOUND | 404 | User does not exist |