T
TOMO
Developer Docs
BETA These docs are under partner review. Some features described are roadmap items, not yet shipped. Verify against your sandbox before relying on any contract.
● DRAFTv1.0.0finance.invest_in_mutual_fund

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_consultation if 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_padded
  • artificial_urgency_text ("Cutoff in 30 min!" without actual exchange cutoff)
  • ai_generated_photo for fund-manager / AMC imagery
  • past_returns_as_ranking_signal (past returns may be DISPLAYED, never used to rank — SEBI rule)
  • editor_recommended, tomo_pick, top_performer, 5_star_rated
  • regular_plan_when_direct_available (TOMO direct-only)
  • commission_padded_expense_ratio
  • forecasted_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.