Integración con ID Externo
La API de Ozzie soporta dos patrones de integración distintos. Puedes elegir el que mejor se adapte a tu arquitectura — o combinar ambos dentro de la misma cuenta de cliente.
Dos Patrones de Integración
Flujo A — Creación explícita de usuario
Primero creas un registro de usuario llamando a POST /v1/users, recibes un UUID de Ozzie (ozz_usr_...) y lo usas en todas las solicitudes posteriores.
POST /v1/users → devuelve id: "ozz_usr_abc123"
POST /v1/users/ozz_usr_abc123/financial-intake → usa UUID de Ozzie
POST /v1/users/ozz_usr_abc123/plan/generate → usa UUID de Ozzie
POST /v1/users/ozz_usr_abc123/chat/messages → usa UUID de Ozzie
Recomendado para: integraciones donde quieres control total sobre la creación de usuarios, o donde necesitas almacenar el UUID de Ozzie en tu propia base de datos.
Flujo B — Aprovisionamiento automático con ID externo
Omites la creación de usuarios por completo. Usa el formato external:{tu_id} como parámetro de ruta {user_id} en cualquier endpoint. En la primera llamada, Ozzie crea automáticamente el usuario en segundo plano y continúa procesando la solicitud.
POST /v1/users/external:usr_8821/financial-intake → usuario creado automáticamente en la 1ª llamada
POST /v1/users/external:usr_8821/plan/generate → mismo usuario, sin paso de creación
POST /v1/users/external:usr_8821/chat/messages → mismo usuario
Recomendado para: integraciones donde no quieres gestionar un paso de creación de usuario separado, o donde estás conectando una base de usuarios existente sin migrar IDs.
No se necesita ninguna configuración o bandera para usar el Flujo B. Cualquier valor no-UUID con prefijo external: se trata automáticamente como una búsqueda por ID externo. Si el usuario aún no existe, se crea silenciosamente.
Cómo funciona external:{id}
Cuando Ozzie recibe una solicitud con external:{tu_id} como parámetro user_id, él:
- Elimina el prefijo
external:y extrae tu ID - Busca al usuario por
(api_client_id, external_user_id) - Si lo encuentra → procesa la solicitud con el usuario encontrado
- Si no lo encuentra → crea automáticamente un nuevo usuario con
external_user_id = tu_idycreated_via = "api_external", luego continúa procesando la solicitud
El usuario creado automáticamente comienza con onboarding_stage: "financial_intake". Ozzie avanza el estado automáticamente después del intake financiero y la generación del plan, igual que con los usuarios creados via POST /v1/users.
Elegir entre los dos flujos
| Situación | Patrón recomendado |
|---|---|
| Quieres registrar usuarios con nombre, correo y teléfono antes de cualquier interacción | Flujo A — POST /v1/users |
| Tienes una base de usuarios existente y quieres cero fricción en la migración | Flujo B — external:{id} en cualquier endpoint |
| Estás construyendo un bot de WhatsApp o interfaz conversacional | Flujo A — establece phone en la creación para la correspondencia de número |
| Eres una fintech que hace proxy de solicitudes para tus propios usuarios | Flujo B — usa el ID de tu base de datos como ID externo |
| Necesitas ambos patrones en la misma integración | Combínalos libremente — cada usuario puede ser creado de cualquier manera |
Flujo B — Ejemplo completo
Este ejemplo envía el intake financiero, genera un plan e inicia un chat — todo sin ninguna llamada a POST /v1/users:
- curl
- Node.js
- Python
# Paso 1 — Enviar intake financiero (usuario creado automáticamente en esta llamada)
curl -X POST https://api.ozzieapp.com/v1/users/external:usr_8821/financial-intake \
-H "Authorization: Bearer TU_BEARER_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"monthly_income": 5000,
"monthly_expenses": 3200,
"financial_goal": "savings"
}'
# Paso 2 — Generar plan
curl -X POST https://api.ozzieapp.com/v1/users/external:usr_8821/plan/generate \
-H "Authorization: Bearer TU_BEARER_TOKEN" \
-H "Content-Type: application/json" \
-d '{}'
# Paso 3 — Chat
curl -X POST https://api.ozzieapp.com/v1/users/external:usr_8821/chat/messages \
-H "Authorization: Bearer TU_BEARER_TOKEN" \
-H "Content-Type: application/json" \
-d '{"message": "¿Cuánto debería ahorrar este mes?"}'
import fetch from 'node-fetch';
const BASE_URL = 'https://api.ozzieapp.com/v1';
const USER_REF = 'external:usr_8821'; // tu ID de usuario, con prefijo "external:"
const TOKEN = Buffer.from('tu_client_id:tu_client_secret').toString('base64');
const headers = {
'Authorization': `Bearer ${TOKEN}`,
'Content-Type': 'application/json',
};
async function post(path, body) {
const res = await fetch(`${BASE_URL}${path}`, {
method: 'POST', headers, body: JSON.stringify(body),
});
const json = await res.json();
if (!res.ok) throw new Error(`[${json.error?.code}] ${json.error?.message}`);
return json.data;
}
// Paso 1 — Intake financiero (usuario creado aquí si es nuevo)
const intake = await post(`/users/${USER_REF}/financial-intake`, {
monthly_income: 5000,
monthly_expenses: 3200,
financial_goal: 'savings',
});
console.log('Intake registrado. Superávit:', intake.monthly_surplus);
// Paso 2 — Generar plan
const plan = await post(`/users/${USER_REF}/plan/generate`, {});
console.log('Nivel del plan:', plan.plan_tier);
// Paso 3 — Chat
const reply = await post(`/users/${USER_REF}/chat/messages`, {
message: '¿Cuánto debería ahorrar este mes?',
});
console.log('Ozzie:', reply.message);
import httpx, base64
BASE_URL = "https://api.ozzieapp.com/v1"
USER_REF = "external:usr_8821" # tu ID de usuario, con prefijo "external:"
TOKEN = base64.b64encode(b"tu_client_id:tu_client_secret").decode()
headers = {
"Authorization": f"Bearer {TOKEN}",
"Content-Type": "application/json",
}
# Paso 1 — Intake financiero (usuario creado aquí si es nuevo)
resp = httpx.post(
f"{BASE_URL}/users/{USER_REF}/financial-intake",
headers=headers,
json={"monthly_income": 5000, "monthly_expenses": 3200, "financial_goal": "savings"},
)
resp.raise_for_status()
print("Superávit:", resp.json()["data"]["monthly_surplus"])
# Paso 2 — Generar plan
resp = httpx.post(f"{BASE_URL}/users/{USER_REF}/plan/generate", headers=headers, json={})
resp.raise_for_status()
print("Nivel del plan:", resp.json()["data"]["plan_tier"])
# Paso 3 — Chat
resp = httpx.post(
f"{BASE_URL}/users/{USER_REF}/chat/messages",
headers=headers,
json={"message": "¿Cuánto debería ahorrar este mes?"},
)
resp.raise_for_status()
print("Ozzie:", resp.json()["data"]["message"])
Recuperando un usuario aprovisionado automáticamente
Los usuarios creados via Flujo B pueden recuperarse en cualquier momento usando GET /v1/users/external:{id} o por su UUID de Ozzie una vez que lo conozcas:
curl https://api.ozzieapp.com/v1/users/external:usr_8821 \
-H "Authorization: Bearer TU_BEARER_TOKEN"
{
"object": "user",
"data": {
"id": "ozz_usr_01HX9KZMR4P5JQNBVT7YCW3DE",
"external_user_id": "usr_8821",
"name": null,
"email": null,
"phone": null,
"language": "en",
"onboarding_stage": "active",
"created_at": "2026-05-06T10:00:00Z"
}
}
Los usuarios aprovisionados automáticamente comienzan con name, email y phone como null. Puedes enriquecer el registro más adelante — contacta a commercial@ozzieapp.com si necesitas un endpoint de actualización de usuario.
Combinando ambos flujos
Ambos patrones son totalmente compatibles dentro de la misma cuenta de cliente de API. Algunos usuarios pueden crearse explícitamente via POST /v1/users (p.ej., usuarios que se registran con nombre y correo), mientras otros se aprovisionan automáticamente via external:{id} (p.ej., usuarios anónimos o importados de un sistema existente).
La única restricción es que external_user_id debe ser único por cliente de API. Un POST /v1/users explícito con el mismo external_user_id que un usuario ya aprovisionado automáticamente devolverá 409 CONFLICT.
Consideraciones de seguridad
- El prefijo
external:se resuelve después de la autenticación. Las solicitudes no autenticadas no pueden usarlo. - Los usuarios aprovisionados automáticamente via Flujo B están vinculados a tu cliente de API. Otro cliente no puede acceder a ellos aunque conozca el
external_user_id. - Los límites de tasa se aplican igualmente a ambos flujos. El aprovisionamiento automático de un nuevo usuario no consume una llamada de API extra — es parte de la misma solicitud.