pay.add_money_to_wallet — Full Intent Specification
INTENT NAMESPACE: pay
INTENT NAME: add_money_to_wallet
FULL ID: pay.add_money_to_wallet
VERSION: v1.0.0
STATUS: live
TTBS WEIGHTS: time 0.35 · taste 0.10 · budget 0.35 · safety 0.20
LAST UPDATED: 2026-05-14
Load funds into a third-party wallet (FASTag, transit metro card, gaming/streaming app wallet, food-delivery wallet, RBI-licensed PPI like Paytm/PhonePe Wallet, etc.) from the user's bank account via UPI / netbanking / card. Distinct from pay.send_money_upi because: (a) destination is not a person but a regulated PPI/closed-loop wallet; (b) the wallet operator must hold an RBI PPI license (or be a sub-issuer of one); (c) certain wallets (FASTag, metro) have hardware/account-state side-effects (tag balance, gate-clearance state) — TOMO must reflect those; (d) max wallet balance under RBI PPI rules is regulated (₹2L full-KYC PPI, ₹10k small-PPI); (e) reload fees vary by partner and funding rail — TOMO must surface the all-in cost.
1. NATURAL LANGUAGE COVERAGE
Classifies IN
- "add ₹500 to my FASTag"
- "recharge FASTag HDFC ₹2000"
- "load ₹1000 metro card Hyderabad"
- "add money to Paytm wallet ₹500"
- "top up PhonePe wallet"
- "recharge Swiggy Money ₹300"
- "load BookMyShow wallet ₹1000"
- "add ₹2000 to Amazon Pay"
- "load my Ola Money"
- "add cash to ONDC wallet"
Classifies OUT — borderline NO
- "send money to Rohit" →
pay.send_money_upi - "pay electricity bill" →
pay.utility_bill_pay - "buy mobile recharge" →
pay.utility_bill_pay(telecom is treated as utility v1) - "add forex to my forex card" →
travel.book_forex - "deposit to savings account" → out of scope (banking, not wallet)
- "buy giftcard" → adjacent — out of v1 (gift card has its own state semantics)
MULTI-INTENT TRIGGERS
- "recharge FASTag and book outstation cab" →
pay.add_money_to_wallet+mobility.book_outstation_package - "top up metro card before tomorrow's commute" →
pay.add_money_to_wallet+mobility.book_recurring_commute - "load Swiggy Money and order biryani" →
pay.add_money_to_wallet+food.order_delivery
2. INPUT — TOMO → PROVIDER
{
"intent": "pay.add_money_to_wallet",
"intent_version": "v1.0.0",
"request_id": "req_wt_3m8x_2026-05-14T11:08:00Z",
"user_session_id": "anon_user_token_or_uid",
"wallet": {
"category": "fastag",
"operator": "HDFC FASTag",
"account_ref": "tag_hdfc_xxxx7421",
"kyc_band": "full"
},
"amount_inr": 2000,
"source_instrument": {
"type": "upi",
"vpa": "keerthi@hdfcbank",
"fallbacks": ["netbanking", "debit_card"]
},
"schedule": {
"mode": "now",
"modes_allowed": ["now", "auto_when_low"],
"auto_when_low_threshold_inr": 200,
"auto_topup_amount_inr": 1000
},
"user_constants": {
"vehicle_rc_optional": "TS09EZ1234",
"tag_class_optional": "VC4"
}
}
Field rules
wallet.categoryENUM strict. Each category maps to a known set of validoperatorvalues; mismatch →ERR_OPERATOR_CATEGORY_MISMATCH.amount_inrinteger (INR_INTEGER); decimals rejected.wallet.kyc_band∈ {full,small,unknown} drives RBI PPI ceiling:full≤ ₹2L balance;small≤ ₹10k balance;unknown→ block.auto_when_low_threshold_inrrequires UPI Autopay mandate (RBI's recurring framework); created out-of-band, referenced by mandate_id.
3. PROVIDER TOOLS
The wallet-load MCP server (pay-wallet-mcp) exposes exactly these tools. Each operator-side wallet is RBI-licensed PPI (or a closed-loop sub-issuer); TOMO is routing only.
wallet.resolve_account
Confirms account/tag exists and is active. Returns current balance + KYC band.
{
"tool": "wallet.resolve_account",
"input": {"category": "fastag", "operator": "HDFC FASTag", "account_ref": "tag_hdfc_xxxx7421"},
"output": {
"valid": true,
"vehicle_rc_match": "TS09EZ1234",
"current_balance_inr": 120,
"kyc_band": "full",
"tag_class": "VC4",
"tag_blacklist_flag": false,
"low_balance_flag": true
}
}
wallet.load
Initiates a load. Hands off to user's UPI app / netbanking page / card 3DS. Returns load_id.
wallet.status
Polls the load state.
wallet.set_autopay_mandate
Creates UPI Autopay mandate for auto-when-low. Mandate is between user's bank and wallet operator — TOMO is referenced as the initiator only.
wallet.cancel_autopay
Revokes the mandate.
wallet.refund
Refunds an unsuccessful or partial load (rare — only when destination credit failed but source debited).
4. RESPONSE SHAPE — PROVIDER → TOMO
{
"intent": "pay.add_money_to_wallet",
"request_id": "req_wt_3m8x_2026-05-14T11:08:00Z",
"wallet": {
"category": "fastag",
"operator": "HDFC FASTag",
"account_ref": "tag_hdfc_xxxx7421",
"current_balance_inr": 120,
"kyc_band": "full",
"vehicle_rc": "TS09EZ1234",
"low_balance_flag": true,
"tag_blacklist_flag": false
},
"options": [
{
"tier": "OK",
"amount_inr": 500,
"funding_rail": "upi",
"fee_inr": 0,
"credit_eta_seconds": 30,
"ppi_license_no": "PPI-MD-DPSS.CO.PD.No.512/02.14.006/2016-17",
"post_load_balance_estimate_inr": 620,
"tier_reason": "minimum top-up — fastest, free"
},
{
"tier": "GOOD",
"amount_inr": 2000,
"funding_rail": "upi",
"fee_inr": 0,
"credit_eta_seconds": 30,
"ppi_license_no": "PPI-MD-DPSS.CO.PD.No.512/02.14.006/2016-17",
"post_load_balance_estimate_inr": 2120,
"tier_reason": "balanced — covers ~10 toll crossings, free"
},
{
"tier": "GREAT",
"amount_inr": 2000,
"funding_rail": "upi",
"fee_inr": 0,
"credit_eta_seconds": 30,
"ppi_license_no": "PPI-MD-DPSS.CO.PD.No.512/02.14.006/2016-17",
"autopay_mandate_inr_per_trigger": 1000,
"autopay_trigger_balance_inr": 200,
"post_load_balance_estimate_inr": 2120,
"tier_reason": "auto-top-up at ₹200, never run out again"
}
],
"kyc_disclosure": {
"band": "full",
"ceiling_inr": 200000,
"current_balance_inr": 120,
"post_load_balance_inr": 2120,
"headroom_inr": 197880
}
}
Field rules
ppi_license_noMUST appear for every option (RBI license). Missing → drop option.credit_eta_secondsis the operator-attested post-debit credit time. Display as plain English ("usually under 30s").fee_inrALWAYS shown — never folded silently intoamount_inr.tag_blacklist_flag: true→ block load and surface remediation (NHAI dispute/RC sync) inline.
5. CONTROLLED VOCABULARIES
wallet.category
fastag · metro_card · ppi_general · food_app_wallet · mobility_app_wallet · entertainment_app_wallet · ecom_app_wallet · gaming_app_wallet
funding_rail
upi · netbanking · debit_card · credit_card
kyc_band
full · small · unknown
schedule.mode
now · auto_when_low
All STRICT ENUM. Anything else → ERR_VOCAB.
6. TTBS DIMENSIONS
TIME (weight 0.35)
credit_eta_seconds(smaller = better)- Funding rail latency — UPI ~ instant, netbanking ~ minutes, card 3DS variable
- Operator's published reliability SLA
- TIME score = 1 − min(1, credit_eta_seconds / 300)
TASTE (weight 0.10)
- Operator brand familiarity to user (from on-device usage history)
- App-side post-load confirmation clarity
- Friction at funding rail (single tap vs 3DS interruption)
- TASTE score = brand_familiar × rail_friction_inv
BUDGET (weight 0.35)
fee_inr(zero ideal)- Hidden cross-rail markup (e.g. credit-card surcharge on some operators)
- Auto-topup amount sized rationally vs use pattern
- BUDGET score = 1 − fee_pct − hidden_markup_pct
SAFETY (weight 0.20)
- Operator RBI PPI license cache age ≤ 14 days
- Tag/account blacklist state false
- KYC band compatible with new balance
- TOMO never holds funds — wallet operator + funding rail PSP are both regulated
- SAFETY score = ppi_license_valid × not_blacklisted × kyc_compatible × rail_psp_compliant
HARD FILTERS (apply before scoring)
- Operator must hold valid RBI PPI license (or be a verified sub-issuer of one).
- Account/tag must resolve as
valid. - Tag/account not blacklisted.
- Post-load balance must not breach KYC ceiling.
auto_when_lowmode requires existing or just-created Autopay mandate.
7. COMPLETION CONTRACT
Success criteria
wallet.resolve_accountreturnsvalid: true, balance + KYC band.- UPI/netbanking/card debit succeeds at user's bank.
- Wallet operator credits balance within
credit_eta_seconds. wallet.statusconfirmsstate: credited.- Push notification to user with old balance → new balance.
- CPC webhook fires.
CPC webhook (HMAC-SHA256, 5-min replay window)
{
"event": "pay.add_money_to_wallet.completed",
"intent_id": "pay.add_money_to_wallet",
"request_id": "req_wt_3m8x_2026-05-14T11:08:00Z",
"load_id": "WLD7K2X9",
"wallet": {
"category": "fastag",
"operator": "HDFC FASTag",
"account_ref": "tag_hdfc_xxxx7421"
},
"amount_inr": 2000,
"fee_inr": 0,
"pass_through_inr": 2000,
"tomo_commission_base_inr": 0,
"tomo_commission_inr": 0,
"funding_rail": "upi",
"credit_eta_actual_seconds": 22,
"ppi_license_no": "PPI-MD-DPSS.CO.PD.No.512/02.14.006/2016-17",
"completed_at_iso": "2026-05-14T11:08:34+05:30",
"signature_hmac_sha666": "REPLACE_BEFORE_PROD",
"signature_hmac_sha256": "…"
}
Note: most operators absorb the load themselves and pay TOMO a fixed per-load referral (₹2–₹10 depending on category), NOT a percent of amount_inr. The tomo_commission_base_inr is therefore the per-load fee where contracted, otherwise 0. Aligns with COMMISSION BASE = NET — TOMO never books the load amount itself as revenue.
Failure cases
debit_succeeded_credit_failed→ operator-side dispute auto-opened; user gets refund SLA banner (T+1 working day).tag_blacklisted→ block + remediation steps.kyc_ceiling_breached→ block + ask user to upgrade KYC or load smaller.mandate_creation_failed→ fall back to single-shotnow.
8. WIDGET — UOE → UI
{
"widget": "WalletLoadWidget",
"header": {
"title": "HDFC FASTag · TS09EZ1234",
"balance_strip": "Current ₹120 · low balance · ~1 toll crossing left",
"kyc_strip": "Full KYC · headroom ₹1,97,880 of ₹2,00,000"
},
"regions": {
"region_1_intelligence": ["tag active", "RC matches vehicle on file", "no blacklist flag", "PPI license verified 2d ago"],
"region_2_summary": "Add ₹X to your FASTag — UPI, free, ~30s credit",
"region_3_visual": "tag_artwork_url (operator-supplied, signed)",
"region_4_now_pin": "Pay ₹2,000 via UPI now",
"region_5_tomo_choices": [
{"tier": "OK", "label": "₹500 · 1-tap UPI · ~30s", "reason": "fastest minimum"},
{"tier": "GOOD", "label": "₹2,000 · 1-tap UPI · covers ~10 tolls", "reason": "balanced"},
{"tier": "GREAT", "label": "₹2,000 + auto top-up below ₹200", "reason": "set and forget"}
]
},
"footer_disclosures": [
"TOMO never holds your money. Funds go from your bank straight to the operator's RBI-licensed wallet.",
"UPI loads are free — fees only apply on credit-card top-ups (we'll show the number before you confirm).",
"You can cancel auto top-up anytime in your bank's UPI Autopay screen."
]
}
UI invariants:
- Current balance + operator license disclosed at top.
- Fee shown plainly (even if zero).
- Autopay mandate explicit consent screen before mandate creation.
9. CACHING POLICY
- Operator PPI license: cache 14 days at registry layer.
wallet.resolve_account: cache 60s on-device; re-resolve before any load.- Current balance: never stale beyond 60s on the load surface.
- Past loads (read-only ledger): cache 90 days on-device, encrypted.
- Autopay mandate state: subscribed via PSP webhook; no polling cache.
10. ERROR CODES
| Code | Meaning | UI surface |
|---|---|---|
ERR_OPERATOR_CATEGORY_MISMATCH |
operator not in category | "We don't recognize this combo" + valid list |
ERR_ACCOUNT_NOT_FOUND |
resolve_account valid: false |
"Tag/account not found — re-enter?" |
ERR_TAG_BLACKLISTED |
tag_blacklist_flag true | Plain explainer + remediation link |
ERR_KYC_CEILING_BREACH |
post-load > PPI ceiling | "Load would exceed RBI's ₹X ceiling — load smaller or upgrade KYC" |
ERR_FUNDING_RAIL_DOWN |
UPI/netbanking/card rail unavailable | Fallback rail prompt |
ERR_DEBIT_OK_CREDIT_FAIL |
source debited, dest not credited | Auto-dispute banner with refund SLA |
ERR_MANDATE_CREATION_FAILED |
UPI Autopay rejected by user's bank | Fall back to single-shot |
ERR_LICENSE_STALE |
operator PPI license cache > 14d | Drop option before showing user |
ERR_AMOUNT_BELOW_MIN |
amount < operator min | Show min + adjust |
ERR_AMOUNT_ABOVE_MAX_PER_TXN |
per-load cap exceeded | Show cap + adjust |
11. SANDBOX → PRODUCTION CHECKLIST
- Sandbox
wallet.resolve_accountreturns realistic balance + blacklist flag. - Sandbox UPI debit OK + credit OK happy path.
- Sandbox UPI debit OK + credit FAIL flow generates refund SLA.
- Sandbox KYC ceiling enforced at ₹2L (full) and ₹10k (small).
- Sandbox tag_blacklist_flag blocks load.
- Sandbox Autopay mandate creation tested end-to-end across at least 3 banks.
- Sandbox CPC webhook signed HMAC-SHA256 (fix any placeholder fields).
- Production operator PPI licenses pulled weekly from RBI master directory.
- Production rate-limit: max 10 loads per user per hour per operator.
- Production fraud watch — multi-account-same-card pattern flagged.
- Production refund SLA T+1 measured on real disputed loads.
- Production push notification on credit confirmation tested across iOS / Android.
12. ANTI-FABRICATION RULES
- NO
paid_placement/editor_pickon wallet operators — operator is chosen by user, not ranked. - NO synthetic
current_balance— always operator-fresh. - NO hidden surcharge folded into
amount_inr— fee is a separate line, always. - NO "instant credit guarantee" — show
credit_eta_secondsas estimate, with explicit "usually" qualifier. - NO auto-recommend an amount higher than user's typical pattern — top-up suggestion stays user-pattern-aware.
- NO bundling load with promo offers from the operator — keeps the payment surface clean.
- NO marketing language ("smart wallet", "intelligent top-up") — boring plain UI is the doctrine.
- NO showing post-load balance as confirmed until operator credit webhook fires.
- NO Autopay mandate creation without explicit consent screen — never auto-opt-in.
- NO TOMO-issued referral nudge ("invite a friend to load") on the payment surface.
13. REGULATORY FRAMING
- RBI Master Direction on PPIs (DPSS.CO.PD.No.1064/02.14.006/2017-18, as amended) governs every wallet operator. Full-KYC PPI: ₹2L balance. Small-PPI: ₹10k balance + ₹10k/month load.
- NHAI FASTag is regulated under MoRTH + RBI PPI. Each issuer (HDFC/ICICI/SBI/Paytm/etc.) holds its own PPI license.
- Metro cards in major cities (DMRC/HMRL/CMRL/BMRC) are closed-loop PPIs under state transit authority.
- NPCI UPI Autopay governs
auto_when_lowmandate creation. Mandate is bank-to-operator; TOMO is initiator only. - DPDPA 2023 — account_ref / vehicle_rc / tag_class are personal data; encrypted on-device + at partner, never at TOMO.
- PMLA 2002 — operator (not TOMO) is the PMLA reporter for suspicious loads.
- TOMO does NOT operate any wallet itself, does NOT hold any PPI license, does NOT carry any custody risk.