Skip to main content

Plan

The Plan is the central output of Ozzie's financial analysis engine. It takes the 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 all AI coach responses in chat.


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 payments
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 reach the goal on time
key_metrics.debt_payoff_datestring (ISO 8601) | nullProjected debt payoff date (if the goal is debt)
action_itemsstring[]Ordered list of recommended next actions
personality_typestring | nullApplied personality profile (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 updates an existing one. If a plan already exists, calling this endpoint recalculates it based on the most recent intake and transaction data.

warning

This endpoint requires a financial intake to exist 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 up-to-date 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 existing intake and transaction data for the user.

Request​

curl -X POST https://channel.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",
"Reduce discretionary spending (currently 28%) to 20% by cutting dining out and streaming subscriptions",
"Set up an automatic transfer on payday so savings happen before you spend",
"Review your food spending β€” you are $120/month above a reasonable baseline for your income",
"When your emergency fund reaches $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 adjust the language, pacing, and emphasis of recommendations to match how the user thinks about money.

info

Personalization requires a plan to already exist. Call POST /plan first, then call /plan/personalize to add the personality profile.

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 do not feel like deprivation

Request​

curl -X POST https://channel.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. Notice how action_items shift to softer, 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 are already saving β€” that is something to feel good about. Let us just grow it a little.",
"Try transferring just $50 more per paycheck to savings this month. One small step.",
"You do not need to cut everything. Pick one expense this week that felt forgettable and pause it.",
"Set up an automatic transfer of $50 on payday β€” you will not notice it, but your savings will.",
"Your goal is reachable. At this pace you will have $3,000 saved in 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 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 and 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.