Visión General de la API
URL Base y Versionado
La API de Ozzie está versionada a través de la ruta de URL. La versión estable actual es v1.
https://api.ozzieapp.com/v1
Política de versionado:
- El prefijo de versión (
/v1,/v2, etc.) se incrementa solo para cambios incompatibles. - Los cambios aditivos (nuevos campos opcionales, nuevos endpoints) se hacen sin incrementar la versión.
- Las versiones depreciadas se anuncian con al menos 90 días de antelación y devolverán un header de respuesta
Deprecationdurante el período de descontinuación. - Siempre fija una versión explícita en tu URL base. Nunca uses
/latest.
Cuando una versión entra en su período de depreciación, todas las respuestas incluirán:
Deprecation: true
Sunset: Sat, 01 Nov 2025 00:00:00 GMT
Todos los Endpoints Disponibles
| Método | Ruta | Descripción |
|---|---|---|
POST | /v1/users | Crear un nuevo usuario |
GET | /v1/users/{user_id} | Recuperar un usuario por ID de Ozzie o external_user_id |
PATCH | /v1/users/{user_id} | Actualizar campos del usuario (name, email, phone, language) |
POST | /v1/users/{user_id}/financial-intake | Enviar un snapshot financiero mensual |
GET | /v1/users/{user_id}/financial-intake | Obtener el intake financiero más reciente |
GET | /v1/users/{user_id}/financial-intake/history | Obtener todos los snapshots históricos de intake |
POST | /v1/users/{user_id}/plan/generate | Generar un plan financiero personalizado |
GET | /v1/users/{user_id}/plan | Obtener el plan activo actual |
POST | /v1/users/{user_id}/goals | Crear una meta financiera |
GET | /v1/users/{user_id}/goals | Listar todas las metas |
GET | /v1/users/{user_id}/goals/{goal_id} | Obtener una meta específica |
PATCH | /v1/users/{user_id}/goals/{goal_id} | Actualizar una meta |
DELETE | /v1/users/{user_id}/goals/{goal_id} | Eliminar una meta |
POST | /v1/users/{user_id}/transactions | Enviar transacciones (texto, imagen, PDF, hoja de cálculo) |
GET | /v1/users/{user_id}/transactions | Listar transacciones con filtros |
GET | /v1/users/{user_id}/transactions/{transaction_id} | Obtener una transacción específica |
DELETE | /v1/users/{user_id}/transactions/{transaction_id} | Eliminar una transacción |
GET | /v1/users/{user_id}/money-moves | Obtener recomendaciones de gasto personalizadas |
POST | /v1/users/{user_id}/chat | Enviar mensaje al asistente financiero de Ozzie |
GET | /v1/users/{user_id}/chat/history | Obtener historial de mensajes del chat |
Autenticación
Todas las solicitudes a la API deben incluir un header Authorization usando autenticación HTTP Bearer. El token es la codificación Base64 de tu client_id y client_secret unidos por dos puntos.
Formato del token:
base64(client_id + ":" + client_secret)
Formato del header:
Authorization: Bearer <base64_encoded_credentials>
Generando el token:
- Node.js
- Python
- curl
const token = Buffer.from(`${clientId}:${clientSecret}`).toString('base64');
// Usar como: `Bearer ${token}`
import base64
token = base64.b64encode(f"{client_id}:{client_secret}".encode()).decode()
# Usar como: f"Bearer {token}"
TOKEN=$(echo -n "tu_client_id:tu_client_secret" | base64)
curl -H "Authorization: Bearer $TOKEN" https://api.ozzieapp.com/v1/users
Nunca expongas tu client_secret en código del lado del cliente, aplicaciones móviles o repositorios públicos. Todas las llamadas a la API deben originarse desde tu servidor backend.
Convenciones de Solicitud y Respuesta
Headers de Solicitud
| Header | Requerido | Valor |
|---|---|---|
Authorization | Sí | Bearer <token> |
Content-Type | Sí (para POST/PATCH) | application/json |
Accept | No (recomendado) | application/json |
Idempotency-Key | No | String UUID v4 para solicitudes POST idempotentes |
Envelope de Respuesta
Todas las respuestas exitosas siguen esta estructura de envelope:
{
"object": "object_type_name",
"data": { ... }
}
Para endpoints de lista:
{
"object": "list",
"data": {
"items": [ ... ],
"has_more": true,
"next_cursor": "2025-04-30T12:00:00Z",
"total_count": 142
}
}
Códigos de Estado HTTP
| Estado | Significado |
|---|---|
200 OK | Solicitud exitosa |
201 Created | Recurso creado |
204 No Content | Recurso eliminado |
400 Bad Request | JSON malformado o error de validación |
401 Unauthorized | Credenciales ausentes o inválidas |
403 Forbidden | Credenciales válidas pero permisos insuficientes |
404 Not Found | El recurso no existe |
409 Conflict | El recurso ya existe (ej.: usuario duplicado) |
422 Unprocessable Entity | Error de lógica de negocio (ej.: intake ausente) |
429 Too Many Requests | Límite de tasa excedido |
500 Internal Server Error | Error en el servidor de Ozzie |
Paginación
Los endpoints de lista usan paginación basada en cursor con cursores datetime ISO. Este enfoque es estable incluso cuando se insertan nuevos registros entre páginas.
Parámetros de Query
| Parámetro | Tipo | Predeterminado | Descripción |
|---|---|---|---|
limit | integer | 50 | Número de registros por página (máx 200) |
cursor | string | — | String datetime ISO 8601 — devuelve registros más antiguos que este timestamp |
from | string | — | Filtro: incluir registros a partir de esta fecha (YYYY-MM-DD) |
to | string | — | Filtro: incluir registros hasta esta fecha (YYYY-MM-DD) |
Campos de Respuesta Paginada
| Campo | Tipo | Descripción |
|---|---|---|
has_more | boolean | true si existen páginas adicionales |
next_cursor | string | null | Pasa esto como cursor en la próxima solicitud para obtener la siguiente página |
total_count | integer | Número total de registros coincidentes (no solo esta página) |
Ejemplo de Paginación
async function fetchAllTransactions(userId) {
const allTransactions = [];
let cursor = null;
do {
const params = new URLSearchParams({ limit: '200' });
if (cursor) params.set('cursor', cursor);
const res = await fetch(
`https://api.ozzieapp.com/v1/users/${userId}/transactions?${params}`,
{ headers }
);
const json = await res.json();
allTransactions.push(...json.data.transactions);
cursor = json.data.has_more ? json.data.next_cursor : null;
} while (cursor);
return allTransactions;
}
Los cursores se basan en timestamps created_at. Los registros creados después de iniciar la paginación no aparecerán en la secuencia de páginas actual — inicia una nueva solicitud para capturar nuevos registros.
Códigos de Error
Todos los errores siguen este formato:
{
"error": {
"code": "ERROR_CODE",
"message": "Explicación legible de qué salió mal."
}
}
| Código | Estado HTTP | Descripción |
|---|---|---|
INVALID_JSON | 400 | El cuerpo de la solicitud no es JSON válido. Verifica comas extra, comillas faltantes o problemas de codificación. |
VALIDATION_ERROR | 400 | Uno o más campos fallaron en la validación. El message identificará el campo y la restricción específicos. |
UNAUTHORIZED | 401 | El header Authorization está ausente, malformado o las credenciales son inválidas. |
FORBIDDEN | 403 | Las credenciales son válidas pero no tienen permiso para acceder a este recurso (ej.: acceder al usuario de otro cliente). |
NOT_FOUND | 404 | El recurso solicitado (usuario, transacción, meta, etc.) no existe. |
CONFLICT | 409 | Ya existe un recurso con una clave única conflictiva (ej.: external_user_id ya en uso). |
INTAKE_REQUIRED | 422 | Debe enviarse un snapshot de intake financiero antes de esta operación (ej.: generación de plan). |
PLAN_REQUIRED | 422 | Debe generarse un plan financiero antes de esta operación (ej.: money moves). |
PERSONALITY_REQUIRED | 422 | El perfil de personalidad de Ozzie para este usuario aún no ha sido inicializado. Se resuelve automáticamente después de la generación del plan. |
RATE_LIMIT_EXCEEDED | 429 | Has excedido el límite de tasa de solicitudes. Consulta el header Retry-After para saber cuándo reintentar. |
VERSION_MISMATCH | 400 | La versión de la API que estás usando ya no está soportada. Actualiza tu URL base a la versión actual. |
INTERNAL_ERROR | 500 | Ocurrió un error inesperado en los servidores de Ozzie. Estos se registran e investigan automáticamente. Reintenta con backoff exponencial. |
Límite de Tasa
Los límites de tasa se aplican por client_id. Límites actuales:
| Tier | Solicitudes por minuto | Burst |
|---|---|---|
| Standard | 120 | 20 |
| Growth | 600 | 100 |
| Enterprise | Personalizado | Personalizado |
Cuando se excede el límite, la API devuelve HTTP 429 con:
HTTP/1.1 429 Too Many Requests
Retry-After: 15
X-RateLimit-Limit: 120
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1746460800
| Header de Respuesta | Descripción |
|---|---|
Retry-After | Segundos a esperar antes de reintentar |
X-RateLimit-Limit | Tu cuota total de solicitudes por minuto |
X-RateLimit-Remaining | Solicitudes restantes en la ventana actual |
X-RateLimit-Reset | Timestamp Unix de cuando se reinicia la ventana |
Siempre implementa backoff exponencial al manejar respuestas 429. Un bucle de reintento simple sin demora empeorará la presión sobre el límite de tasa. Comienza con un retraso de 1 segundo, duplicando hasta 32 segundos.
Idempotencia
Las solicitudes POST que crean recursos soportan idempotencia a través del header Idempotency-Key. Si proporcionas la misma clave en una solicitud repetida (ej.: después de un timeout de red), Ozzie devolverá la respuesta original en lugar de crear un duplicado.
POST https://api.ozzieapp.com/v1/users
Authorization: Bearer czJjbGllbnQ6czJzZWNyZXQ=
Content-Type: application/json
Idempotency-Key: 550e8400-e29b-41d4-a716-446655440000
{
"external_user_id": "usr_8821",
"name": "Maria Santos"
}
Reglas:
- La clave debe ser un UUID v4.
- Las claves están limitadas a tu
client_idy expiran después de 24 horas. - Si la solicitud original todavía está en curso, el reintento recibirá HTTP
409con códigoCONFLICThasta que el original se complete. - Soportado en:
POST /v1/users,POST /v1/users/{user_id}/financial-intake,POST /v1/users/{user_id}/transactions.
POST /v1/users/{user_id}/plan/generate y POST /v1/users/{user_id}/chat no son idempotentes por diseño — cada llamada intencionalmente genera un nuevo recurso.