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.
● LIVEv1.0.0compliance.verify_kyc

compliance.verify_kyc — Full Intent Specification

INTENT NAMESPACE: compliance
INTENT NAME:      verify_kyc
FULL ID:          compliance.verify_kyc
VERSION:          v1.0.0
STATUS:           live
TTBS WEIGHTS:     time 0.30 · taste 0.05 · budget 0.20 · safety 0.45
LAST UPDATED:     2026-05-10

Cross-cutting utility intent. Verifies user identity documents (Aadhaar, PAN, passport, driving license, voter ID) against authoritative sources. Invoked from inside other intents that require KYC (finance.apply_personal_loan, finance.buy_term_insurance, mobility.book_self_drive on first booking, etc.). Safety weight dominates.


1. NATURAL LANGUAGE COVERAGE

Classifies IN

  • "verify my KYC for the loan"
  • "do KYC for car rental"
  • "complete KYC"
  • "Aadhaar verification"
  • "PAN verification"
  • "DigiLocker KYC"
  • "video KYC for insurance"

Classifies OUT — borderline NO

  • "verify RC for buying used car" → compliance.verify_rc_dl
  • "what's my KYC status" (read-only) → utility lookup
  • "register my business with GSTIN" → not a KYC intent

MULTI-INTENT TRIGGERS (most common case)

KYC is almost always runtime-injected by another intent:

  • finance.apply_personal_loan → injected compliance.verify_kyc if user not already KYC'd
  • mobility.book_self_drive → injected if first-time renter
  • marketplace.buy_used_car → injected for buyer + seller
  • finance.buy_term_insurance → injected if claim amount > ₹50L

Standalone invocation is rare but supported (user proactively does KYC before applying for products).


2. INPUT — TOMO → PROVIDER

{
  "intent":          "compliance.verify_kyc",
  "intent_version":  "v1.0.0",
  "request_id":      "req_kyc_8f4k_2026-05-10T08:14:00Z",
  "user_session_id": "anon_user_token_or_uid",

  "kyc_kind":        "video_kyc",
  "kyc_purpose":     "personal_loan",
  "regulator":       "rbi",

  "documents_to_verify": [
    {
      "doc_kind":           "aadhaar",
      "doc_number_masked":  "•••• •••• 1234",
      "verification_method": "digilocker"
    },
    {
      "doc_kind":           "pan",
      "doc_number_masked":  "•••• 1234X",
      "verification_method": "income_tax_api"
    }
  ],

  "user_profile_provided": {
    "full_name_legal":      "REDACTED at TOMO ingest — partner gets via secure channel",
    "date_of_birth":        "REDACTED",
    "gender":               "REDACTED",
    "address_line_1":       "REDACTED",
    "city":                 "REDACTED",
    "state":                "REDACTED",
    "pincode":              "REDACTED",
    "country_code":         "IN",
    "phone_masked":         "+91 98XXX XX234",
    "email_masked":         "k••••@gmail.com"
  },

  "is_runtime_injected":   true,
  "injected_from_intent":  "finance.apply_personal_loan",
  "injected_request_id":   "req_pl_8f4k_...",
  "minimum_kyc_level_required": "full_kyc",

  "context": {
    "user_locale":          "en-IN",
    "user_currency_pref":   "INR",
    "trust_signals": {
      "is_repeat_kyc":            false,
      "prior_kyc_with_partner":   0,
      "user_account_age_days":    312,
      "previously_flagged":       false
    }
  }
}
Field Type Constraint Notes
kyc_kind enum REQUIRED, STRICT see §6
kyc_purpose enum REQUIRED, see §6 drives required document set
regulator enum REQUIRED, see §6 RBI / IRDAI / SEBI / RTO / generic
documents_to_verify array REQUIRED, ≥1 each entry is a doc to verify
documents_to_verify[].doc_kind enum REQUIRED, see §6
documents_to_verify[].doc_number_masked string REQUIRED last-4 only (privacy)
documents_to_verify[].verification_method enum REQUIRED, see §6
user_profile_provided object REQUIRED, all fields REDACTED partner receives full data via secure channel directly
minimum_kyc_level_required enum REQUIRED, STRICT min_kyc | full_kyc | video_kyc | in_person_kyc
is_runtime_injected bool REQUIRED drives different success widget
injected_from_intent string or null REQUIRED parent intent if injected
context.trust_signals.previously_flagged bool REQUIRED prior KYC failure history

