Skip to main content

Plan

The Plan is the core output of Ozzie's financial analysis engine. It takes a user's financial intake data (income, expenses, debts, goals) and produces a structured roadmap with budget allocations, projected milestones, and prioritized action items.

The plan is the prerequisite for generating money moves and shapes every response from the AI chat coach.


Object reference​

FieldTypeDescription
idstringUnique identifier for the plan
user_idstringThe Ozzie user this plan belongs to
timeline_monthsintegerProjected months to reach the user's primary goal
allocationsobjectJSONB budget breakdown by category (percentages)
allocations.needsnumber% of net income allocated to essential needs
allocations.wantsnumber% of net income allocated to discretionary spending
allocations.savingsnumber% of net income allocated to savings
allocations.debtnumber% of net income allocated to debt repayment
key_metricsobjectKey projected financial indicators
key_metrics.monthly_savings_amountnumberDollar amount saved per month under this plan
key_metrics.projected_goal_datestring (ISO 8601)Estimated date the user reaches their goal
key_metrics.current_savings_ratenumberCurrent savings rate as a percentage of income
key_metrics.target_savings_ratenumberRecommended savings rate to hit the goal on time
key_metrics.debt_payoff_datestring (ISO 8601) | nullProjected debt-free date (if goal is debt)
action_itemsstring[]Ordered list of recommended next actions
personality_typestring | nullPersonality profile applied (if personalized)
created_atstring (ISO 8601)When the plan was generated
updated_atstring (ISO 8601)When the plan was last updated

POST /v1/users/{user_id}/plan​

Calculates a new financial plan for the user, or retrieves and refreshes an existing one. If a plan already exists, calling this endpoint recalculates it based on the latest intake and transaction data.

warning

This endpoint requires that a financial intake exists for the user. If no intake has been submitted, you will receive an INTAKE_REQUIRED error.

info

Plan generation is idempotent per user. You can safely call this endpoint multiple times β€” Ozzie will return the most current plan, recalculating if new intake or transaction data has been submitted since the last generation.

Path parameters​

ParameterTypeRequiredDescription
user_idstringYesThe Ozzie user ID

Request body​

No request body required. Ozzie reads all data from the user's existing intake and transactions.

Request​

curl -X POST https://api.ozzieapp.com/v1/users/usr_4f8a1b2c3d/plan \
-H "Authorization: Bearer ozp_Y2xpZW50X2ExYjJjM2Q0OnNrX2xpdmVfeEs5bVAycVI3dEwu" \
-H "Content-Type: application/json"

Response​

{
"object": "plan",
"data": {
"id": "pln_8b3d1e9f2a",
"user_id": "usr_4f8a1b2c3d",
"timeline_months": 57,
"allocations": {
"needs": 60,
"wants": 20,
"savings": 15,
"debt": 5
},
"key_metrics": {
"monthly_savings_amount": 562.50,
"projected_goal_date": "2029-11-01T00:00:00Z",
"current_savings_rate": 8,
"target_savings_rate": 15,
"debt_payoff_date": null
},
"action_items": [
"Increase your savings contribution from 8% to 15% of net income β€” start with $562.50/month",
"Cut discretionary spending (currently 28%) down to 20% by reducing dining out and streaming subscriptions",
"Set up an automatic transfer on payday so savings happen before you can spend them",
"Review your grocery spending β€” you're currently $120/month over a reasonable baseline for your income",
"Once your emergency fund hits $3,000, consider opening a high-yield savings account"
],
"personality_type": null,
"created_at": "2025-05-05T14:50:00Z",
"updated_at": "2025-05-05T14:50:00Z"
}
}

Errors​

CodeHTTP StatusWhen it occurs
UNAUTHORIZED401Missing or invalid credentials
NOT_FOUND404The user does not exist
INTAKE_REQUIRED422No financial intake exists for the user
INTERNAL_ERROR500Plan calculation failed β€” safe to retry

POST /v1/users/{user_id}/plan/personalize​

Adjusts the user's existing plan based on a personality profile. Personality-aware plans tune the language, pacing, and emphasis of recommendations to match how the user thinks about money.

info

Personality personalization requires a plan to already exist. Call POST /plan first, then call /plan/personalize to layer the personality profile on top.

Path parameters​

ParameterTypeRequiredDescription
user_idstringYesThe Ozzie user ID

Request body​

FieldTypeRequiredDescription
personality_typestringYesThe personality profile identifier (e.g., "optimizer", "avoider", "planner", "spender")

Personality types​

TypeDescription
"optimizer"Data-driven, motivated by efficiency and maximum returns
"planner"Methodical, prefers detailed roadmaps and predictability
"avoider"Anxious about finances; needs reassurance and small wins
"spender"Lifestyle-oriented; needs strategies that don't feel like deprivation

Request​

curl -X POST https://api.ozzieapp.com/v1/users/usr_4f8a1b2c3d/plan/personalize \
-H "Authorization: Bearer ozp_Y2xpZW50X2ExYjJjM2Q0OnNrX2xpdmVfeEs5bVAycVI3dEwu" \
-H "Content-Type: application/json" \
-d '{
"personality_type": "avoider"
}'

Response​

Returns the updated plan with the personality profile applied. Note how the action_items shift to gentler, smaller steps for an "avoider" profile.

{
"object": "plan",
"data": {
"id": "pln_8b3d1e9f2a",
"user_id": "usr_4f8a1b2c3d",
"timeline_months": 57,
"allocations": {
"needs": 60,
"wants": 20,
"savings": 15,
"debt": 5
},
"key_metrics": {
"monthly_savings_amount": 562.50,
"projected_goal_date": "2029-11-01T00:00:00Z",
"current_savings_rate": 8,
"target_savings_rate": 15,
"debt_payoff_date": null
},
"action_items": [
"You're already saving β€” that's something to feel good about. Let's just grow it a little.",
"Try moving just $50 more per paycheck into savings this month. One small step.",
"You don't have to cut everything. Pick one expense this week that felt forgettable and pause it.",
"Set up a $50 automatic transfer on payday β€” you won't notice it, but your savings will.",
"Your goal is doable. At this pace you'll have $3,000 saved within 6 months."
],
"personality_type": "avoider",
"created_at": "2025-05-05T14:50:00Z",
"updated_at": "2025-05-05T15:10:00Z"
}
}

Errors​

CodeHTTP StatusWhen it occurs
UNAUTHORIZED401Missing or invalid credentials
NOT_FOUND404The user does not exist
PLAN_REQUIRED422No plan exists yet β€” call POST /plan first
PERSONALITY_REQUIRED422personality_type field is missing from the request body
VALIDATION_ERROR422Unknown personality_type value
INVALID_JSON400Request body is not valid JSON
tip

You can determine a user's personality type by running a short quiz in your onboarding flow, then passing the result here. The personality_type string is stored on the plan and reflected in all subsequent AI coach responses for that user.