Autenticação
A API Ozzie utiliza autenticação HTTP Bearer. Toda requisição deve incluir um header Authorization com um token codificado em base64 derivado do seu client_id e client_secret.
Obtendo suas credenciais
Para obter credenciais de API, você pode:
- Entrar em contato com o time Ozzie em commercial@ozzieapp.com para discutir seu caso de uso e obter acesso
- Acessar o painel Ozzie e navegar até Configurações → Chaves de API para gerar um par de
client_ideclient_secret
Você receberá dois valores:
| Valor | Exemplo | Descrição |
|---|---|---|
client_id | ozz_client_a1b2c3d4 | Um identificador estável para seu cliente de API. Seguro para registrar em logs. |
client_secret | sk_live_xK9mP2qR7tL... | Uma chave secreta. Trate-a como uma senha. Nunca a exponha. |
Seu client_secret é exibido apenas uma vez no momento da geração. Armazene-o com segurança imediatamente — se você perdê-lo, precisará rotacionar suas credenciais.
Como a autenticação funciona
O Ozzie usa HTTP Basic Auth codificado como token Bearer. O token é a codificação base64 da string client_id:client_secret (separados por dois pontos, sem espaços).
token = base64("ozz_client_a1b2c3d4:sk_live_xK9mP2qR7tL...")
Authorization: Bearer <token>
Este é um token único e estático que você gera uma vez e reutiliza em todas as requisições. Não é necessário trocá-lo por um token de sessão nem renová-lo periodicamente — ele é válido até que você rotacione suas credenciais.
Codificando seu 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...
Fazendo requisições autenticadas
Com o token em mãos, inclua-o em todas as requisições no header 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())
Construa o token uma única vez na inicialização da aplicação e reutilize-o em todas as requisições. Não há expiração — você não precisa regenerá-lo a cada requisição.
Rate limiting
Toda resposta da API inclui headers de limite de taxa para que você possa acompanhar seu uso:
| Header | Descrição |
|---|---|
X-RateLimit-Limit | O número máximo de requisições permitidas na janela atual |
X-RateLimit-Remaining | Quantas requisições restam na janela atual |
X-RateLimit-Reset | Timestamp Unix (segundos) de quando a janela redefine e seu limite é reabastecido |
Exemplo de headers de resposta:
HTTP/1.1 200 OK
Content-Type: application/json
X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 847
X-RateLimit-Reset: 1746489600
Quando você atinge o limite de taxa, a API retorna status 429. Aguarde e tente novamente após o timestamp em 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"
}
]
}
}
Os limites de taxa variam por tier. Clientes do tier gratuito têm limites menores do que clientes Pro ou Enterprise. Entre em contato com commercial@ozzieapp.com se precisar de maior capacidade.
Códigos de erro de autenticação
| Status HTTP | Código de Erro | Quando ocorre |
|---|---|---|
401 Unauthorized | UNAUTHORIZED | O header Authorization está ausente, malformado ou contém credenciais inválidas |
429 Too Many Requests | RATE_LIMIT_EXCEEDED | Seu cliente enviou mais requisições do que o permitido na janela atual |
Exemplo de resposta 401:
{
"error": {
"code": "UNAUTHORIZED",
"message": "Invalid or missing authorization credentials.",
"details": []
}
}
Causas comuns de um 401:
- Esqueceu de incluir o header
Authorization - Codificou
client_id:client_secretcom espaços ou caracteres extras - Usando um
client_secretque foi rotacionado ou revogado - Enviando a string bruta
client_id:client_secretem vez da versão codificada em base64
Autenticação do usuário final (OTP)
Aplicativos para consumidores autenticam seus usuários finais por meio de um código OTP de 6 dígitos entregue via WhatsApp ou e-mail. Esse fluxo é gerenciado inteiramente pela Channel API — seu servidor chama channel.ozzieapp.com em nome do usuário.
O fluxo OTP é separado da autenticação de cliente de API. Seu client_id / client_secret são credenciais servidor a servidor. O OTP é para autenticar o usuário humano dentro do seu aplicativo.
O fluxo OTP completo
1. Registrar o usuário (apenas uma vez)
│
▼
POST /v1/users → recebe o user_id do Ozzie
│
▼
2. Solicitar um código OTP
│
▼
POST /v1/auth/otp/request → código enviado ao usuário via WhatsApp ou e-mail
│
▼
3. Usuário digita o código na sua interface
│
▼
POST /v1/auth/otp/verify → retorna user_id + onboarding_stage
│
▼
4. Seu app cria uma sessão para o usuário
(SESSION_SECRET e gerenciamento de sessão são de responsabilidade do seu app)
Detalhes do OTP
| Propriedade | Valor |
|---|---|
| Comprimento do código | 6 dígitos |
| TTL | 10 minutos |
| Uso único | Sim — o código é invalidado na primeira verificação bem-sucedida |
| Armazenamento | Hash bcrypt no banco de dados |
identifier | Telefone no formato E.164 (ex.: +5511999990000) ou endereço de e-mail |
channel | "whatsapp" ou "email" — detectado automaticamente a partir do identifier se omitido |
Exemplo em 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 e o gerenciamento de cookies de sessão são responsabilidade do seu próprio backend (ex.: Lovable). O Ozzie não emite tokens de sessão — ele apenas valida o OTP e retorna o user_id e o onboarding_stage que seu app precisa para inicializar a sessão.
Para uma referência completa sobre os parâmetros e códigos de erro de requisição/verificação de OTP, consulte a referência de Auth.
Multi-tenancy e escopo de cliente
Seu client_id é a raiz do seu namespace de tenant. Todo usuário criado com POST /users pertence ao seu cliente, e suas credenciais de API só podem acessar usuários que você criou. Você não pode ler ou modificar usuários criados por outros clientes de API.
Isso significa que:
- Você gerencia todos os seus usuários finais por meio de um único conjunto de credenciais
- Os usuários são identificados pelo seu próprio
external_user_id— você é responsável pelo mapeamento entre os registros de usuário do Ozzie e seu banco de dados - Toda atividade (intake financeiro, transações, planos, chat) tem escopo para os usuários do seu
client_id
Não há autenticação por usuário. Seu backend no lado do servidor se autentica no Ozzie usando suas credenciais de cliente e age em nome dos seus usuários. Nunca envie suas credenciais de API para um cliente frontend.
Boas práticas de segurança
Nunca exponha credenciais no lado do cliente. Seu client_id e client_secret devem existir somente no ambiente do lado do servidor. Um navegador ou aplicativo mobile que chame a API Ozzie diretamente exporia suas credenciais a qualquer pessoa que inspecione o tráfego de rede.
Use variáveis de ambiente. Armazene as credenciais em variáveis de ambiente, não hardcoded nos arquivos de código-fonte. Isso previne commits acidentais no controle de versão.
# .env (never commit this file)
OZZIE_CLIENT_ID=ozz_client_a1b2c3d4
OZZIE_CLIENT_SECRET=sk_live_xK9mP2qR7tL...
Rotacione os secrets periodicamente. Gere um novo client_secret no painel Ozzie em uma programação regular (ex.: a cada 90 dias), ou imediatamente se suspeitar que um secret foi comprometido. Atualize suas variáveis de ambiente e faça o deploy antes de revogar o secret antigo.
Use credenciais separadas por ambiente. Crie clientes de API distintos para seus ambientes de desenvolvimento, staging e produção, para que o tráfego de teste nunca toque dados reais de usuários, e um secret de desenvolvimento comprometido não possa afetar a produção.
Limite o acesso de saída do seu servidor. Se sua infraestrutura suportar, restrinja chamadas HTTPS de saída para api.ozzieapp.com apenas aos serviços que precisam disso — não a todos os servidores do seu stack.