Privacy note: TOMO never stores raw KYC data. The user_profile_provided block surfaces redacted previews; the actual document numbers + full identity data flow from user → partner directly via the partner's encrypted upload widget. TOMO's role is orchestration, not data custody.

Anti-fabrication preamble: no paid placement, no commission-based partner ordering, TOMO never holds raw KYC data.


3. PROVIDER TOOLS

Tool 1: initiate_kyc_session

PURPOSE:        create a KYC verification session
INPUT:          §2 request body
OUTPUT:         { session_id, session_token, partner_kyc_url, expires_at, expected_minutes }
SLA:            p95 < 1500ms
IDEMPOTENCY:    REQUIRED on (user_session_id, kyc_purpose) — same purpose for same user returns same session_id within 24h
PRIVACY:        partner returns a URL for user to complete KYC in partner's own surface

Tool 2: get_kyc_status

PURPOSE:        poll session status
INPUT:          { session_id, request_id }
OUTPUT:         KycStatus (§5)
SLA:            p95 < 600ms
RATE LIMIT:     ≤ 1 every 5s

Tool 3: consume_kyc_result

PURPOSE:        receive partner's verified KYC summary (after user completes)
INPUT:          { session_id, request_id }
OUTPUT:         KycVerificationResult (§5) — minimal data
SLA:            p95 < 1000ms
RETURNS:        verification_status booleans + reference IDs.
                NO raw document data. NO scanned image URLs.

Tool 4: cancel_kyc_session

PURPOSE:        user cancels mid-flow
INPUT:          { session_id, reason, request_id }
OUTPUT:         { status, partial_data_purged: bool, refund_to_purpose_intent: bool }
SLA:            p95 < 1500ms

Tool 5: revoke_kyc

PURPOSE:        user requests deletion of completed KYC (DPDP 2023 right-to-erasure)
INPUT:          { session_id, reason, request_id, user_consent_token }
OUTPUT:         { status, deletion_iso, downstream_partners_notified[] }
SLA:            p95 < 5000ms (asynchronous delete OK)

All five REQUIRED. Tool 5 is non-negotiable for DPDP 2023 compliance.


4. RESPONSE SHAPE

initiate_kyc_session output

session_id:                       string, REQUIRED
session_token:                    string, REQUIRED                # opaque, used in subsequent calls
partner_kyc_url:                  URL, REQUIRED                    # user-facing URL for KYC completion
url_expires_at:                   ISO_DATETIME, REQUIRED
expected_completion_minutes:      int, REQUIRED                    # typical user time
session_expires_at:               ISO_DATETIME, REQUIRED          # session lifetime

method:
  primary_method:                 STRICT ENUM, REQUIRED            # see §6
  fallback_methods:               array<enum>, REQUIRED, may be empty
  video_kyc_human_required:       boolean, REQUIRED
  video_kyc_avg_wait_minutes:     int, REQUIRED                    # 0 if not video KYC

documents_required:               array, REQUIRED, ≥1
  - doc_kind:                     STRICT ENUM, REQUIRED
    is_mandatory:                 boolean, REQUIRED
    upload_methods:               array<enum>, REQUIRED, ≥1        # see §6 (digilocker | upload | api)
    sample_format:                STRICT ENUM, REQUIRED            # pdf | jpg | png | scanned

privacy:
  data_minimization_enforced:     boolean, REQUIRED                # MUST be true
  data_retention_days:            int, REQUIRED                    # per regulator / DPDP
  data_retention_purpose:         string, REQUIRED                  # legal basis for retention
  data_sharing_with_third_parties: boolean, REQUIRED                # MUST be false unless regulator-mandated
  user_consent_required:          boolean, REQUIRED                # MUST be true
  user_can_revoke:                boolean, REQUIRED                # MUST be true (DPDP)

trust:
  partner_iso_27001_certified:    boolean, REQUIRED
  partner_soc_2_type_2_audited:   boolean, REQUIRED
  partner_iso_27018_certified:    boolean, REQUIRED                # cloud privacy
  partner_authorized_by:          STRICT ENUM, REQUIRED            # see §6
  partner_license_number:         string, REQUIRED
  partner_license_valid_until:    ISO_DATE, REQUIRED
  digilocker_authorized:          boolean, REQUIRED
  uidai_authorized_user_agency:   boolean, REQUIRED                # for Aadhaar
  uidai_aua_kua_kind:             STRICT ENUM, REQUIRED            # see §6

