🔧 API Orchestration Example (EP-Centric)

How a page designer would use BOTH Calculate and Guidance APIs to build an interactive calculator.
NO AI ASSIST — Just code logic to orchestrate the APIs.
Entry-point-centric model: UTMs / entry_point_id resolve to an Entry Point. calculation_results is Optional.

5 STATELESS SCENARIOS — Each is a complete, independent call SCENARIO 1: DISCOVER (First Page Load — UTMs Only) POST /api/v1/guidance/context { utm_campaign: "taxes", utm_source: "google" } ← No user data, no calc results → entry_point_resolution: { method: "UTM", id: "tax-efficiency" } → branching_questions: ["What is your annual income?"] SCENARIO 2: HOOK (Age + Income Only) POST /api/v1/guidance/context { entry_point_id: "tax-efficiency", user_profile: { current_age: 45, income: 200000 } } ← No calc results → entry_point_resolution: { method: "EXPLICIT", id: "tax-efficiency" } → branching_questions: ["What state do you live in?"] SCENARIO 3: ENGAGE (With State + Calc Results) POST /api/v1/calculate/simulate → calc_results (charts update immediately) POST /api/v1/guidance/context { entry_point_id: "tax-efficiency", user_profile: { ...+state_code }, calculation_results: calc_results } ← Optional: enables $ insights → headline: "You're on track — Evergreen adds $350K" SCENARIO 4: QUALIFY (Full Account Breakdown) Same as ENGAGE but with tier 3 calc_results (taxable accounts) → differentiators: [{ id: "tax_loss_harvesting", status: "REVEALED", value: 47000 }] SCENARIO 5: INTERACTIVE SLIDER (Calculate → Guidance Debounce) USER MOVES SLIDER (every movement) │ ▼ ┌───────────────────────────────────────────────────────────────────────────────┐ │ STEP 1: CALCULATE API (called immediately, <20ms) │ │ POST /api/v1/calculate/simulate │ │ INPUT: user_profile, portfolio, config │ │ OUTPUT: PURE NUMBERS — success_probability, wealth, chart_data │ │ ➔ Charts update IMMEDIATELY │ └───────────────────────────────────────────────────────────────────────────────┘ │ ▼ (debounce 300ms - wait for user to stop) ┌───────────────────────────────────────────────────────────────────────────────┐ │ STEP 2: GUIDANCE API (called on commit, ~50ms) │ │ POST /api/v1/guidance/context │ │ INPUT: entry_point_id, user_profile, calculation_results (OPTIONAL) │ │ OUTPUT: HAND-CRAFTED INTERPRETATION │ │ • entry_point_resolution: { method: "EXPLICIT", id: "tax-efficiency" } │ │ • headline: "You're on track — Evergreen adds $350K" │ │ • differentiators: [ { value: 47000, insight: "..." } ] │ │ • branching_questions: [...] │ │ ➔ Text/insights update after user stops │ └───────────────────────────────────────────────────────────────────────────────┘
KEY INSIGHTS (EP-Centric Model):

Entry point resolution priority: 1. Explicit entry_point_id (deeplink) → method: EXPLICIT
2. utm_campaign → mapped via campaigns_config → method: UTM
3. Behavioral signals → method: BEHAVIORAL
4. Default entry point → method: DEFAULT

calculation_results is Optional — Guidance works at any stage:
• First page load: UTMs only → DISCOVER stage, first branching question
• With user data: HOOK/ENGAGE stage, unlockable differentiators
• With calc results: dollar-value insights, REVEALED differentiators

Why this pattern?
• Sliders feel instant — Calculate API updates charts on every movement
• Text is meaningful — Guidance API interprets numbers using hand-crafted rules
• No AI hallucination — All text comes from entry_points.json config
• Stateless — each Guidance call is self-contained

📝 User Input (Progressive)

TIER 1
Ready — Enter data and click Calculate

📊 Combined Results (Calculate + Guidance)

HOOK
ENGAGE
QUALIFY
PLAN
READY

From Calculate API /api/v1/calculate/simulate

Success Probability

Wealth at Retirement (P50)

Evergreen Tax Alpha Benefit

Tax Bracket

Differentiators (Guidance tells status, Calculate provides numbers)

🔒 UNLOCKABLE
Add taxable account balance to reveal TLH insights

🧭 From Guidance API /api/v1/guidance

Current Stage

HOOK

Data Fidelity Tier

TIER_1

Next Questions to Ask

What state do you live in?
Unlocks: state-specific tax strategies

Progression

Progress to Next Stage

Next Stage: ENGAGE

Data Needed: state_code

Progress: 20%

IMPORTANT:
The Guidance API does NOT calculate numbers.
It tells you:
• What stage the user is in
• What questions to ask next
• What differentiators are UNLOCKABLE vs REVEALED

The ACTUAL numbers come from the Calculate API!

💻 Interactive Slider Code (The Debounce Pattern)

// INTERACTIVE SLIDER PATTERN: Calculate → Guidance
class InteractiveCalculator {
    constructor() {
        this.debounceTimer = null;
        this.lastCalculateResults = null;  // Store for passing to Guidance
    }

    // Called on EVERY slider movement (instant)
    async onSliderMove(value) {
        // STEP 1: Call Calculate API immediately (<20ms)
        const calculateRequest = {
            tier: this.tier,
            user_profile: { 
                current_age: this.age, 
                income: this.income,
                monthly_contribution: value  // Slider value
            },
            portfolio: this.portfolio
        };

        const response = await fetch('/api/v1/calculate/simulate', {
            method: 'POST',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify(calculateRequest)
        });
        
        this.lastCalculateResults = await response.json();

        // STEP 2: Update charts IMMEDIATELY
        this.updateCharts(this.lastCalculateResults);
        this.updateSuccessProbability(this.lastCalculateResults.results.success_probability);

        // STEP 3: Debounce the Guidance API call (300ms)
        clearTimeout(this.debounceTimer);
        this.debounceTimer = setTimeout(() => this.onSliderCommit(), 300);
    }

    // Called when user STOPS moving (debounced)
    async onSliderCommit() {
        // STEP 4: Call Guidance API — entry_point_id required, calc_results OPTIONAL
        const guidanceRequest = {
            entry_point_id: this.entryPointId,  // from URL param or UTM resolution
            user_profile: this.userProfile,
            portfolio: this.portfolio,
            calculation_results: this.lastCalculateResults  // OPTIONAL — enables $ insights
        };

        const response = await fetch('/api/v1/guidance/context', {
            method: 'POST',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify(guidanceRequest)
        });
        
        const guidance = await response.json();

        // STEP 5: Update text/insights
        this.updateHeadline(guidance.headline);  // "You're on track — Evergreen adds $350K"
        this.updateDifferentiators(guidance.journey_context.differentiators);
    }
}

// The result:
// • User drags slider → Charts animate smoothly (Calculate API)
// • User stops → Text updates with insights (Guidance API)