Skip to main content

Auth

Base URL: https://channel.ozzieapp.com/v1

The Auth endpoints authenticate end users via a 6-digit OTP code delivered by WhatsApp or email. This is distinct from API client authentication (which uses a static Bearer token) β€” OTP is for the human user inside your app.

info

For the conceptual overview of how OTP fits into the full onboarding flow, see Architecture.


OTP details​

PropertyValue
Code length6 digits
TTL10 minutes
Single-useYes β€” invalidated on first successful verify
Storagebcrypt-hashed
identifierE.164 phone (e.g. +5511999990000) or email address
channel auto-detectionE.164 phone β†’ whatsapp; email address β†’ email

POST /v1/auth/otp/request​

Generates a 6-digit OTP code and delivers it to the user via WhatsApp or email.

Request body​

FieldTypeRequiredDescription
identifierstringYesPhone in E.164 format or email address
channelstringNo"whatsapp" or "email". Auto-detected from identifier if omitted.
languagestringNo"pt" | "en" | "es" β€” language of the OTP message. Defaults to "en".
curl -X POST https://channel.ozzieapp.com/v1/auth/otp/request \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"identifier": "+5511999990000",
"language": "pt"
}'

Response (200 OK):

{
"object": "otp_request",
"data": {
"channel": "whatsapp",
"masked": "+55119****0000",
"expires_at": "2026-05-16T14:40:00Z"
}
}
FieldDescription
channelDelivery channel used: "whatsapp" or "email"
maskedMasked version of the identifier β€” safe to display to the user for confirmation
expires_atISO 8601 timestamp when the code expires (10 minutes from issuance)

POST /v1/auth/otp/verify​

Verifies the OTP code the user entered. On success, returns the Ozzie user_id and the user's current onboarding_stage.

Request body​

FieldTypeRequiredDescription
identifierstringYesSame phone or email used in the request step
codestringYes6-digit code the user received
curl -X POST https://channel.ozzieapp.com/v1/auth/otp/verify \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"identifier": "+5511999990000",
"code": "482910"
}'

Response (200 OK):

{
"object": "otp_verify",
"data": {
"user_id": "external:usr_8821",
"core_user_id": "ozz_usr_01HX9KZMR4P5JQNBVT7YCW3DE",
"onboarding_stage": "financial_intake"
}
}
FieldDescription
user_idThe Ozzie user identifier in external:{id} format β€” use this in subsequent path params
core_user_idThe internal Ozzie UUID
onboarding_stageCurrent stage: financial_intake | personality | plan | active

Errors​

CodeHTTPWhen
INVALID_CODE400The code does not match or has already been used
CODE_EXPIRED400The code was issued more than 10 minutes ago
NOT_FOUND404No pending OTP found for this identifier
VALIDATION_ERROR400identifier or code field is missing or malformed

Full onboarding flow​

After a successful verify, use onboarding_stage to route the user to the correct screen:

onboarding_stageNext step
financial_intakeShow the financial intake form β†’ POST /v1/users/:id/financial-intake
personalityShow the personality quiz β†’ GET /v1/personality/questions + POST /v1/personality/score
planShow the plan generation screen β†’ POST /v1/users/:id/plan/generate, then PATCH /v1/users/:id/onboarding/complete
activeUser is fully onboarded β€” go directly to the main chat screen
tip

SESSION_SECRET and session cookie management are your app's responsibility. Ozzie does not issue session tokens β€” it only validates the OTP code and returns the data your app needs to bootstrap its own session.