fees:
  fee_inr:                        INR_INTEGER, REQUIRED            # 0 if free for purpose intent
  fee_charged_to:                 STRICT ENUM, REQUIRED            # user | parent_intent_partner | tomo (NEVER tomo for v1)
  refund_on_failure:              boolean, REQUIRED

_provider:
  name:                           string, REQUIRED
  tomo_partner_id:                string, REQUIRED
  partner_tier:                   STRICT ENUM, REQUIRED
  customer_support_phone:         string, REQUIRED
  customer_support_24x7:          boolean, REQUIRED
  in_app_chat_supported:          boolean, REQUIRED
  human_kyc_specialist_available: boolean, REQUIRED

KycStatus (returned by get_kyc_status)

session_id:                       string, REQUIRED
status:                           STRICT ENUM, REQUIRED            # see §6
status_updated_iso:               ISO_DATETIME, REQUIRED
status_history:                   array, REQUIRED, ≥1
  - status:                       STRICT ENUM, REQUIRED
    iso:                          ISO_DATETIME, REQUIRED
    notes:                        string, REQUIRED                  # "" allowed

progress:
  total_steps:                    int, REQUIRED, ≥1
  completed_steps:                int, REQUIRED, ≥0
  current_step:                   STRICT ENUM, REQUIRED            # see §6
  estimated_remaining_minutes:    int, REQUIRED

documents_received:               array, REQUIRED, may be empty
  - doc_kind:                     STRICT ENUM, REQUIRED
    received_iso:                 ISO_DATETIME, REQUIRED
    verification_status:          STRICT ENUM, REQUIRED            # pending | verified | failed | expired
    verification_iso:             ISO_DATETIME, REQUIRED            # epoch sentinel if pending
    failure_reason:               STRICT ENUM, REQUIRED            # see §6 ("none" if verified or pending)

identity_match:
  name_match:                     STRICT ENUM, REQUIRED            # exact | fuzzy_high | fuzzy_low | mismatch | pending
  dob_match:                      STRICT ENUM, REQUIRED            # match | mismatch | pending
  address_match:                  STRICT ENUM, REQUIRED            # match | partial_match | mismatch | pending

risk:
  risk_score:                     int, REQUIRED, 0-100              # higher = more risky
  risk_signals:                   array<enum>, REQUIRED, may be empty
  watchlist_match:                boolean, REQUIRED                # PEP / sanctions
  pep_status:                     boolean, REQUIRED                # politically exposed person

support_phone:                    string, REQUIRED
support_email:                    string, REQUIRED

KycVerificationResult (returned by consume_kyc_result)

session_id:                       string, REQUIRED
result_id:                        string, REQUIRED                  # opaque KYC reference
verified_at_iso:                  ISO_DATETIME, REQUIRED
verification_kind:                STRICT ENUM, REQUIRED            # min_kyc | full_kyc | video_kyc | in_person_kyc
verification_validity_until_iso:  ISO_DATE, REQUIRED                # KYC expires; user re-verifies after

flags:
  identity_verified:              boolean, REQUIRED
  address_verified:               boolean, REQUIRED
  pan_aadhaar_linked:             boolean, REQUIRED
  watchlist_clear:                boolean, REQUIRED
  pep_status:                     boolean, REQUIRED
  age_at_least_18:                boolean, REQUIRED
  india_resident:                 boolean, REQUIRED
  high_value_eligibility:         boolean, REQUIRED                # for transactions > ₹50L

# NO RAW DOCUMENT DATA RETURNED. NO SCANNED IMAGES.
# Partner's downstream products consume by referencing result_id with user consent.

audit_trail:
  consent_iso:                    ISO_DATETIME, REQUIRED
  consent_text_displayed:         string, REQUIRED                  # exact text user agreed to
  data_collected:                 array<STRICT ENUM>, REQUIRED      # which doc kinds were collected
  data_minimized:                 boolean, REQUIRED                # MUST be true
  retention_days:                 int, REQUIRED
  retention_purpose:              string, REQUIRED

_provider:
  name:                           string, REQUIRED
  tomo_partner_id:                string, REQUIRED

Forbidden fields

paid_placement_score | sponsored_rank | promotion_priority |
ad_bid | hidden_kyc_fee | document_image_url | scanned_aadhaar_url |
raw_document_number_unmasked | partner_will_share_with_advertisers

Critical: TOMO ingest scans for any field containing raw document numbers, full names not masked, scanned image URLs. If detected, the entire response is rejected and the partner is flagged for DPDP 2023 violation.


