Autenticación
La API de Ozzie utiliza autenticación HTTP Bearer. Cada solicitud debe incluir un encabezado Authorization con un token codificado en base64 derivado de tu client_id y client_secret.
Cómo obtener tus credenciales
Para obtener credenciales de API, ya sea:
- Contacta al equipo de Ozzie en commercial@ozzieapp.com para discutir tu caso de uso y obtener acceso
- Inicia sesión en el panel de Ozzie y navega a Configuración → Claves de API para generar un par
client_idyclient_secret
Recibirás dos valores:
| Valor | Ejemplo | Descripción |
|---|---|---|
client_id | ozz_client_a1b2c3d4 | Un identificador estable para tu cliente de API. Seguro para registrar. |
client_secret | sk_live_xK9mP2qR7tL... | Una clave secreta. Trátala como una contraseña. Nunca la expongas. |
Tu client_secret se muestra solo una vez al momento de su generación. Guárdalo de forma segura inmediatamente — si lo pierdes, necesitarás rotar tus credenciales.
Cómo funciona la autenticación
Ozzie usa HTTP Basic Auth codificado como token Bearer. El token es la codificación base64 del string client_id:client_secret (separados por dos puntos, sin espacios).
token = base64("ozz_client_a1b2c3d4:sk_live_xK9mP2qR7tL...")
Authorization: Bearer <token>
Este es un token estático y único que generas una vez y reutilizas en todas las solicitudes. No necesitas intercambiarlo por un token de sesión ni actualizarlo según un horario — es válido hasta que rotas tus credenciales.
Codificando tu token
- Shell
- Node.js
- Python
# Encode your credentials
CLIENT_ID="ozz_client_a1b2c3d4"
CLIENT_SECRET="sk_live_xK9mP2qR7tL..."
TOKEN=$(echo -n "$CLIENT_ID:$CLIENT_SECRET" | base64)
echo $TOKEN
# ozp_Y2xpZW50X2ExYjJjM2Q0OnNrX2xpdmVfeEs5bVAycVI3dEwu...
const clientId = process.env.OZZIE_CLIENT_ID;
const clientSecret = process.env.OZZIE_CLIENT_SECRET;
const token = Buffer.from(`${clientId}:${clientSecret}`).toString('base64');
console.log(token);
// ozp_Y2xpZW50X2ExYjJjM2Q0OnNrX2xpdmVfeEs5bVAycVI3dEwu...
import base64
import os
client_id = os.environ["OZZIE_CLIENT_ID"]
client_secret = os.environ["OZZIE_CLIENT_SECRET"]
token = base64.b64encode(f"{client_id}:{client_secret}".encode()).decode()
print(token)
# ozp_Y2xpZW50X2ExYjJjM2Q0OnNrX2xpdmVfeEs5bVAycVI3dEwu...
Realizando solicitudes autenticadas
Una vez que tienes el token, adjúntalo a cada solicitud en el encabezado Authorization:
- curl
- Node.js
- Python
curl https://api.ozzieapp.com/v1/api/v1/users \
-H "Authorization: Bearer ozp_Y2xpZW50X2ExYjJjM2Q0OnNrX2xpdmVfeEs5bVAycVI3dEwu..." \
-H "Content-Type: application/json" \
-d '{
"external_user_id": "user_123",
"name": "Alex Johnson",
"language": "en"
}'
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/api/v1/users', {
method: 'POST',
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
external_user_id: 'user_123',
name: 'Alex Johnson',
language: 'en',
}),
});
const data = await response.json();
console.log(data);
import base64
import os
import 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/api/v1/users",
headers={
"Authorization": f"Bearer {token}",
"Content-Type": "application/json",
},
json={
"external_user_id": "user_123",
"name": "Alex Johnson",
"language": "en",
},
)
print(response.json())
Construye el token una vez al inicio de la aplicación y reutilízalo en todas las solicitudes. No tiene vencimiento — no necesitas regenerarlo por cada solicitud.
Límite de tasa
Cada respuesta de la API incluye encabezados de límite de tasa para que puedas monitorear tu uso:
| Encabezado | Descripción |
|---|---|
X-RateLimit-Limit | El número máximo de solicitudes permitidas en la ventana actual |
X-RateLimit-Remaining | Cuántas solicitudes te quedan en la ventana actual |
X-RateLimit-Reset | Timestamp Unix (segundos) cuando se reinicia la ventana y tu límite se repone |
Ejemplo de encabezados de respuesta:
HTTP/1.1 200 OK
Content-Type: application/json
X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 847
X-RateLimit-Reset: 1746489600
Cuando alcanzas el límite de tasa, la API devuelve un estado 429. Espera y reintenta después del timestamp en X-RateLimit-Reset.
{
"error": {
"code": "RATE_LIMIT_EXCEEDED",
"message": "You have exceeded the rate limit for this window. Please retry after the reset time.",
"details": [
{
"reset_at": 1746489600,
"limit": 1000,
"window": "1 minute"
}
]
}
}
Los límites de tasa varían según el tier. Los clientes del tier gratuito tienen límites más bajos que los clientes Pro o Enterprise. Contacta commercial@ozzieapp.com si necesitas mayor capacidad.
Códigos de error de autenticación
| Estado HTTP | Código de error | Cuándo ocurre |
|---|---|---|
401 Unauthorized | UNAUTHORIZED | El encabezado Authorization está ausente, malformado o contiene credenciales inválidas |
429 Too Many Requests | RATE_LIMIT_EXCEEDED | Tu cliente ha enviado más solicitudes de las permitidas en la ventana actual |
Ejemplo de respuesta 401:
{
"error": {
"code": "UNAUTHORIZED",
"message": "Invalid or missing authorization credentials.",
"details": []
}
}
Causas comunes de un error 401:
- Olvidaste incluir el encabezado
Authorization - Codificaste
client_id:client_secretcon espacios o caracteres adicionales - Usas un
client_secretque fue rotado o revocado - Enviaste el string
client_id:client_secretsin codificar en base64
Autenticación del usuario final (OTP)
Las aplicaciones de consumo autentican a sus usuarios finales mediante un código OTP de 6 dígitos entregado por WhatsApp o correo electrónico. Este flujo es manejado completamente por la Channel API — tu servidor llama a channel.ozzieapp.com en nombre del usuario.
El flujo OTP es independiente de la autenticación del cliente de API. Tu client_id / client_secret son credenciales de servidor a servidor. El OTP sirve para autenticar al usuario final humano dentro de tu aplicación.
El flujo OTP completo
1. Registrar al usuario (una sola vez)
│
▼
POST /v1/users → recibe el user_id de Ozzie
│
▼
2. Solicitar un código OTP
│
▼
POST /v1/auth/otp/request → código enviado al usuario por WhatsApp o correo
│
▼
3. El usuario ingresa el código en tu UI
│
▼
POST /v1/auth/otp/verify → devuelve user_id + onboarding_stage
│
▼
4. Tu aplicación crea una sesión para el usuario
(SESSION_SECRET y la gestión de sesiones son responsabilidad de tu aplicación)
Detalles del OTP
| Propiedad | Valor |
|---|---|
| Longitud del código | 6 dígitos |
| TTL | 10 minutos |
| Uso único | Sí — el código se invalida al primer verificado exitoso |
| Almacenamiento | Hasheado con bcrypt en la base de datos |
identifier | Teléfono en formato E.164 (ej. +5511999990000) o dirección de correo electrónico |
channel | "whatsapp" o "email" — detectado automáticamente desde identifier si se omite |
Ejemplo en TypeScript
const BASE = 'https://channel.ozzieapp.com/v1';
const token = Buffer.from(
`${process.env.OZZIE_CLIENT_ID}:${process.env.OZZIE_CLIENT_SECRET}`
).toString('base64');
const headers = {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json',
};
// Step 1 — Register the user (skip if already registered; handle 409 gracefully)
const registerRes = await fetch(`${BASE}/users`, {
method: 'POST',
headers,
body: JSON.stringify({
external_user_id: 'usr_8821', // your own user ID — do NOT include "external:" prefix
name: 'Maria Santos',
phone: '+5511999990000', // E.164 format
language: 'pt',
}),
});
if (!registerRes.ok && registerRes.status !== 409) {
throw new Error('User registration failed');
}
// Step 2 — Request OTP
const otpRes = await fetch(`${BASE}/auth/otp/request`, {
method: 'POST',
headers,
body: JSON.stringify({
identifier: '+5511999990000',
language: 'pt',
// channel: 'whatsapp' // optional — auto-detected from E.164 phone
}),
});
const { data: otpData } = await otpRes.json();
// otpData = { channel: "whatsapp", masked: "+55119****0000", expires_at: "2026-05-16T..." }
// Step 3 — Verify OTP (called after the user submits the code in your UI)
async function verifyOtp(code: string) {
const verifyRes = await fetch(`${BASE}/auth/otp/verify`, {
method: 'POST',
headers,
body: JSON.stringify({
identifier: '+5511999990000',
code,
}),
});
if (!verifyRes.ok) {
const { error } = await verifyRes.json();
// error.code === 'INVALID_CODE' or 'CODE_EXPIRED'
throw new Error(error.code);
}
const { data } = await verifyRes.json();
// data = { user_id: "external:usr_8821", core_user_id: "uuid", onboarding_stage: "financial_intake" }
// Step 4 — Create a session (SESSION_SECRET is your app's own concern, not Ozzie's)
return data;
}
SESSION_SECRET y la gestión de cookies de sesión son manejados por tu propio backend (ej. Lovable). Ozzie no emite tokens de sesión — solo valida el OTP y devuelve el user_id y el onboarding_stage que tu aplicación necesita para inicializar la sesión.
Para una referencia completa sobre los parámetros de solicitud/verificación OTP y los códigos de error, consulta la referencia de Auth.
Multi-tenancy y alcance del cliente
Tu client_id es la raíz de tu espacio de nombres de tenant. Cada usuario que creas con POST /users pertenece a tu cliente, y tus credenciales de API solo pueden acceder a los usuarios que tú creaste. No puedes leer ni modificar usuarios creados por otros clientes de API.
Esto significa:
- Gestionas todos tus usuarios finales a través de un único conjunto de credenciales
- Los usuarios se identifican por tu propio
external_user_id— tú posees el mapeo entre los registros de usuario de Ozzie y tu base de datos - Toda la actividad (intake financiero, transacciones, planes, chat) está limitada a los usuarios bajo tu
client_id
No existe autenticación por usuario. Tu backend del lado del servidor se autentica con Ozzie usando tus credenciales de cliente y luego actúa en nombre de tus usuarios. Nunca envíes tus credenciales de API a un cliente frontend.
Buenas prácticas de seguridad
Nunca expongas las credenciales en el lado del cliente. Tu client_id y client_secret solo deben existir en tu entorno del lado del servidor. Un navegador o aplicación móvil que llame directamente a la API de Ozzie expondría tus credenciales a cualquiera que inspeccione el tráfico de red.
Usa variables de entorno. Almacena las credenciales en variables de entorno, no hardcodeadas en archivos de código fuente. Esto previene commits accidentales al control de versiones.
# .env (never commit this file)
OZZIE_CLIENT_ID=ozz_client_a1b2c3d4
OZZIE_CLIENT_SECRET=sk_live_xK9mP2qR7tL...
Rota los secretos periódicamente. Genera un nuevo client_secret desde el panel de Ozzie de forma regular (ej., cada 90 días), o de inmediato si sospechas que un secreto fue comprometido. Actualiza tus variables de entorno y realiza el despliegue antes de revocar el secreto anterior.
Usa credenciales separadas por entorno. Crea clientes de API distintos para tus entornos de desarrollo, staging y producción, de modo que el tráfico de prueba nunca toque datos reales de usuarios, y que un secreto de desarrollo comprometido no afecte la producción.
Limita el acceso saliente de tu servidor. Si tu infraestructura lo permite, restringe las llamadas HTTPS salientes a api.ozzieapp.com únicamente desde los servicios que lo necesitan — no desde cada servidor en tu stack.