Users
The User object is the root entity in Ozzie. All other resources β financial intakes, plans, goals, transactions, chat messages β are scoped to a user. You create one user per person in your system, identified by your own external_user_id.
The User Objectβ
{
"object": "user",
"data": {
"id": "ozz_usr_01HX9KZMR4P5JQNBVT7YCW3DE",
"external_user_id": "usr_8821",
"name": "Maria Santos",
"email": "maria@example.com",
"phone": "+15551234567",
"language": "en",
"onboarding_stage": "active",
"created_at": "2025-05-05T14:30:00Z"
}
}
User Object Fieldsβ
| Field | Type | Description |
|---|---|---|
id | string | Ozzie-generated UUID for this user. Use this in all subsequent API paths. |
external_user_id | string | Your internal user ID. Unique per API client. |
name | string | null | User's display name. |
email | string | null | User's email address. |
phone | string | null | User's phone number in E.164 format. Required for WhatsApp integration. |
language | string | Language preference: "en", "pt", or "es". |
onboarding_stage | string | Current onboarding state (see table below). |
created_at | string | ISO 8601 UTC timestamp of when the user was created. |
onboarding_stage Valuesβ
| Value | Meaning |
|---|---|
intake_pending | User created, no financial intake submitted yet |
plan_pending | Intake submitted, plan not yet generated |
active | Plan generated β full platform access available |
POST /v1/usersβ
Creates a new user under your API client. Each user must have a unique external_user_id within your client account.
Request Bodyβ
| Field | Type | Required | Constraints | Description |
|---|---|---|---|---|
external_user_id | string | Yes | Max 100 chars | Your internal user ID (e.g. database primary key or UUID) |
name | string | No | Max 200 chars | User's display name |
email | string | No | Valid email format | User's email address |
phone | string | No | E.164 format | Phone number, e.g. +15551234567. Required for WhatsApp. |
language | string | No | "en" | "pt" | "es" | Chat and notification language. Defaults to "en". |
Responseβ
Returns the created user object with HTTP 201 Created.
Errorsβ
| Code | HTTP | When |
|---|---|---|
CONFLICT | 409 | A user with this external_user_id already exists under your client |
VALIDATION_ERROR | 400 | A field failed validation (e.g., invalid email format, phone not in E.164) |
UNAUTHORIZED | 401 | Missing or invalid credentials |
Examplesβ
- curl
- Node.js
- Python
curl -X POST https://api.ozzieapp.com/v1/users \
-H "Authorization: Bearer czJjbGllbnQ6czJzZWNyZXQ=" \
-H "Content-Type: application/json" \
-d '{
"external_user_id": "usr_8821",
"name": "Maria Santos",
"email": "maria@example.com",
"phone": "+15551234567",
"language": "en"
}'
import fetch from 'node-fetch';
const token = Buffer.from('your_client_id:your_client_secret').toString('base64');
const response = await fetch('https://api.ozzieapp.com/v1/users', {
method: 'POST',
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
external_user_id: 'usr_8821',
name: 'Maria Santos',
email: 'maria@example.com',
phone: '+15551234567',
language: 'en',
}),
});
const { data: user } = await response.json();
console.log('Created user:', user.id);
import requests
import base64
token = base64.b64encode(b'your_client_id:your_client_secret').decode()
response = requests.post(
'https://api.ozzieapp.com/v1/users',
headers={
'Authorization': f'Bearer {token}',
'Content-Type': 'application/json',
},
json={
'external_user_id': 'usr_8821',
'name': 'Maria Santos',
'email': 'maria@example.com',
'phone': '+15551234567',
'language': 'en',
}
)
user = response.json()['data']
print(f"Created user: {user['id']}")
Example Response (201 Created):
{
"object": "user",
"data": {
"id": "ozz_usr_01HX9KZMR4P5JQNBVT7YCW3DE",
"external_user_id": "usr_8821",
"name": "Maria Santos",
"email": "maria@example.com",
"phone": "+15551234567",
"language": "en",
"onboarding_stage": "intake_pending",
"created_at": "2025-05-05T14:30:00Z"
}
}
Error Response β Duplicate user (409 Conflict):
{
"error": {
"code": "CONFLICT",
"message": "A user with external_user_id 'usr_8821' already exists under your API client. Use GET /v1/users/{user_id} to retrieve them."
}
}
Error Response β Invalid email (400 Validation Error):
{
"error": {
"code": "VALIDATION_ERROR",
"message": "Field 'email' must be a valid email address. Received: 'not-an-email'."
}
}
If you're not certain whether a user already exists, catch the CONFLICT error and immediately call GET /v1/users/external:{external_user_id} to retrieve the existing record. This is safe to do in an upsert-style flow.
GET /v1/users/{user_id}β
Retrieves a user by their Ozzie UUID or by their external_user_id.
Path Parameterβ
| Parameter | Description |
|---|---|
user_id | Either the Ozzie UUID (e.g. ozz_usr_01HX9KZMR4P5JQNBVT7YCW3DE) or a prefixed external ID (e.g. external:usr_8821) |
Looking Up by external_user_idβ
To fetch a user by your own ID instead of the Ozzie UUID, prefix the value with external::
GET /v1/users/external:usr_8821
This is equivalent to querying by external_user_id and avoids the need to store the Ozzie UUID in your database at creation time.
Responseβ
Returns the user object with HTTP 200 OK.
Errorsβ
| Code | HTTP | When |
|---|---|---|
NOT_FOUND | 404 | No user exists with this ID under your client |
UNAUTHORIZED | 401 | Missing or invalid credentials |
FORBIDDEN | 403 | The user exists but belongs to a different API client |
Examplesβ
- curl
- Node.js
- Python
# By Ozzie UUID
curl https://api.ozzieapp.com/v1/users/ozz_usr_01HX9KZMR4P5JQNBVT7YCW3DE \
-H "Authorization: Bearer czJjbGllbnQ6czJzZWNyZXQ="
# By external_user_id
curl "https://api.ozzieapp.com/v1/users/external:usr_8821" \
-H "Authorization: Bearer czJjbGllbnQ6czJzZWNyZXQ="
import fetch from 'node-fetch';
const token = Buffer.from('your_client_id:your_client_secret').toString('base64');
// By Ozzie UUID
const res = await fetch(
'https://api.ozzieapp.com/v1/users/ozz_usr_01HX9KZMR4P5JQNBVT7YCW3DE',
{ headers: { 'Authorization': `Bearer ${token}` } }
);
// By external_user_id
const res2 = await fetch(
'https://api.ozzieapp.com/v1/users/external:usr_8821',
{ headers: { 'Authorization': `Bearer ${token}` } }
);
const { data: user } = await res.json();
console.log('Onboarding stage:', user.onboarding_stage);
import requests
import base64
token = base64.b64encode(b'your_client_id:your_client_secret').decode()
headers = {'Authorization': f'Bearer {token}'}
# By Ozzie UUID
response = requests.get(
'https://api.ozzieapp.com/v1/users/ozz_usr_01HX9KZMR4P5JQNBVT7YCW3DE',
headers=headers
)
# By external_user_id
response2 = requests.get(
'https://api.ozzieapp.com/v1/users/external:usr_8821',
headers=headers
)
user = response.json()['data']
print(f"Onboarding stage: {user['onboarding_stage']}")
Example Response (200 OK):
{
"object": "user",
"data": {
"id": "ozz_usr_01HX9KZMR4P5JQNBVT7YCW3DE",
"external_user_id": "usr_8821",
"name": "Maria Santos",
"email": "maria@example.com",
"phone": "+15551234567",
"language": "en",
"onboarding_stage": "active",
"created_at": "2025-05-05T14:30:00Z"
}
}
Error Response β Not found (404):
{
"error": {
"code": "NOT_FOUND",
"message": "No user found with id 'ozz_usr_NOTEXIST' under your API client."
}
}
Use GET /v1/users/{user_id} to check a user's onboarding_stage before deciding which UI to show. If the stage is intake_pending, prompt the user for their financial details. If plan_pending, show a loading state while triggering plan generation.