5. CONTROLLED VOCABULARIES

kyc_kind

min_kyc | full_kyc | video_kyc | in_person_kyc

kyc_purpose

personal_loan | home_loan | credit_card | mutual_fund | demat_account |
trading_account | term_insurance_high_value | health_insurance |
self_drive_rental_first_time | high_value_marketplace_transaction |
employment_verification | tenant_verification | nbfc_credit_check |
generic_kyc

regulator

rbi | irdai | sebi | rto | generic

documents_to_verify[].doc_kind

aadhaar | pan | passport | driving_license | voter_id |
ration_card | bank_statement | utility_bill | property_doc |
employment_letter | itr_form_16 | gstin | shop_establishment_act

documents_to_verify[].verification_method

digilocker | uidai_otp | uidai_offline_xml | income_tax_api |
manual_upload_with_otp_verification | api_provider_verified |
video_kyc_face_match | in_person_visit

method.primary_method

digilocker_pull | aadhaar_otp_authentication | aadhaar_offline_xml |
video_kyc_with_human | video_kyc_automated | document_upload_plus_face_match |
in_person_branch_visit | dsa_field_visit

method.fallback_methods

Same enum as primary_method.

documents_required[].upload_methods

digilocker | direct_upload | api_pull | scan_via_app | physical_upload

documents_required[].sample_format

pdf | jpg | png | scanned_pdf | digilocker_xml | api_json

trust.partner_authorized_by

rbi_pa_psp | rbi_nbfc | irdai_corporate_agent | sebi_registered_intermediary |
mca_registered | uidai_aua_kua | digilocker_authorized_partner

trust.uidai_aua_kua_kind

not_applicable | aua_only | kua_only | aua_kua_both | sub_aua_kua

KycStatus.status

initiated | awaiting_user_action | docs_received | identity_verifying |
identity_verified | risk_assessment | watchlist_check |
verified | failed_doc | failed_identity_match | failed_watchlist |
failed_pep_blocked | timeout | cancelled_by_user | revoked_by_user

KycStatus.progress.current_step

not_started | digilocker_consent | aadhaar_otp_input | document_upload |
face_match | video_kyc_call | review_submitted | risk_assessment |
done

KycStatus.documents_received[].verification_status

pending | verified | failed | expired

KycStatus.documents_received[].failure_reason

none | doc_unreadable | doc_expired | doc_tampered | name_mismatch |
dob_mismatch | photo_mismatch | api_unavailable | uidai_otp_failed |
manual_review_required

KycStatus.identity_match.name_match

exact | fuzzy_high | fuzzy_low | mismatch | pending

KycStatus.identity_match.dob_match

match | mismatch | pending

KycStatus.identity_match.address_match

match | partial_match | mismatch | pending

KycStatus.risk.risk_signals

none | new_pan_within_30_days | aadhaar_recently_updated_address |
multiple_kyc_attempts_24h | mismatched_phone_number |
unusual_geographic_pattern | shared_device_with_other_kyc_user |
recently_blacklisted_in_partner_db | pep_match | sanctions_match

cancel_kyc_session.reason

user_changed_mind | wrong_documents | upload_failure | parent_intent_cancelled |
privacy_concern | technical_failure | other

KycVerificationResult.verification_kind

Same as kyc_kind enum.

audit_trail.data_collected[]

Same as documents_to_verify[].doc_kind enum.

_provider.partner_tier

tier1_path_a | tier1_path_b | tier1_path_c | tier2_path_d

6. TTBS DIMENSIONS

Per-domain weights (locked)

compliance: { time: 0.30, taste: 0.05, budget: 0.20, safety: 0.45 }

Safety weight dominates (highest in any TOMO intent — KYC is identity verification).

TIME

SIGNALS USED:
  - method.expected_completion_minutes (lower = better) weight 0.40
  - method.video_kyc_avg_wait_minutes                   weight 0.20
  - method.primary_method == digilocker_pull (fastest)  weight 0.20
  - partner historical p95 completion time              weight 0.20

USER BAND HANDLING:
  - is_runtime_injected=true → time weight up to 0.45 (user blocked on parent intent)
  - standalone → time weight steady

TASTE

SIGNALS USED:
  - human_kyc_specialist_available                       weight 0.40
  - in_app_chat_supported                                weight 0.30
  - vernacular language support (per user_locale)        weight 0.30

