Skip to main content

Financial Intake

The financial intake is the foundation of every Ozzie user experience. It captures a point-in-time snapshot of a user's monthly financial picture β€” income, expenses, and primary goal. Ozzie uses this data to generate a personalized financial plan, compute savings targets, and provide context-aware recommendations.

Required before plan generation

POST /v1/users/{user_id}/plan/generate will return INTAKE_REQUIRED until at least one financial intake snapshot has been submitted. Always submit an intake before generating a plan.


The Financial Intake Object​

{
"object": "financial_intake",
"data": {
"id": "ozz_intake_01HX9M3FQKRNT8YDWP6BZ2LSA",
"user_id": "ozz_usr_01HX9KZMR4P5JQNBVT7YCW3DE",
"monthly_income": 5000,
"monthly_expenses": 3200,
"monthly_surplus": 1800,
"financial_goal": "savings",
"next_pay_date": "2025-05-15",
"created_at": "2025-05-05T14:31:00Z"
}
}

Financial Intake Object Fields​

FieldTypeDescription
idstringOzzie-generated UUID for this intake snapshot
user_idstringThe Ozzie user this intake belongs to
monthly_incomenumberGross monthly income in the user's base currency
monthly_expensesnumberTotal monthly outgoings
monthly_surplusnumberComputed field: monthly_income - monthly_expenses
financial_goalstring"savings" or "debt" β€” the user's primary goal type
next_pay_datestring | nullUser's next paycheck date in YYYY-MM-DD format
created_atstringISO 8601 UTC timestamp of when this snapshot was created

Immutable Snapshot Model​

Each call to POST /v1/users/{user_id}/financial-intake creates a new immutable snapshot. Previous snapshots are preserved in history. Ozzie always uses the most recently created snapshot for plan generation and recommendations.

This design means:

  • You never need to "update" an intake β€” just submit a new one.
  • Historical snapshots are available via GET /v1/users/{user_id}/financial-intake/history for audit and trend analysis.
  • Re-generating a plan after a new intake submission will pick up the latest numbers automatically.
May 1 intake: income=4500, expenses=3100 ← used for May plan
↓
May 31 intake: income=5000, expenses=3200 ← becomes the new latest
↓
POST /plan/generate β†’ uses May 31 snapshot
When to submit a new intake

Submit a fresh intake when the user reports a meaningful change: a salary increase, a new recurring bill, paying off a debt, or a life event (new job, moved cities). Monthly re-submission is a good default cadence.


POST /v1/users/{user_id}/financial-intake​

Stores a new monthly financial snapshot for the user.

Path Parameters​

ParameterDescription
user_idThe Ozzie user UUID or external:{external_user_id}

Request Body​

FieldTypeRequiredConstraintsDescription
monthly_incomenumberYesMust be β‰₯ 0Gross monthly income in the user's base currency (e.g., USD)
monthly_expensesnumberYesMust be β‰₯ 0Total monthly outgoings (rent, food, bills, subscriptions, etc.)
financial_goalstringYes"savings" | "debt"The user's primary financial objective
next_pay_datestringNoISO date YYYY-MM-DDDate of the user's next paycheck β€” used for cash-flow timing

financial_goal Values​

ValueMeaningPlan behavior
"savings"User's priority is building savings or an emergency fundPlan emphasizes contribution rate and compounding growth
"debt"User's priority is paying down debtPlan emphasizes debt payoff sequencing (avalanche or snowball)

Response​

Returns the created financial_intake object with HTTP 201 Created.

Errors​

CodeHTTPWhen
NOT_FOUND404The user_id does not exist
VALIDATION_ERROR400A required field is missing or has an invalid value
UNAUTHORIZED401Missing or invalid credentials

Examples​

curl -X POST \
https://api.ozzieapp.com/v1/users/ozz_usr_01HX9KZMR4P5JQNBVT7YCW3DE/financial-intake \
-H "Authorization: Bearer czJjbGllbnQ6czJzZWNyZXQ=" \
-H "Content-Type: application/json" \
-d '{
"monthly_income": 5000,
"monthly_expenses": 3200,
"financial_goal": "savings",
"next_pay_date": "2025-05-15"
}'

Example Response (201 Created):

{
"object": "financial_intake",
"data": {
"id": "ozz_intake_01HX9M3FQKRNT8YDWP6BZ2LSA",
"user_id": "ozz_usr_01HX9KZMR4P5JQNBVT7YCW3DE",
"monthly_income": 5000,
"monthly_expenses": 3200,
"monthly_surplus": 1800,
"financial_goal": "savings",
"next_pay_date": "2025-05-15",
"created_at": "2025-05-05T14:31:00Z"
}
}

Error Response β€” Missing required field (400):

{
"error": {
"code": "VALIDATION_ERROR",
"message": "Field 'financial_goal' is required. Must be one of: 'savings', 'debt'."
}
}

Error Response β€” Invalid income value (400):

{
"error": {
"code": "VALIDATION_ERROR",
"message": "Field 'monthly_income' must be a non-negative number. Received: -500."
}
}

GET /v1/users/{user_id}/financial-intake​

Returns the most recently created financial intake snapshot for the user.

Path Parameters​

ParameterDescription
user_idThe Ozzie user UUID or external:{external_user_id}

Response​

Returns the financial_intake object with HTTP 200 OK. If no intake has been submitted, returns HTTP 404 with code NOT_FOUND.

Errors​

CodeHTTPWhen
NOT_FOUND404The user exists but has not submitted any intake yet, or the user_id is invalid
UNAUTHORIZED401Missing or invalid credentials

Examples​

curl https://api.ozzieapp.com/v1/users/ozz_usr_01HX9KZMR4P5JQNBVT7YCW3DE/financial-intake \
-H "Authorization: Bearer czJjbGllbnQ6czJzZWNyZXQ="

Example Response (200 OK):

{
"object": "financial_intake",
"data": {
"id": "ozz_intake_01HX9M3FQKRNT8YDWP6BZ2LSA",
"user_id": "ozz_usr_01HX9KZMR4P5JQNBVT7YCW3DE",
"monthly_income": 5000,
"monthly_expenses": 3200,
"monthly_surplus": 1800,
"financial_goal": "savings",
"next_pay_date": "2025-05-15",
"created_at": "2025-05-05T14:31:00Z"
}
}

Error Response β€” No intake yet (404):

{
"error": {
"code": "NOT_FOUND",
"message": "No financial intake found for user 'ozz_usr_01HX9KZMR4P5JQNBVT7YCW3DE'. Submit a POST /v1/users/{user_id}/financial-intake first."
}
}
Checking intake before plan generation

Before calling POST /v1/users/{user_id}/plan/generate, you can optionally call this endpoint to confirm an intake exists and preview the monthly_surplus. This avoids a round-trip error if the intake was never submitted.