Intake Financeiro
O intake financeiro é a base de toda experiência do usuário no Ozzie. Ele captura um snapshot pontual do quadro financeiro mensal do usuário — renda, despesas e objetivo principal. O Ozzie usa esses dados para gerar um plano financeiro personalizado, calcular metas de poupança e fornecer recomendações contextuais.
POST /v1/users/{user_id}/plan/generate retornará INTAKE_REQUIRED até que pelo menos um snapshot de intake financeiro tenha sido enviado. Sempre envie um intake antes de gerar um plano.
O Objeto Financial Intake
{
"object": "financial_intake",
"data": {
"id": "ozz_intake_01HX9M3FQKRNT8YDWP6BZ2LSA",
"user_id": "ozz_usr_01HX9KZMR4P5JQNBVT7YCW3DE",
"monthly_income": 5000,
"monthly_expenses": 3200,
"monthly_surplus": 1800,
"financial_goal": "savings",
"next_pay_date": "2025-05-15",
"created_at": "2025-05-05T14:31:00Z"
}
}
Campos do Objeto Financial Intake
| Campo | Tipo | Descrição |
|---|---|---|
id | string | UUID gerado pelo Ozzie para este snapshot de intake |
user_id | string | O usuário Ozzie a quem este intake pertence |
monthly_income | number | Renda mensal bruta na moeda base do usuário |
monthly_expenses | number | Total de despesas mensais |
monthly_surplus | number | Campo calculado: monthly_income - monthly_expenses |
financial_goal | string | "savings" ou "debt" — o tipo de objetivo financeiro principal do usuário |
next_pay_date | string | null | Data do próximo pagamento do usuário no formato YYYY-MM-DD |
created_at | string | Timestamp UTC ISO 8601 de quando este snapshot foi criado |
Modelo de Snapshot Imutável
Cada chamada a POST /v1/users/{user_id}/financial-intake cria um novo snapshot imutável. Snapshots anteriores são preservados no histórico. O Ozzie sempre usa o snapshot criado mais recentemente para geração de planos e recomendações.
Este design significa:
- Você nunca precisa "atualizar" um intake — apenas envie um novo.
- Snapshots históricos estão disponíveis via
GET /v1/users/{user_id}/financial-intake/historypara auditoria e análise de tendências. - Regenerar um plano após um novo envio de intake usará automaticamente os números mais recentes.
Intake de mai/1: income=4500, expenses=3100 ← usado para o plano de maio
↓
Intake de mai/31: income=5000, expenses=3200 ← torna-se o novo mais recente
↓
POST /plan/generate → usa snapshot de mai/31
Envie um intake novo quando o usuário reportar uma mudança significativa: aumento de salário, nova conta recorrente, quitação de dívida ou evento de vida (novo emprego, mudança de cidade). Reenvio mensal é um bom padrão.
POST /v1/users/{user_id}/financial-intake
Armazena um novo snapshot financeiro mensal para o usuário.
Parâmetros de Caminho
| Parâmetro | Descrição |
|---|---|
user_id | O UUID Ozzie do usuário ou external:{external_user_id} |
Corpo da Requisição
| Campo | Tipo | Obrigatório | Restrições | Descrição |
|---|---|---|---|---|
monthly_income | number | Sim | Deve ser ≥ 0 | Renda mensal bruta na moeda base do usuário |
monthly_expenses | number | Sim | Deve ser ≥ 0 | Total de despesas mensais (aluguel, alimentação, contas, assinaturas, etc.) |
financial_goal | string | Sim | "savings" | "debt" | O objetivo financeiro principal do usuário |
next_pay_date | string | Não | Data ISO YYYY-MM-DD | Data do próximo pagamento do usuário — usada para temporização do fluxo de caixa |
Valores de financial_goal
| Valor | Significado | Comportamento do plano |
|---|---|---|
"savings" | Prioridade do usuário é construir poupança ou fundo de emergência | Plano enfatiza taxa de contribuição e crescimento composto |
"debt" | Prioridade do usuário é quitar dívidas | Plano enfatiza sequenciamento de pagamento de dívidas (avalanche ou bola de neve) |
Resposta
Retorna o objeto financial_intake criado com HTTP 201 Created.
Erros
| Código | HTTP | Quando |
|---|---|---|
NOT_FOUND | 404 | O user_id não existe |
VALIDATION_ERROR | 400 | Um campo obrigatório está ausente ou tem um valor inválido |
UNAUTHORIZED | 401 | Credenciais ausentes ou inválidas |
Exemplos
- curl
- Node.js
- Python
curl -X POST \
https://api.ozzieapp.com/v1/users/ozz_usr_01HX9KZMR4P5JQNBVT7YCW3DE/financial-intake \
-H "Authorization: Bearer czJjbGllbnQ6czJzZWNyZXQ=" \
-H "Content-Type: application/json" \
-d '{
"monthly_income": 5000,
"monthly_expenses": 3200,
"financial_goal": "savings",
"next_pay_date": "2025-05-15"
}'
import fetch from 'node-fetch';
const token = Buffer.from('seu_client_id:seu_client_secret').toString('base64');
const userId = 'ozz_usr_01HX9KZMR4P5JQNBVT7YCW3DE';
const response = await fetch(
`https://api.ozzieapp.com/v1/users/${userId}/financial-intake`,
{
method: 'POST',
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
monthly_income: 5000,
monthly_expenses: 3200,
financial_goal: 'savings',
next_pay_date: '2025-05-15',
}),
}
);
const { data: intake } = await response.json();
console.log(`Superávit mensal: R$${intake.monthly_surplus}`);
import requests
import base64
token = base64.b64encode(b'seu_client_id:seu_client_secret').decode()
user_id = 'ozz_usr_01HX9KZMR4P5JQNBVT7YCW3DE'
response = requests.post(
f'https://api.ozzieapp.com/v1/users/{user_id}/financial-intake',
headers={
'Authorization': f'Bearer {token}',
'Content-Type': 'application/json',
},
json={
'monthly_income': 5000,
'monthly_expenses': 3200,
'financial_goal': 'savings',
'next_pay_date': '2025-05-15',
}
)
intake = response.json()['data']
print(f"Superávit mensal: R${intake['monthly_surplus']}")
Exemplo de Resposta (201 Created):
{
"object": "financial_intake",
"data": {
"id": "ozz_intake_01HX9M3FQKRNT8YDWP6BZ2LSA",
"user_id": "ozz_usr_01HX9KZMR4P5JQNBVT7YCW3DE",
"monthly_income": 5000,
"monthly_expenses": 3200,
"monthly_surplus": 1800,
"financial_goal": "savings",
"next_pay_date": "2025-05-15",
"created_at": "2025-05-05T14:31:00Z"
}
}
Resposta de Erro — Campo obrigatório ausente (400):
{
"error": {
"code": "VALIDATION_ERROR",
"message": "Field 'financial_goal' is required. Must be one of: 'savings', 'debt'."
}
}
Resposta de Erro — Valor de renda inválido (400):
{
"error": {
"code": "VALIDATION_ERROR",
"message": "Field 'monthly_income' must be a non-negative number. Received: -500."
}
}
GET /v1/users/{user_id}/financial-intake
Retorna o snapshot de intake financeiro criado mais recentemente para o usuário.
Parâmetros de Caminho
| Parâmetro | Descrição |
|---|---|
user_id | O UUID Ozzie do usuário ou external:{external_user_id} |
Resposta
Retorna o objeto financial_intake com HTTP 200 OK. Se nenhum intake tiver sido enviado, retorna HTTP 404 com código NOT_FOUND.
Erros
| Código | HTTP | Quando |
|---|---|---|
NOT_FOUND | 404 | O usuário existe mas não enviou nenhum intake ainda, ou o user_id é inválido |
UNAUTHORIZED | 401 | Credenciais ausentes ou inválidas |
Exemplos
- curl
- Node.js
- Python
curl https://api.ozzieapp.com/v1/users/ozz_usr_01HX9KZMR4P5JQNBVT7YCW3DE/financial-intake \
-H "Authorization: Bearer czJjbGllbnQ6czJzZWNyZXQ="
import fetch from 'node-fetch';
const token = Buffer.from('seu_client_id:seu_client_secret').toString('base64');
const userId = 'ozz_usr_01HX9KZMR4P5JQNBVT7YCW3DE';
const response = await fetch(
`https://api.ozzieapp.com/v1/users/${userId}/financial-intake`,
{ headers: { 'Authorization': `Bearer ${token}` } }
);
if (response.status === 404) {
console.log('Nenhum intake enviado ainda. Solicite os detalhes financeiros ao usuário.');
} else {
const { data: intake } = await response.json();
console.log(`Intake mais recente de ${intake.created_at}: superávit R$${intake.monthly_surplus}`);
}
import requests
import base64
token = base64.b64encode(b'seu_client_id:seu_client_secret').decode()
user_id = 'ozz_usr_01HX9KZMR4P5JQNBVT7YCW3DE'
response = requests.get(
f'https://api.ozzieapp.com/v1/users/{user_id}/financial-intake',
headers={'Authorization': f'Bearer {token}'}
)
if response.status_code == 404:
print('Nenhum intake enviado ainda.')
else:
intake = response.json()['data']
print(f"Intake mais recente: superávit R${intake['monthly_surplus']}")
Exemplo de Resposta (200 OK):
{
"object": "financial_intake",
"data": {
"id": "ozz_intake_01HX9M3FQKRNT8YDWP6BZ2LSA",
"user_id": "ozz_usr_01HX9KZMR4P5JQNBVT7YCW3DE",
"monthly_income": 5000,
"monthly_expenses": 3200,
"monthly_surplus": 1800,
"financial_goal": "savings",
"next_pay_date": "2025-05-15",
"created_at": "2025-05-05T14:31:00Z"
}
}
Resposta de Erro — Nenhum intake ainda (404):
{
"error": {
"code": "NOT_FOUND",
"message": "No financial intake found for user 'ozz_usr_01HX9KZMR4P5JQNBVT7YCW3DE'. Submit a POST /v1/users/{user_id}/financial-intake first."
}
}
Antes de chamar POST /v1/users/{user_id}/plan/generate, você pode opcionalmente chamar este endpoint para confirmar que um intake existe e visualizar o monthly_surplus. Isso evita um erro de round-trip se o intake nunca foi enviado.