Taste weight is small (0.05) — user just wants KYC done.

BUDGET

SIGNALS USED:
  - fees.fee_inr (lower = better)                        weight 0.50
  - fees.fee_charged_to == user (penalty if true)        weight 0.30
  - refund_on_failure                                    weight 0.20

HARD FILTERS:
  - fees.fee_charged_to == tomo → drop (TOMO doesn't subsidize KYC)

SAFETY (the dominant axis)

SIGNALS USED:
  - trust.partner_iso_27001_certified                    HARD FILTER
  - trust.partner_authorized_by ∈ valid regulator enum   HARD FILTER
  - privacy.data_minimization_enforced=true              HARD FILTER
  - privacy.user_consent_required=true                   HARD FILTER
  - privacy.user_can_revoke=true                         HARD FILTER
  - trust.uidai_authorized_user_agency=true (Aadhaar use) HARD FILTER if Aadhaar in docs
  - trust.partner_iso_27018_certified (cloud privacy)    weight 0.20
  - trust.partner_soc_2_type_2_audited                   weight 0.20
  - privacy.data_sharing_with_third_parties=false        HARD FILTER (must be false)
  - partner_license_valid_until > now+30d                HARD FILTER
  - audit_trail.data_minimized=true                      HARD FILTER
  - method.primary_method ∈ regulator-approved list       HARD FILTER
  - historical breach record (partner)                    weight 0.20 penalty
  - partner_iso_27018_certified                           weight 0.20

User-context HARD FILTERS:
  - kyc_purpose=personal_loan + minimum_kyc_level_required=full_kyc
       → method must support full_kyc → drop video_kyc_only partners
  - kyc_purpose=high_value (>₹50L) → method must include video_kyc OR in_person

Hidden ranking factor

information_completeness_score weight 0.10. historical_kyc_success_rate weight 0.20 — partners with >5% KYC failure rate get penalized.


7. COMPLETION CONTRACT

POST /api/v1/cpc/mcp_provider/{tomo_partner_id}
X-TOMO-Timestamp: <ms>
X-TOMO-Signature: sha256=<hex>

{
  "intent":            "compliance.verify_kyc",
  "intent_version":    "v1.0.0",
  "external_id":       "DIGIO-KYC-XYZ",
  "amount_inr":         0,
  "closed_at":         "2026-05-10T08:42:00+05:30",
  "request_id":        "req_kyc_8f4k_...",
  "status":            "verified",
  "currency":          "INR",
  "session_id":        "DIGIO-KYC-XYZ",
  "verified_at_iso":   "2026-05-10T08:42:00+05:30",
  "verification_kind": "full_kyc",
  "verification_validity_until_iso": "2027-05-10",
  "fees_charged_inr":  0,
  "result_id":         "kyc_result_abc123",
  "documents_verified": ["aadhaar", "pan"],
  "watchlist_clear":   true,
  "high_value_eligible": true,
  "injected_from_intent": "finance.apply_personal_loan",
  "injected_request_id":  "req_pl_8f4k_...",
  "notes":             ""
}

Status enum: verified | failed_doc | failed_identity_match | failed_watchlist | failed_pep_blocked | cancelled_by_user | revoked_by_user

amount_inr is typically 0 because KYC fees are usually absorbed by the parent intent's partner (loan provider, insurance provider, etc.). When amount_inr > 0 (rare standalone KYC fee), TOMO commission applies normally.

Privacy: the completion POST includes ONLY metadata (verification status, validity date, document KINDS verified, eligibility flags). NEVER raw document data.


8. WIDGET

WIDGET TYPE:        kyc_card
SOURCE:             src/widgets/types.ts
TYPE NAME:          KycCardPayload
RENDERED IN:        components/widgets/KycCardWidget.tsx

Two render contexts:

  1. Standalone — full card with KYC kind, expected duration, partner trust badges, "Start KYC" CTA → opens partner_kyc_url in iframe or external.
  2. Runtime-injected — slim banner inside the parent intent's confirmation widget. Shows: "Verify your identity to proceed (estimated 4 min)" with inline "Verify now" button. Completion returns user to parent flow with KYC result attached.

9. CACHING POLICY

Call TTL Rationale
initiate_kyc_session 0s Always fresh
get_kyc_status 0s Live progress
consume_kyc_result 0s Always fresh
cancel_kyc_session 0s
revoke_kyc 0s

10. ERROR CODES

Code HTTP Meaning TOMO behavior
INVALID_REQUEST 400 Malformed surface
INVALID_DOCUMENT_KIND 400 doc_kind not supported surface
UIDAI_DOWNTIME 503 UIDAI Aadhaar API down suggest fallback method
DIGILOCKER_DOWNTIME 503 DigiLocker down suggest fallback method
IDENTITY_MISMATCH 422 name/DOB don't match across docs surface to user, retry
WATCHLIST_BLOCKED 403 PEP/sanctions match surface, terminate KYC
DOC_EXPIRED 422 document past validity surface, request new
SESSION_TIMEOUT 410 user took too long surface, restart
INVALID_AUTH 401 partner credentials bad partner re-auth
RATE_LIMITED 429 partner-side throttle back off
INTERNAL_ERROR 500 partner-side failure drop partner from pool
MANUAL_REVIEW_REQUIRED 202 low-confidence verification escalate to human

11. SANDBOX → PRODUCTION CHECKLIST

[ ] All §2 inputs validated, request_id echoed
[ ] initiate_kyc_session returns valid partner_kyc_url within SLA
[ ] get_kyc_status returns progress updates ≤5s old
[ ] consume_kyc_result returns ONLY metadata (no raw doc data)
[ ] cancel_kyc_session purges partial data per privacy commitments
[ ] revoke_kyc properly purges + notifies downstream consumers (DPDP 2023)
[ ] All §4 required fields populated with REAL data
[ ] No forbidden fields anywhere (no raw doc URLs, no unmasked numbers)
[ ] CPC webhook arrives within 60s of verification completion
[ ] HMAC verification passes
[ ] ISO 27001 certificate uploaded + verified
[ ] ISO 27018 certificate uploaded if cloud-based
[ ] SOC 2 Type 2 audit report uploaded
[ ] RBI / IRDAI / SEBI authorization number valid + verifiable
[ ] UIDAI AUA/KUA authorization for Aadhaar verification (if applicable)
[ ] DigiLocker authorized partner attestation
[ ] Privacy policy URL live + DPDP 2023 compliant
[ ] Data retention period documented + matches regulator requirements
[ ] User consent UX reviewed for clarity
[ ] Failure recovery flow tested (UIDAI downtime, DigiLocker downtime)
[ ] customer_support 24x7 reachable + KYC specialist available
[ ] No commission-based partner ordering (1% audit cross-check)
[ ] Vernacular language support tested (en-IN, hi-IN minimum)
[ ] Field-test with TOMO ops doing real KYC

12. ANTI-FABRICATION RULES

RULE 1 — No paid placement signals
RULE 2 — No raw document data in any TOMO-bound response
  TOMO ingest scans for unmasked Aadhaar / PAN / DL numbers, scanned image URLs,
  full names not redacted. Detection = entire response rejected + DPDP violation flag.
RULE 3 — Data minimization mandatory
  privacy.data_minimization_enforced MUST be true. Partners that collect more
  than the kyc_purpose strictly requires = listing rejection.
RULE 4 — User consent must be auditable
  audit_trail.consent_iso + consent_text_displayed must be exact. Partners
  cannot retro-grant consent.
RULE 5 — User right-to-revoke must function
  revoke_kyc must purge data within partner's documented retention SLA AND
  notify downstream consumers. Failure = DPDP 2023 violation + suspension.
RULE 6 — No third-party data sharing without disclosure
  privacy.data_sharing_with_third_parties MUST be false unless regulator-mandated
  AND disclosed in user consent text.
RULE 7 — UIDAI authorization must be valid
  trust.uidai_authorized_user_agency=true requires UIDAI license certificate
  on demand within 24h. Failure = listing rejection.
RULE 8 — No commission-based partner ordering
  Same KYC purpose on TOMO must rank by user-fit (TTBS, safety dominant),
  not by partner commission.
RULE 9 — Customer support honest
  Human KYC specialist available within stated SLA. Field-tested.
RULE 10 — No watchlist data caching beyond regulator window
  PEP / sanctions data refreshed per regulator requirements. Stale data =
  legal liability + breach.
RULE 11 — No forced upsells inside KYC flow
  Partner cannot use KYC session to push other products. KYC is a utility,
  not a sales surface.
RULE 12 — TOMO never holds raw KYC data
  All raw document data flows user → partner directly. TOMO orchestrates
  but does not custody.

VERSION HISTORY

v1.0.0 — 2026-05-10 — Initial spec (Block F.2)