Plan
El Plan es la salida central del motor de análisis financiero de Ozzie. Toma los datos de intake financiero del usuario (ingresos, gastos, deudas, metas) y produce una hoja de ruta estructurada con asignaciones presupuestarias, hitos proyectados y elementos de acción priorizados.
El plan es el prerrequisito para generar money moves y moldea todas las respuestas del coach de IA en el chat.
Referencia del objeto
| Campo | Tipo | Descripción |
|---|---|---|
id | string | Identificador único del plan |
user_id | string | El usuario Ozzie al que pertenece este plan |
timeline_months | integer | Meses proyectados para alcanzar el objetivo principal del usuario |
allocations | object | Desglose presupuestario JSONB por categoría (porcentajes) |
allocations.needs | number | % de la renta neta asignado a necesidades esenciales |
allocations.wants | number | % de la renta neta asignado a gastos discrecionales |
allocations.savings | number | % de la renta neta asignado a ahorros |
allocations.debt | number | % de la renta neta asignado al pago de deudas |
key_metrics | object | Principales indicadores financieros proyectados |
key_metrics.monthly_savings_amount | number | Monto en dinero ahorrado por mes bajo este plan |
key_metrics.projected_goal_date | string (ISO 8601) | Fecha estimada en que el usuario alcanza su meta |
key_metrics.current_savings_rate | number | Tasa de ahorro actual como porcentaje de los ingresos |
key_metrics.target_savings_rate | number | Tasa de ahorro recomendada para alcanzar la meta a tiempo |
key_metrics.debt_payoff_date | string (ISO 8601) | null | Fecha proyectada de pago de deuda (si la meta es deuda) |
action_items | string[] | Lista ordenada de próximas acciones recomendadas |
personality_type | string | null | Perfil de personalidad aplicado (si está personalizado) |
created_at | string (ISO 8601) | Cuando se generó el plan |
updated_at | string (ISO 8601) | Cuando se actualizó el plan por última vez |
POST /v1/users/{user_id}/plan
Calcula un nuevo plan financiero para el usuario, o recupera y actualiza uno existente. Si ya existe un plan, llamar a este endpoint lo recalcula basándose en el intake y los datos de transacciones más recientes.
Este endpoint requiere que exista un intake financiero para el usuario. Si no se ha enviado ningún intake, recibirás un error INTAKE_REQUIRED.
La generación de plan es idempotente por usuario. Puedes llamar a este endpoint de forma segura varias veces — Ozzie devolverá el plan más actual, recalculando si se han enviado nuevos datos de intake o transacciones desde la última generación.
Parámetros de ruta
| Parámetro | Tipo | Requerido | Descripción |
|---|---|---|---|
user_id | string | Sí | El ID de usuario de Ozzie |
Cuerpo de la solicitud
No se requiere cuerpo de solicitud. Ozzie lee todos los datos del intake y transacciones existentes del usuario.
Solicitud
- curl
- Node.js
- Python
curl -X POST https://api.ozzieapp.com/v1/users/usr_4f8a1b2c3d/plan \
-H "Authorization: Bearer ozp_Y2xpZW50X2ExYjJjM2Q0OnNrX2xpdmVfeEs5bVAycVI3dEwu" \
-H "Content-Type: application/json"
const token = Buffer.from(
`${process.env.OZZIE_CLIENT_ID}:${process.env.OZZIE_CLIENT_SECRET}`
).toString('base64');
const response = await fetch(
'https://api.ozzieapp.com/v1/users/usr_4f8a1b2c3d/plan',
{
method: 'POST',
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json',
},
}
);
const { data } = await response.json();
console.log(data);
import base64, os, httpx
token = base64.b64encode(
f"{os.environ['OZZIE_CLIENT_ID']}:{os.environ['OZZIE_CLIENT_SECRET']}".encode()
).decode()
response = httpx.post(
"https://api.ozzieapp.com/v1/users/usr_4f8a1b2c3d/plan",
headers={"Authorization": f"Bearer {token}"},
)
print(response.json()["data"])
Respuesta
{
"object": "plan",
"data": {
"id": "pln_8b3d1e9f2a",
"user_id": "usr_4f8a1b2c3d",
"timeline_months": 57,
"allocations": {
"needs": 60,
"wants": 20,
"savings": 15,
"debt": 5
},
"key_metrics": {
"monthly_savings_amount": 562.50,
"projected_goal_date": "2029-11-01T00:00:00Z",
"current_savings_rate": 8,
"target_savings_rate": 15,
"debt_payoff_date": null
},
"action_items": [
"Aumenta tu contribución de ahorro del 8% al 15% de la renta neta — comienza con $562.50/mes",
"Reduce los gastos discrecionales (actualmente 28%) al 20% reduciendo salidas a comer y suscripciones de streaming",
"Configura una transferencia automática el día de pago para que el ahorro ocurra antes de que puedas gastar",
"Revisa tus gastos en alimentación — actualmente estás $120/mes por encima de una línea base razonable para tus ingresos",
"Una vez que tu fondo de emergencia llegue a $3,000, considera abrir una cuenta de ahorro de alto rendimiento"
],
"personality_type": null,
"created_at": "2025-05-05T14:50:00Z",
"updated_at": "2025-05-05T14:50:00Z"
}
}
Errores
| Código | Estado HTTP | Cuándo ocurre |
|---|---|---|
UNAUTHORIZED | 401 | Credenciales ausentes o inválidas |
NOT_FOUND | 404 | El usuario no existe |
INTAKE_REQUIRED | 422 | No existe intake financiero para el usuario |
INTERNAL_ERROR | 500 | Falló el cálculo del plan — seguro para reintentar |
POST /v1/users/{user_id}/plan/personalize
Ajusta el plan existente del usuario basándose en un perfil de personalidad. Los planes con consciencia de personalidad ajustan el lenguaje, ritmo y énfasis de las recomendaciones para coincidir con la forma en que el usuario piensa sobre el dinero.
La personalización requiere que ya exista un plan. Llama a POST /plan primero, luego llama a /plan/personalize para agregar el perfil de personalidad.
Parámetros de ruta
| Parámetro | Tipo | Requerido | Descripción |
|---|---|---|---|
user_id | string | Sí | El ID de usuario de Ozzie |
Cuerpo de la solicitud
| Campo | Tipo | Requerido | Descripción |
|---|---|---|---|
personality_type | string | Sí | El identificador del perfil de personalidad (ej.: "optimizer", "avoider", "planner", "spender") |
Tipos de personalidad
| Tipo | Descripción |
|---|---|
"optimizer" | Orientado a datos, motivado por eficiencia y retornos máximos |
"planner" | Metódico, prefiere hojas de ruta detalladas y previsibilidad |
"avoider" | Ansioso con las finanzas; necesita tranquilidad y pequeñas victorias |
"spender" | Orientado al estilo de vida; necesita estrategias que no se sientan como privación |
Solicitud
- curl
- Node.js
- Python
curl -X POST https://api.ozzieapp.com/v1/users/usr_4f8a1b2c3d/plan/personalize \
-H "Authorization: Bearer ozp_Y2xpZW50X2ExYjJjM2Q0OnNrX2xpdmVfeEs5bVAycVI3dEwu" \
-H "Content-Type: application/json" \
-d '{
"personality_type": "avoider"
}'
const token = Buffer.from(
`${process.env.OZZIE_CLIENT_ID}:${process.env.OZZIE_CLIENT_SECRET}`
).toString('base64');
const response = await fetch(
'https://api.ozzieapp.com/v1/users/usr_4f8a1b2c3d/plan/personalize',
{
method: 'POST',
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({ personality_type: 'avoider' }),
}
);
const { data } = await response.json();
console.log(data.action_items);
import base64, os, httpx
token = base64.b64encode(
f"{os.environ['OZZIE_CLIENT_ID']}:{os.environ['OZZIE_CLIENT_SECRET']}".encode()
).decode()
response = httpx.post(
"https://api.ozzieapp.com/v1/users/usr_4f8a1b2c3d/plan/personalize",
headers={
"Authorization": f"Bearer {token}",
"Content-Type": "application/json",
},
json={"personality_type": "avoider"},
)
print(response.json()["data"]["action_items"])
Respuesta
Devuelve el plan actualizado con el perfil de personalidad aplicado. Observa cómo los action_items cambian a pasos más suaves y pequeños para un perfil "avoider".
{
"object": "plan",
"data": {
"id": "pln_8b3d1e9f2a",
"user_id": "usr_4f8a1b2c3d",
"timeline_months": 57,
"allocations": {
"needs": 60,
"wants": 20,
"savings": 15,
"debt": 5
},
"key_metrics": {
"monthly_savings_amount": 562.50,
"projected_goal_date": "2029-11-01T00:00:00Z",
"current_savings_rate": 8,
"target_savings_rate": 15,
"debt_payoff_date": null
},
"action_items": [
"Ya estás ahorrando — eso es algo que celebrar. Solo vamos a crecer un poco más.",
"Intenta mover solo $50 más por pago a ahorros este mes. Un pequeño paso.",
"No tienes que cortar todo. Elige un gasto esta semana que pareció olvidable y paúsalo.",
"Configura una transferencia automática de $50 el día de pago — no lo notarás, pero tus ahorros sí.",
"Tu meta es alcanzable. A este ritmo tendrás $3,000 ahorrados en 6 meses."
],
"personality_type": "avoider",
"created_at": "2025-05-05T14:50:00Z",
"updated_at": "2025-05-05T15:10:00Z"
}
}
Errores
| Código | Estado HTTP | Cuándo ocurre |
|---|---|---|
UNAUTHORIZED | 401 | Credenciales ausentes o inválidas |
NOT_FOUND | 404 | El usuario no existe |
PLAN_REQUIRED | 422 | No existe ningún plan aún — llama a POST /plan primero |
PERSONALITY_REQUIRED | 422 | Campo personality_type ausente en el cuerpo de la solicitud |
VALIDATION_ERROR | 422 | Valor de personality_type desconocido |
INVALID_JSON | 400 | El cuerpo de la solicitud no es JSON válido |
Puedes determinar el tipo de personalidad de un usuario realizando un quiz corto en tu flujo de onboarding y luego pasando el resultado aquí. El string personality_type se almacena en el plan y se refleja en todas las respuestas subsiguientes del coach de IA para ese usuario.