Monitor every client's plan, nightly
An advisor platform's recurring job: rerun every client's core numbers and
surface only the plans that moved. /v1/batch makes that one
call per client — up to 20 calculations, dispatched in order, one result each — and
determinism makes the "what moved?" diff nearly free.
1 · One client, one call
A 62-year-old couple, $1.2M saved, retiring at 65 with Social Security at 67: the nightly row is readiness (a seeded 2,000-path Monte Carlo — cheap and fully reproducible), IRMAA headroom on this year's MAGI, and conversion room in the current bracket.
curl -X POST https://api.numeratica.com/v1/batch \
-H "Authorization: Bearer nmr_sk_test_xxx" \
-H "Content-Type: application/json" \
-d '{
"requests": [
{ "path": "/v1/retirement/monte-carlo",
"body": { "current_age": 62, "retirement_age": 65, "end_age": 95,
"current_balance": 1200000, "annual_contribution": 20000,
"annual_spending": 80000, "expected_return": 0.06,
"return_volatility": 0.11, "inflation": 0.025,
"num_simulations": 2000, "seed": 7,
"guaranteed_income": [{ "annual_amount": 42000, "start_age": 67 }] } },
{ "path": "/v1/medicare/irmaa",
"body": { "irmaa_year": 2026, "filing_status": "mfj", "magi": 190000 } },
{ "path": "/v1/tax/bracket-room",
"body": { "tax_year": 2026, "filing_status": "mfj", "taxable_income": 155000 } }
]
}'2 · The response
One entry per sub-request, order preserved, each exactly what the standalone endpoint returns — same handlers, same metering (one usage event per sub-request), same validation. A bad sub-request comes back as a 4xx inside the batch; the batch itself still returns 200.
{
"result_id": "batch_c4fada937e72e252", /* identical batches return an identical id */
"engine_version": "batch-1.0.0",
"result": {
"results": [
{ "status": 200, "body": {
"result_id": "mc_0af83c3608cf196c", "seed": 7,
"result": { "success_probability": 0.918,
"median_ending_balance": 2587227.05,
"median_depletion_age": 90 } } },
{ "status": 200, "body": {
"result_id": "irmaa_9d0ff229ae054efb",
"result": { "tier": 0, "part_b_total": 202.90,
"annual_surcharge": 0,
"headroom_to_next_tier": 28000 } } },
{ "status": 200, "body": {
"result_id": "bracket_f7f57d47efc36f93",
"result": { "marginal_rate": 0.22,
"ordinary_room": [ { "rate": 0.22, "room": 56400 } ],
"ltcg_fifteen_room": 458700 } } }
]
},
"disclaimer": "Hypothetical projection from a stochastic model; simulated results do not reflect actual investment performance and are not a guarantee of future results. Informational only; not financial advice."
}3 · The change-detection trick
Every sub-result's result_id is a content hash of its inputs
and engine version. So the nightly diff is: store yesterday's ids, compare today's.
Same ids ⇒ nothing moved — same client data, same tables, same engine —
skip the row entirely. An id changed ⇒ either the client's inputs changed or an engine/table
was updated; recompute the deltas and flag the row for a human. No field-by-field
comparison, no tolerance thresholds.
Operational notes, all deliberate: rate limiting is all-or-nothing (a
batch of N reserves N tokens up front — a 429 means the whole batch waited, never a
partial run), the batch shares one 25-second budget (a seeded 2,000-path Monte Carlo uses
a fraction of it), and the 20-sub-request cap means one call per client, chunked — 500
clients is 500 calls, which is what the per-key rate limit and
Retry-After are for. Sweep nightly at 2,000 simulations for
speed; run the full-depth Monte Carlo monthly or when a row flags.
4 · Render it
The dashboard row the sweep feeds — one line per client, only flagged rows expanded.
<div style="font-family:-apple-system,Segoe UI,Roboto,Helvetica,Arial,sans-serif;color:#1a2230;max-width:600px">
<div style="font-size:14px;color:#5b6675;margin-bottom:8px">Nightly plan sweep · 2026-07-09 · 1 of 20 clients shown</div>
<div style="border:1px solid #e6e9ef;border-radius:10px;padding:12px">
<div style="display:flex;justify-content:space-between;align-items:baseline;margin-bottom:8px">
<div style="font-weight:600">Client #1047 · retiring 2029</div>
<div style="font-size:12px;color:#16a34a;font-weight:600">✓ no change since yesterday</div>
</div>
<div style="display:grid;grid-template-columns:1fr 1fr 1fr;gap:10px;font-size:13px">
<div>
<div style="color:#5b6675;font-size:12px">Readiness</div>
<div style="font-size:20px;font-weight:700">91.8%</div>
<div style="color:#5b6675;font-size:12px">median end $2.59M</div>
</div>
<div>
<div style="color:#5b6675;font-size:12px">IRMAA headroom</div>
<div style="font-size:20px;font-weight:700">$28,000</div>
<div style="color:#5b6675;font-size:12px">tier 0 · no surcharge</div>
</div>
<div>
<div style="color:#5b6675;font-size:12px">22% bracket room</div>
<div style="font-size:20px;font-weight:700">$56,400</div>
<div style="color:#5b6675;font-size:12px">conversion window open</div>
</div>
</div>
</div>
<div style="color:#5b6675;font-size:12px;margin-top:6px">result_ids mc_0af83c3608cf196c · irmaa_9d0ff229ae054efb · bracket_f7f57d47efc36f93 · hypothetical projection, not a guarantee of future results · not financial advice</div>
</div>
The pieces of the row: the readiness check, the IRMAA planner, and Roth conversion when the bracket-room column says the window is open. Agents can drive the same sweep through the MCP server.