Intent Spec — finance.invest_in_mutual_fund
FULL ID: finance.invest_in_mutual_fund
VERSION: v1.0.0
STATUS: draft
LAST UPDATED: 2026-05-13
DOMAIN: finance
PRIMARY AGENT: FinanceAgent
TTBS WEIGHTS: time=0.10 taste=0.15 budget=0.20 safety=0.55
User invests in SEBI-registered mutual fund scheme(s) via SIP or lumpsum. Covers scheme discovery, KYC + nomination, BSE StAR MF / NSE NMF II order placement, NAV-based unit allocation, recurring SIP setup, redemption, switch, and SWP. Direct plan only at TOMO ingest (no commission-bias on regular plans). Distinct from PMS / AIF / ETF cash-segment which route to v1.1+.
Partner exemplars: Zerodha Coin, Groww, Kuvera, Paytm Money, ETMoney, INDmoney, MFCentral, Niyo, Scripbox, AssetPlus.
SECTION 1 — INTENT IDENTITY
User wants to invest in a SEBI-registered mutual fund scheme. Distinct from:
- ETFs traded on cash segment — v1.1+
finance.buy_etf - PMS / AIF — out of scope v1; UHNI-only
- Direct stocks — v1.1+
finance.buy_stock - Sovereign Gold Bond / Government Securities — v1.1+
- Crypto / digital assets — not on TOMO under current regulatory clarity
- NPS / EPF / PPF — separate intents (v1.1+)
- Tax-saving ELSS — same intent; user prefers
category=ELSS
Single intent per investment action (one new SIP setup, one lumpsum order, one switch, one redemption, etc.). Multi-scheme baskets fan out one intent per scheme.
SECTION 2 — NATURAL LANGUAGE COVERAGE
CLASSIFIES IN
- "Start a SIP in Parag Parikh Flexi Cap"
- "Invest ₹50,000 lumpsum in HDFC Top 100"
- "Start ₹10k monthly SIP in Nifty 50 index fund"
- "ELSS investment under 80C"
- "Switch my Axis Bluechip to ICICI Pru Bluechip"
- "Redeem 50% of my Mirae Asset Large Cap"
- "Top up my existing SIP by ₹2,000"
- "Start SWP from my debt fund"
- "Best mid-cap direct plan SIP"
- "Liquid fund for parking ₹2L"
CLASSIFIES OUT — BORDERLINE NO
- "Buy stocks of TCS" → v1.1+
finance.buy_stock - "Buy NPS Tier 1" → v1.1+
- "What's my portfolio worth?" — non-transactional research
- "What are mutual funds?" — educational; surfaces in research
- "Tax-saving options" → may route to
finance.book_tax_consultationif user wants advice - "Crypto investment" — not supported v1
- "PPF account" → v1.1+
finance.open_ppf
MULTI-INTENT TRIGGERS
- "ELSS + insurance + tax consultation" →
finance.invest_in_mutual_fund+finance.book_tax_consultation - "Start SIP + open demat" → v1.1+ composite if demat not present
- "Goal-based plan" →
finance.book_financial_advisor_session+finance.invest_in_mutual_fund
SECTION 3 — INPUT (TOMO → PROVIDER)
{
"intent": "finance.invest_in_mutual_fund",
"request_id": "req_01J9Z...",
"user_locale": "en-IN",
"user_currency": "INR",
"user_location": { "lat": 17.4475, "lng": 78.3563, "city": "Hyderabad", "pincode": "500032" },
"action_type": "start_sip", // STRICT ENUM §6
"investor": {
"full_name": "Rakesh Kumar",
"date_of_birth": "1990-04-18",
"pan_last4": "5678",
"ckyc_id": "K-12345678", // CKYC reference if available
"residency_status": "resident_indian", // STRICT ENUM §6
"tax_status": "individual", // STRICT ENUM §6
"occupation": "salaried_corporate", // STRICT ENUM §6
"annual_income_band": "10L_to_25L", // STRICT ENUM §6
"is_politically_exposed": false,
"fatca_required": false,
"bank_account_ifsc": "HDFC0001234",
"bank_account_last4": "9876"
},
"investment": {
"scheme_filter": {
"category": "flexi_cap", // STRICT ENUM §6
"plan_type": "direct", // direct only — see §13
"option": "growth", // STRICT ENUM §6
"amc": null, // optional AMC filter
"min_aum_inr_crore": 1000, // floor on scheme size
"max_exit_load_pct": 1.0,
"max_expense_ratio_pct": 1.5
},
"sip_setup": {
"amount_inr": 10000,
"frequency": "monthly", // STRICT ENUM §6
"day_of_month": 5,
"duration_months": 120, // 0 = perpetual
"step_up_pct_annual": 10,
"first_installment_via_lumpsum": false
},
"lumpsum_setup": null, // mutually exclusive with sip_setup
"switch_setup": null,
"redemption_setup": null,
"swp_setup": null
},
"nominee": {
"full_name": "Anita Kumar",
"relationship": "spouse", // STRICT ENUM §6
"date_of_birth": "1992-09-12",
"share_pct": 100
},
"ttbs_user_band": {
"time": "flexible",
"taste": "balanced",
"budget": "good",
"safety": "great"
},
"session_context": { "tomo_session_id": "ses_01J9Z...", "user_dna_hash": "dna_v3_a7c9..." }
}
| Field | Type | Constraint | Notes |
|---|---|---|---|
intent |
string | REQUIRED, STRICT ENUM | Always finance.invest_in_mutual_fund |
action_type |
enum | REQUIRED, STRICT ENUM §6 | Drives which sub-block must be non-null |
investor.pan_last4 |
string | REQUIRED, 4 digits | Full PAN collected at KYC, not in input |
investor.residency_status |
enum | REQUIRED, STRICT ENUM §6 | NRI flow differs (FATCA, NRO/NRE) |
investor.is_politically_exposed |
bool | REQUIRED | PEP triggers enhanced KYC |
investment.scheme_filter.plan_type |
enum | REQUIRED, value MUST be "direct" | TOMO does not route to regular plans v1 |
investment.scheme_filter.option |
enum | REQUIRED, STRICT ENUM §6 | growth / IDCW |
investment.sip_setup |
object | REQUIRED nullable | Non-null when action_type=start_sip / modify_sip |
investment.lumpsum_setup |
object | REQUIRED nullable | Non-null when action_type=lumpsum |
investment.switch_setup |
object | REQUIRED nullable | Non-null when action_type=switch |
investment.redemption_setup |
object | REQUIRED nullable | Non-null when action_type=redeem |
investment.swp_setup |
object | REQUIRED nullable | Non-null when action_type=start_swp |
nominee.share_pct |
int | REQUIRED, 1-100 | Sum across nominees = 100 |
Anti-fabrication preamble: Provider must surface direct-plan schemes only. Past returns are NOT a TTBS signal at TOMO — only structural metrics (expense ratio, AUM, vintage, regulatory standing). SEBI-mandated disclosures (risk-o-meter, scheme info doc) must be linked in every response. Partner cannot promote schemes via this pipeline.
SECTION 4 — PROVIDER TOOLS
Tool 1: search_schemes
PURPOSE: Up to 20 schemes matching scheme_filter (direct plans only)
SLA: p50 ≤ 500ms, p95 ≤ 1500ms, p99 ≤ 3500ms
RATE LIMIT: 120 req/min (high — research-heavy)
IDEMPOTENCY: request_id; 10min cache (NAV updates daily)
RETRY: 1 on 429, 2 on 5xx
Tool 2: complete_kyc_and_nominee
PURPOSE: CKYC pull / fresh KYC + nominee setup + bank mandate
SLA: p50 ≤ 1800ms, p95 ≤ 5000ms
RATE LIMIT: 30 req/min
IDEMPOTENCY: request_id
RETRY: No retry
Tool 3: place_order
PURPOSE: Place SIP/lumpsum/switch/redeem/SWP order via BSE StAR MF / NSE NMF II
SLA: p50 ≤ 2500ms, p95 ≤ 6000ms, p99 ≤ 12000ms
RATE LIMIT: 60 req/min
IDEMPOTENCY: request_id (critical — never double-execute)
RETRY: No retry on order placement
Tool 4: setup_mandate
PURPOSE: eNACH / UPI Autopay mandate for recurring SIP
SLA: p50 ≤ 2000ms, p95 ≤ 5000ms
RATE LIMIT: 30 req/min
IDEMPOTENCY: request_id
RETRY: No retry
Tool 5: cancel_or_pause
PURPOSE: Cancel pending order, pause SIP, or cancel mandate
SLA: p50 ≤ 1500ms, p95 ≤ 4000ms
RATE LIMIT: 45 req/min
IDEMPOTENCY: order_id / sip_id
RETRY: 1 on 5xx
SECTION 5 — RESPONSE SHAPE
Scheme
Scheme:
scheme_id: { type: string, constraint: REQUIRED }
isin: { type: string, constraint: REQUIRED, semantics: "ISIN of the specific direct-growth plan" }
scheme_name: { type: string, constraint: REQUIRED }
scheme_code: { type: string, constraint: REQUIRED, semantics: "AMFI scheme code" }
amc:
amc_id: { type: string, constraint: REQUIRED }
name: { type: string, constraint: REQUIRED }
amfi_member_number: { type: string, constraint: REQUIRED }
aum_inr_crore: { type: int, constraint: REQUIRED, ≥0 }
category:
type: enum
constraint: REQUIRED, STRICT ENUM §6, semantics: "SEBI category classification"
sub_category: { type: string, constraint: REQUIRED, semantics: "AMC's marketed sub-cat e.g. 'flexi-cap'" }
plan_type: { type: enum, constraint: REQUIRED, STRICT ENUM §6 }
option: { type: enum, constraint: REQUIRED, STRICT ENUM §6 }
nav:
nav_inr: { type: float, constraint: REQUIRED, ≥0 }
nav_date: { type: string, constraint: REQUIRED, ISO_DATE }
scheme_aum_inr_crore: { type: int, constraint: REQUIRED, ≥0 }
expense_ratio_pct: { type: float, constraint: REQUIRED, 0-3 }
exit_load:
pct: { type: float, constraint: REQUIRED, 0-5 }
period_days: { type: int, constraint: REQUIRED, ≥0 }
description: { type: string, constraint: REQUIRED }
min_investment:
sip_inr: { type: int, constraint: REQUIRED, INR_INTEGER, ≥100 }
lumpsum_inr: { type: int, constraint: REQUIRED, INR_INTEGER, ≥500 }
sip_increment_inr: { type: int, constraint: REQUIRED, INR_INTEGER, ≥1 }
lumpsum_increment_inr: { type: int, constraint: REQUIRED, INR_INTEGER, ≥1 }
inception_date: { type: string, constraint: REQUIRED, ISO_DATE }
scheme_vintage_years: { type: float, constraint: REQUIRED, ≥0 }
benchmark_index: { type: string, constraint: REQUIRED, semantics: "Scheme's stated benchmark e.g. 'NIFTY 500 TRI'" }
fund_manager:
name: { type: string, constraint: REQUIRED }
tenure_years_at_amc: { type: float, constraint: REQUIRED, ≥0 }
tenure_years_on_scheme: { type: float, constraint: REQUIRED, ≥0 }
risk_o_meter:
type: enum
constraint: REQUIRED, STRICT ENUM §6
values: [low, low_to_moderate, moderate, moderately_high, high, very_high]
riskometer_image_url: { type: string, constraint: REQUIRED, HTTPS URL }
scheme_information_document_url: { type: string, constraint: REQUIRED, HTTPS URL, semantics: "Latest SID PDF from AMC" }
key_information_memorandum_url: { type: string, constraint: REQUIRED, HTTPS URL }
factsheet_url: { type: string, constraint: REQUIRED, HTTPS URL }
tax_class:
type: enum
constraint: REQUIRED, STRICT ENUM §6
values: [equity_oriented, debt_oriented, hybrid_equity_oriented, hybrid_debt_oriented, gold]
is_elss: { type: boolean, constraint: REQUIRED }
elss_lock_in_years: { type: int, constraint: REQUIRED, 0/3 }
cutoff_time_local: { type: string, constraint: REQUIRED, semantics: "Local IST cutoff for same-day NAV" }
partner_reference:
source: { type: string, constraint: REQUIRED }
deeplink: { type: string, constraint: REQUIRED, HTTPS URL }
KYCResult
KYCResult:
kyc_id: { type: string, constraint: REQUIRED }
request_id: { type: string, constraint: REQUIRED }
kyc_status:
type: enum
constraint: REQUIRED, STRICT ENUM §6
values: [verified_ckyc, verified_aadhaar, verified_pan_only, fatca_required, pep_enhanced_due_diligence, pending_manual, failed]
ckyc_id: { type: string, constraint: REQUIRED nullable }
fatca_declaration_required: { type: boolean, constraint: REQUIRED }
signature_validation_required: { type: boolean, constraint: REQUIRED }
documents_pending: { type: array<string>, constraint: REQUIRED, may be empty }
bank_mandate_status:
type: enum
constraint: REQUIRED, STRICT ENUM §6
values: [active, pending, none_required, failed]
expires_at: { type: string, constraint: REQUIRED, ISO_DATETIME }
MandateResult
MandateResult:
mandate_id: { type: string, constraint: REQUIRED }
scheme_id: { type: string, constraint: REQUIRED nullable }
type: { type: enum, constraint: REQUIRED, STRICT ENUM §6, values: [enach, upi_autopay] }
status: { type: enum, constraint: REQUIRED, STRICT ENUM §6, values: [active, pending, rejected, expired, cancelled] }
max_amount_inr: { type: int, constraint: REQUIRED, INR_INTEGER, ≥0 }
frequency: { type: enum, constraint: REQUIRED, STRICT ENUM §6 }
start_date: { type: string, constraint: REQUIRED, ISO_DATE }
end_date: { type: string, constraint: REQUIRED, ISO_DATE }
bank_ifsc: { type: string, constraint: REQUIRED }
OrderResult
OrderResult:
order_id: { type: string, constraint: REQUIRED, immutable }
exchange_order_id: { type: string, constraint: REQUIRED, semantics: "BSE / NSE confirmation ID" }
scheme_id: { type: string, constraint: REQUIRED }
isin: { type: string, constraint: REQUIRED }
action_type: { type: enum, constraint: REQUIRED, STRICT ENUM §6 }
status:
type: enum
constraint: REQUIRED, STRICT ENUM §6
values: [accepted, allotted, partially_allotted, rejected_amfi, rejected_amc, rejected_payment, cancelled]
placed_at: { type: string, constraint: REQUIRED, ISO_DATETIME }
nav_applicable_date: { type: string, constraint: REQUIRED, ISO_DATE }
allotment_nav_inr: { type: float, constraint: REQUIRED nullable, ≥0, semantics: "null until allotted" }
units_allotted: { type: float, constraint: REQUIRED nullable, ≥0 }
amount_invested_inr: { type: int, constraint: REQUIRED, INR_INTEGER, ≥0 }
stamp_duty_inr: { type: int, constraint: REQUIRED, INR_INTEGER, ≥0 }
net_units_after_stamp_duty: { type: float, constraint: REQUIRED nullable, ≥0 }
rejection_reason_code:
type: enum
constraint: REQUIRED nullable, STRICT ENUM §6
values: [bank_failure, mandate_inactive, amc_blocked, isin_suspended, kyc_invalid, cutoff_missed, scheme_closed, none]
contract_note_url: { type: string, constraint: REQUIRED nullable, HTTPS URL }
SIPSchedule
SIPSchedule:
sip_id: { type: string, constraint: REQUIRED }
scheme_id: { type: string, constraint: REQUIRED }
amount_inr: { type: int, constraint: REQUIRED, INR_INTEGER, ≥0 }
frequency: { type: enum, constraint: REQUIRED, STRICT ENUM §6 }
day_of_month: { type: int, constraint: REQUIRED, 1-28 }
duration_months: { type: int, constraint: REQUIRED, ≥0, semantics: "0 = perpetual" }
step_up_pct_annual: { type: int, constraint: REQUIRED, 0-50 }
status: { type: enum, constraint: REQUIRED, STRICT ENUM §6, values: [active, paused, cancelled, completed] }
start_date: { type: string, constraint: REQUIRED, ISO_DATE }
next_installment_date: { type: string, constraint: REQUIRED, ISO_DATE }
mandate_id: { type: string, constraint: REQUIRED }
CancellationResult
CancellationResult:
reference_id: { type: string, constraint: REQUIRED, semantics: "order_id, sip_id, or mandate_id" }
reference_type: { type: enum, constraint: REQUIRED, STRICT ENUM §6, values: [order, sip, mandate] }
cancelled_at: { type: string, constraint: REQUIRED, ISO_DATETIME }
status: { type: enum, constraint: REQUIRED, STRICT ENUM §6, values: [cancelled, pause_only, cancellation_failed] }
refund_amount_inr: { type: int, constraint: REQUIRED, INR_INTEGER, ≥0 }
refund_eta_days: { type: int, constraint: REQUIRED, 0-7 }
FORBIDDEN FIELDS
paid_placement_score,ad_bid,sponsored_rank,kickback_amount,commission_paddedartificial_urgency_text("Cutoff in 30 min!" without actual exchange cutoff)ai_generated_photofor fund-manager / AMC imagerypast_returns_as_ranking_signal(past returns may be DISPLAYED, never used to rank — SEBI rule)editor_recommended,tomo_pick,top_performer,5_star_ratedregular_plan_when_direct_available(TOMO direct-only)commission_padded_expense_ratioforecasted_returns,cagr_promise,guaranteed_return
SECTION 6 — CONTROLLED VOCABULARIES
action_type:
values:
start_sip: "New SIP setup"
modify_sip: "Change amount / frequency / step-up"
pause_sip: "Pause active SIP"
cancel_sip: "Cancel SIP permanently"
lumpsum: "One-time investment"
switch: "Move units between schemes (taxable event)"
redeem: "Sell units"
start_swp: "Set up systematic withdrawal"
cancel_swp: "Stop SWP"
investor.residency_status:
values: [resident_indian, nri_nre, nri_nro, oci, pio]
investor.tax_status:
values: [individual, huf, sole_proprietor, partnership_firm, llp, private_ltd, public_ltd, trust, society, ngo]
investor.occupation:
values:
salaried_corporate, salaried_government, self_employed_professional,
self_employed_business, homemaker, student, retired
investor.annual_income_band:
values:
upto_2L, 2L_to_5L, 5L_to_10L, 10L_to_25L,
25L_to_50L, 50L_to_1Cr, above_1Cr
scheme_filter.category:
values:
large_cap, mid_cap, small_cap, large_and_mid_cap, multi_cap, flexi_cap,
elss, focused, value, contra, dividend_yield, sectoral_thematic,
aggressive_hybrid, balanced_hybrid, conservative_hybrid, multi_asset,
arbitrage, equity_savings, dynamic_asset_allocation,
overnight, liquid, ultra_short, low_duration, money_market, short_duration,
medium_duration, medium_to_long, long_duration, dynamic_bond, gilt, credit_risk,
banking_and_psu, corporate_bond, fmp,
gold_etf_fof, international, fund_of_funds_domestic, index, etf_fof,
retirement, children, solution_oriented
scheme_filter.plan_type:
values:
direct: "TOMO routes only direct plans"
regular: "REJECTED at TOMO ingest in v1"
scheme_filter.option / option:
values: [growth, idcw_payout, idcw_reinvestment]
risk_o_meter:
values: [low, low_to_moderate, moderate, moderately_high, high, very_high]
tax_class:
values: [equity_oriented, debt_oriented, hybrid_equity_oriented, hybrid_debt_oriented, gold]
frequency:
values: [daily, weekly, fortnightly, monthly, quarterly, semi_annual, annual]
nominee.relationship:
values: [spouse, son, daughter, father, mother, brother, sister, grandparent, grandchild, legal_guardian, charitable_trust]
kyc_status:
values: [verified_ckyc, verified_aadhaar, verified_pan_only, fatca_required, pep_enhanced_due_diligence, pending_manual, failed]
bank_mandate_status: [active, pending, none_required, failed]
MandateResult.type: [enach, upi_autopay]
order status: [accepted, allotted, partially_allotted, rejected_amfi, rejected_amc, rejected_payment, cancelled]
rejection_reason_code: [bank_failure, mandate_inactive, amc_blocked, isin_suspended, kyc_invalid, cutoff_missed, scheme_closed, none]
SIPSchedule.status: [active, paused, cancelled, completed]
CancellationResult.reference_type: [order, sip, mandate]
CancellationResult.status: [cancelled, pause_only, cancellation_failed]
SECTION 7 — TTBS DIMENSIONS
TIME (weight = 0.10):
signals_used:
- cutoff_time_local (today's NAV vs next-day's)
- estimated NAV applicable date based on placement time
weighting:
same_day_nav_possible: 0.70
cutoff_buffer: 0.30
user_band_handling:
fast: prioritize same-day NAV (place before cutoff)
TASTE (weight = 0.15):
signals_used:
- amc matches preferred AMC in DNA
- category matches user's stated preference
- scheme matches user's existing portfolio (rebalance fit)
weighting:
amc_dna: 0.40
category_match: 0.40
portfolio_fit: 0.20
BUDGET (weight = 0.20):
signals_used:
- expense_ratio_pct (lower preferred)
- exit_load.pct (lower preferred)
- min_investment.sip_inr matches user's amount
weighting:
expense_ratio: 0.65
exit_load: 0.20
min_invest_fit: 0.15
user_band_handling:
ok: lowest expense passing safety
good: balanced
great: expense-insensitive; safety + manager tenure dominate
SAFETY (weight = 0.55):
signals_used:
- amc.amfi_member_number (active SEBI-registered AMC)
- scheme_aum_inr_crore (≥500cr meaningful; ≥2000cr robust)
- scheme_vintage_years (≥5y preferred for non-NFO)
- fund_manager.tenure_years_on_scheme (≥3y preferred)
- risk_o_meter alignment with user's risk profile
- scheme_information_document_url present and recent
- category falls under SEBI's standard 36 categories (no off-category labelling)
weighting:
amc_standing: 0.20
aum: 0.20
vintage: 0.15
manager_tenure: 0.15
risk_alignment: 0.15
documents: 0.10
category_compliance: 0.05
user_band_handling:
fast: relax AUM ≥250cr
balanced: AUM ≥500cr, vintage ≥3y
great: AUM ≥2000cr AND vintage ≥5y AND manager_tenure ≥3y
Locked weights per finance.invest_in_mutual_fund: time 0.10 / taste 0.15 / budget 0.20 / safety 0.55. Safety-heavy because mutual funds carry capital-loss risk that compounds; structural quality outranks immediate cost.
Past returns are NOT a TTBS signal. SEBI prohibits past-return-based recommendation. Past returns may be DISPLAYED in the widget for informational purposes only.
SECTION 8 — COMPLETION CONTRACT
POST /api/v1/cpc/mcp_provider/<your_partner_id>
Body:
{
"intent": "finance.invest_in_mutual_fund",
"external_id": "<order_id or sip_id>",
"request_id": "<request_id>",
"amount_inr": 0, // Direct plans: zero distributor commission. TOMO charges flat unit-economics or subscription separately, NOT a % of investment.
"gst_inr": 0,
"tips_inr": 0,
"pass_through_inr": 10000, // first installment amount that went to AMC
"closed_at": "2026-05-13T11:20:00+05:30",
"status": "completed",
"action_type": "start_sip",
"amc_id": "ppfas",
"scheme_isin": "INF879O01100",
"sip_amount_inr": 10000,
"sip_frequency": "monthly"
}
Important: Direct mutual funds carry ZERO distributor commission by SEBI design. This is the entire point of "direct plan." Partners who route direct-plan orders earn revenue via flat subscriptions, exchange platform fees from BSE/NSE, or value-added services — NEVER from AMC-paid trail. amount_inr here is therefore zero or a flat-fee component the partner separately discloses to the user.
If partner uses a flat-subscription model with the user, TOMO does NOT take a percentage of that subscription. TOMO's economics on MF investment is unit-economics on user-side acquisition, NOT a % of investment flow.
HMAC-SHA256, 5-min replay.
SECTION 9 — WIDGET
MutualFundWidget (planned). Interim: ListingsWidget.
Field mapping:
- Scheme.scheme_name + amc.name → header
- sub_category → eyebrow ("Flexi Cap · Equity")
- risk_o_meter → coloured risk pill
- expense_ratio_pct → "X% expense"
- scheme_aum_inr_crore → "₹X cr AUM"
- scheme_vintage_years → "Xy old" pill
- fund_manager.name + tenure_years_on_scheme → manager line
- exit_load → "Exit X% / Y days" pill
- min_investment.sip_inr / lumpsum_inr → min line
- scheme_information_document_url → "SID" link
- factsheet_url → "Factsheet" link
- cutoff_time_local → cutoff reminder when within 1h
Widget MUST also show SEBI-mandated risk disclaimer below the listing.
SECTION 10 — CACHING POLICY
| Call | TTL | Rationale |
|---|---|---|
| search_schemes (NAV-bearing) | 10min | NAV updates daily after AMC publishes |
| search_schemes (static metadata) | 24h | Category, vintage, SID rarely change |
| complete_kyc_and_nominee | NO CACHE | Idempotent |
| place_order | NO CACHE | Critical idempotency |
| setup_mandate | NO CACHE | — |
| cancel_or_pause | NO CACHE | — |
| AMC static (member number, AUM) | 24h | AMFI publishes |
| SID / KIM / Factsheet URLs | 7d | Versioned monthly |
| Risk-o-meter image | 24h | Re-published monthly |
SECTION 11 — ERROR CODES
| Code | HTTP | Meaning | Retry |
|---|---|---|---|
INVALID_REQUEST |
400 | Malformed | No |
RATE_LIMITED |
429 | Throttle | 1, 2s |
INTERNAL_ERROR |
500 | Partner failure | 2, exp |
SIGNATURE_INVALID |
401 | HMAC fail | No |
KYC_REQUIRED |
422 | KYC not complete | No (KYC tool first) |
KYC_FAILED |
422 | KYC mismatch | No |
PAN_INVALID |
422 | PAN format / not found | No |
BANK_MANDATE_REQUIRED |
422 | SIP without active mandate | No (mandate tool first) |
MANDATE_INACTIVE |
422 | Mandate expired / cancelled | No |
BELOW_MIN_INVESTMENT |
422 | Amount below scheme minimum | No |
ISIN_SUSPENDED |
422 | Scheme subscription paused by AMC | No |
CUTOFF_MISSED |
422 | Past today's cutoff; next-day NAV | n/a (advisory) |
MANDATE_LIMIT_EXCEEDED |
422 | Order > mandate max_amount | No |
INSUFFICIENT_BALANCE |
402 | Bank rejection | No |
NRI_NOT_PERMITTED |
422 | Scheme excludes NRI | No |
FATCA_PENDING |
422 | FATCA declaration required | No |
ELSS_LOCKIN_ACTIVE |
422 | Redemption attempt within 3y lock | No |
EXIT_LOAD_DISCLOSURE_REQUIRED |
200 (advisory) | UI must show exit load before redeem | n/a |
PARTIAL_ALLOTMENT |
200 | AMC allotted partial units | n/a |
PROVIDER_DOWN |
503 | Exchange platform unavailable | 1 retry, 2s |
SECTION 12 — SANDBOX → PRODUCTION CHECKLIST
[ ] All five tools implemented
[ ] BSE StAR MF or NSE NMF II membership verifiable
[ ] AMFI registration number current
[ ] SEBI-registered investment adviser / RIA license (if offering advice) OR SEBI-registered distributor flag
[ ] At least 10 AMCs in catalog (covering top-AUM AMCs)
[ ] All schemes returned have direct-plan ISINs
[ ] Regular-plan ISINs rejected at TOMO ingest (test case)
[ ] expense_ratio_pct, exit_load, NAV match AMFI daily NAV file
[ ] scheme_aum_inr_crore matches AMC's monthly disclosure
[ ] fund_manager.tenure_years_on_scheme verifiable from AMC site
[ ] risk_o_meter matches AMC's latest published risk-o-meter
[ ] scheme_information_document_url returns CURRENT month's SID
[ ] factsheet_url current month
[ ] No past-return-based ranking signal in scoring
[ ] Cutoff times correctly reflect AMC's published cutoff
[ ] All controlled vocabularies respected
[ ] HMAC signing verified
[ ] amount_inr in CPC is ZERO or flat-fee disclosed (NO % of investment)
[ ] pass_through_inr captures actual amount routed to AMC
[ ] No forbidden fields anywhere
[ ] No "Top Pick" / "5-Star" / "Recommended" badges
[ ] SLA p95 met
[ ] Compliance: GSTIN, SEBI / AMFI license, privacy policy, grievance officer, investor grievance email
[ ] Customer support: SEBI-compliant grievance redressal
[ ] FATCA / CKYC integration tested
SECTION 13 — ANTI-FABRICATION RULES
RULE 1: No paid_placement / ad / kickback fields. Partners earning AMC
trail by routing regular plans = NOT TOMO partners. Direct-only.
RULE 2: scheme_filter.plan_type = "regular" is REJECTED at TOMO ingest.
Partners returning regular-plan ISINs in response = suspension.
RULE 3: expense_ratio_pct must match AMFI daily NAV file. Misstating to
win on Budget axis = customer-harm + suspension.
RULE 4: scheme_aum_inr_crore must match AMC's monthly disclosure.
RULE 5: NAV must match AMFI daily file for the nav_date.
RULE 6: Past returns are NOT a TTBS signal. Partners cannot pass
"past_return_3y", "cagr_5y", etc. as ranking hints. SEBI prohibits
recommendation based on past returns.
RULE 7: forecasted_returns / cagr_promise / guaranteed_return forbidden
anywhere in payload. Mutual funds carry market risk; promises
violate SEBI Mutual Fund Regulations.
RULE 8: risk_o_meter must match AMC's latest monthly published value
(not a historical or marketing-skewed version).
RULE 9: SID / KIM / factsheet URLs must be the CURRENT version (current
month's SID; latest factsheet). Stale documents = customer-harm.
RULE 10: artificial_urgency_text forbidden ("Last chance for 80C!"
beyond actual financial year cutoff).
RULE 11: No AI-generated AMC / fund-manager imagery.
RULE 12: amount_inr in CPC must reflect actual partner revenue. For
direct plans, this is zero or a flat fee — never a % of
investment. False positives = suspension.
RULE 13: No "Top Pick" / "5-Star" / "Recommended" / "Best Fund" badges.
SEBI prohibits fund recommendation by distributors / aggregators
without RIA license; even with RIA, advice must follow regulated
risk-profiling — not TTBS-passenger ranking.
RULE 14: information_completeness_score (hidden ranking factor, weight
0.10) rewards full §5 shape.
RULE 15: NRI / FATCA / PEP handling must follow SEBI / PMLA rules.
Enhanced due diligence for PEPs is non-negotiable.
RULE 16: For ELSS schemes, lock-in must be enforced server-side. Redemption
attempts within 3y of allotment must return ELSS_LOCKIN_ACTIVE.
RULE 17: Switch transactions are taxable events (units sold + units bought
on the same day). UI must surface tax implication before
confirmation. Hiding tax implication = customer-harm.
RULE 18: Mandate max_amount must equal or exceed the largest possible SIP
installment including step-ups across the duration. Misconfigured
mandate = SIP failure = customer-harm.
RULE 19: For all NFO / sectoral / thematic categories, partners MUST surface
category-specific risk warning beyond standard risk-o-meter.
VERSION HISTORY
v1.0.0 — 2026-05-13 — Initial spec. Direct plans only. SEBI / AMFI
category vocabulary. BSE StAR / NSE NMF II order
paths. eNACH + UPI Autopay for SIP mandates. Past
returns explicitly excluded from TTBS. Safety-
dominant (weight 0.55) reflecting capital-loss
compounding risk.