# CFCFRMS: I4C-MHA API Integration (Banks ↔ I4C 2.0) This is the runbook for the bank's real-time integration with the NCRP-CFCFRMS platform of I4C (Ministry of Home Affairs), built to the **I4C-MHA "Banks API Integration Specification 2.0"**. It covers what the integration does, how to configure and test it, and what an officer does with it day to day. The work is required by RBI letter **CO.DOS.KYC.No.S6675/11-01-006/2025-26 (05 Dec 2025)**. It asks banks, including Primary UCBs, to: - integrate by API for real-time action on cyber-fraud complaints; - cover every system and delivery channel; - periodically review the integration's performance to minimise failures. The older CFCFRMS demo (`/v1/cfcfrms/*`, `/v1/simulator/cfcfrms/*` and the Angular mock interceptor) is untouched. This integration lives beside it under `/v1/i4c/*`. --- ## 1. What happens to a complaint ``` I4C ──POST /auth/token──────────────► bank checks username/password (plain or CryptoJS-encrypted) → access_token I4C ──POST /complaints (Bearer)─────► bank checks token + source IP → validates → stores → ack {00, job_id} │ (after commit, on the I4C worker pool) ▼ for each incident (own transaction): find the account (savings, then current) layer 0 → find the debit, report where the money went layer ≥1 → hold up to the disputed amount at once, report where the rest went (next bank, by I4C bank code), layer 1 only: keep the rest as a prospective hold ▼ bank ──POST ───────────────► I4C token bank ──POST (Bearer)──► I4C section 6 message: acknowledgement_no, job_id, transactions[] (outbox: retried with backoff until delivered) ``` ### The chain: Bank A → Bank B → Bank C (MHA SOP 02.01.2026, para 6(g), 6(j), 9.1) - The victim complains on NCRP / 1930. CFCFRMS sends the complaint to the **victim's bank (A, layer 0)** and to the bank that received the money (**B, layer 1**), each with its own layer number. A reports which debit it was and where it went; B holds what is left. - If B's customer had already sent part of it to **Bank C**, B reports a *Money Transfer To* entry naming C by its **I4C bank code**. CFCFRMS then sends C its own complaint as **layer 2**, and so on "till the complete money trail is established" (SOP 6(j)). - This CBS plays every role. It reads the layer in each incident: - **Layer 0:** it traces the victim's debit. - **Layer 1** (a first-layer account, SOP definition (x)): it holds the disputed amount, reports the onward transfers for what is gone, and keeps a **prospective hold** for the gap. The prospective hold tops up from later credits (SOP 9.1(i)). - **Layer 2 and later:** it holds what is there and reports onward. It places **no prospective hold**, because SOP 9.1(i) limits that to first-layer accounts. - **The next bank must be named by its code.** The seeded master knows only some IFSC prefixes, and co-operative banks often use a sponsor bank's IFSC series. Bank Master > *IFSC to I4C Bank Code map* (migration 0617, endpoints `GET/POST /v1/i4c/cases/ifsc-map`) maps a prefix or a full IFSC to the right code. It also lists the IFSCs already reported with code 0, and the dashboard counts them. - **Money that moved within this bank** (to another savings / current account here, or to a loan) is raised as a linked alert by the Hold Review job, so the officer can act before CFCFRMS sends the next-layer complaint back to us. - **Repeat pushes:** the same reported transaction (ack, account, layer, RRN, amount) arriving again in a new request finds the earlier hold. It is never held twice. - **Freeze and digital suspension** need the LEA's direction (order / notice reference, SOP 9.1(i), S.106 BNSS). Without it, only the disputed amount is held. - The hold detail shows the customer's **other accounts** here, to report as related accounts (SOP 9.1(v)). They are not held automatically. ### Response types produced | txn_type_id | txn_type | When | |---|---|---| | 1 | Transaction Put on Hold | Money held on the account here. A 0.00 entry with remarks means nothing could be held yet; the amount is prospective. | | 3 | Money Transfer To | The money went on by outward IMPS/NEFT/RTGS (payee IFSC, account, UTR and I4C bank code), or to another account in this bank. | | 14 | Withdrawal through Cheque | Withdrawn at the branch against a cheque (clerk register or CTS clearing). A branch cash withdrawal above Rs 500 is also reported here, with remarks saying so (see §8). | | 17 | Others Less than equal to 500 | An untraced debit of Rs 500 or less. | | status_code 01 | Record not found | The account does not exist in this CBS. | | status_code 02 | Invalid RRN | The account exists, but no debit/credit of that amount near that date. | | 70010 | (in remarks) | The incident could not be processed after 5 attempts. It is reported so I4C is not left waiting, and is handled manually. | Types 5 (ATM), 6 (AEPS) and 11 (POS) are built to the specification's field lists, but are not produced today, because this CBS has no ATM, AEPS or POS channel. A channel that records those debits only has to classify them as `ATM_WITHDRAWAL` / `AEPS_WITHDRAWAL` / `POS_WITHDRAWAL` in `I4cTransactionTracer`, with the ATM / merchant details. The Money Transfer To entry carries the payee reference under **both** `rrn` (the section 6 table) and `rrn_transaction_id` (the section 6 sample), with the same value. ### Rules applied to holds These follow the MHA SOP for NCRP-CFCFRMS (02.01.2026) and RBI's draft KYC Amendment Directions on debit holds (Sep 2026). - **Only the disputed amount is held** as an amount hold (`AMOUNT_HOLD`). The rest of the balance stays usable. Every debit path honours the hold: counter, transfer, standing instruction, CTS inward and NACH. - **Prospective hold** (first-layer accounts only, not nodal / pool / escrow, SOP 9.1(i)): what the balance cannot cover is recorded as a shortfall. The *I4C Hold Review* job tops it up from later credits. At layer 2+ the gap is reported onward and is not held from future credits. - **Full debit freeze is never automatic.** An officer asks for it (savings only) and a different officer approves it. - **Review date** is set on every hold: 90 days by default. The register flags holds 15 days before it. - **Customer intimation deadline:** 24 hours by default ("by the end of the next day"). It is tracked in the register until an officer records how the customer was told. - **Release** always goes through maker-checker and records the reason and order: - IO order; - GRM decision (IO / DGRO / SGRO); - MRM restoration; - court order; - LEA direction; - 90-day review; - wrong account. --- ## 2. Endpoints ### Called by I4C (outside the Fineract login; see `SecurityConfig`) | Method | Path | Spec section | |---|---|---| | POST | `/fineract-provider/api/v1/i4c/auth/token` | 2: returns `{"access_token":"…","token_type":"Bearer","expires_in":900}` or 401 `{"message":"You are not authorized to access"}`. Credentials may come as JSON `username`/`password` or `client_id`/`client_secret`, as a form body, or as an `Authorization: Basic` header. | | POST | `/fineract-provider/api/v1/i4c/complaints` | 4 and 5: returns `{"meta":{"response_code":"00","response_message":"Success"},"data":{"acknowledgement_no":"…","job_id":"BANKS-…"}}`, or code `11` "Structure validation failed" (HTTP 400) | I4C sends no tenant header. These paths are served by the tenant in `fineract.i4c.tenant-identifier`. Other refusals: - 503 with code `70004` while the integration is disabled; - 403 from an IP not on the allowlist; - 413 (`70005`) above 1 MB; - 429 (`70006`) above 600 calls/minute from one IP; - 400 (`60015`) when an encrypted envelope cannot be decrypted. **How a push may authenticate** (section 2 names "Basic Auth / Auth 2.0" and credentials "in every request"). `fineract.i4c.inbound.auth-modes` lists the allowed ones; every one checks the same credentials and the same IP allowlist: | Mode | What I4C sends | |---|---| | `TOKEN` | `Authorization: Bearer ` from `/auth/token` | | `BASIC` | `Authorization: Basic base64(username:password)` | | `BODY` | `username` / `password` (or `client_id` / `client_secret`) inside the complaint JSON. They are removed from the message before it is stored. | Each credential may be plain, plain Base64 (the specification's text), or CryptoJS-encrypted (its samples). **Refused pushes.** With `ack.code-style=SECTION5` the ack is always `11` "Structure validation failed". With `SECTION7` it carries the specific section 7 code: `40001` invalid account number, `40004` invalid amount, `40005` invalid date (also a `transaction_date` more than `ack.max-future-days` ahead). **A repeat push** with the same acknowledgement number and the same incidents is never processed twice. With `ack.duplicate-mode=SAME_JOB` it gets `00` and the original job_id. With `CODE_50003` it gets HTTP 409 and `50003` "Duplicate Transaction", still with the original job_id. **Separate ack call.** The ack is always the reply to the push. If I4C also wants it as a call of its own (section 5 is titled "Ack Request Message from BANKS to I4C-MHA"), set `outbound.ack-path`. The ack is then queued with the request and delivered before the response. ### Officer API (ordinary login): `/v1/i4c/admin/*` | Path | Permission | |---|---| | `GET dashboard`, `requests`, `requests/{id}`, `holds`, `holds/{id}`, `hold-actions`, `outbox`, `api-log`, `api-log/{id}`, `bank-master`, `configuration` | `READ_I4C` | | `POST holds/{id}/actions` (`RELEASE` with an optional `amount` for a partial release, `UNFREEZE_AND_RELEASE`, `FREEZE`, `UNFREEZE`) | `REQUESTACTION_I4CHOLD` | | `POST hold-actions/{id}/approve`, `.../reject` | `APPROVEACTION_I4CHOLD` (never your own request) | | `POST holds/{id}/intimation` (record by hand), `POST holds/{id}/intimation/send` (send now on the automatic channels) | `INTIMATE_I4CHOLD` | | `GET intimation-channels` | `READ_I4C` | | `PUT intimation-channels/{channel}`, `POST intimation-channels/{channel}/test` | `CONFIGURE_I4C` | | `POST outbox/{id}/retry`, `requests/{id}/reprocess` | `RETRY_I4C` | Every list (`requests`, `holds`, `hold-actions`, `outbox`, `api-log`, `bank-master`) is paged: it takes `offset` and `limit` (default 20, at most 500) and returns `{totalFilteredRecords, pageItems}`. Each list also takes `fromDate` / `toDate` (yyyy-MM-dd, both days included; not for `bank-master`) and its own filters, which are listed in the Swagger summary of each endpoint. ### GRM / MRM API (ordinary login): `/v1/i4c/cases/*` | Path | Permission | |---|---| | `GET grievances`, `grievances/{id}`, `restorations`, `restorations/{id}`, `pro-rata`, `mrm-balance-report`, `mrm-balance-report.csv`, `holds/{id}/statement.csv`, `documents`, `documents/{id}/content` | `READ_I4C` | | `POST holds/{id}/grievances`, `grievances/{id}/edd`, `…/submit`, `…/nodal`, `…/events`, `…/investigation`, `…/decision`, `…/escalate`, `…/withdraw` | `MANAGE_I4CGRIEVANCE` | | `POST grievances/{id}/action` (raise the account change an order needs) | `MANAGE_I4CGRIEVANCE` + `REQUESTACTION_I4CHOLD` | | `POST holds/{id}/restorations` (assess), `PUT restorations/{id}/request`, `POST restorations/{id}/bank-action`, `…/mrm-updated`, `victim-cases`, `restorations/{id}/receipt`, `…/victim-status` | `MANAGE_I4CRESTORATION` (bank action COMPLIED also needs `REQUESTACTION_I4CHOLD`) | | `POST restorations/{id}/retry-outward` | `APPROVEACTION_I4CHOLD` | | `POST documents` | `MANAGE_I4CGRIEVANCE` / `MANAGE_I4CRESTORATION` / `REQUESTACTION_I4CHOLD` by owner | In the UI: **CFCFRMS → I4C Live**. Its tabs are Dashboard, Complaints from I4C, Holds Register, Approvals, GRM Grievances, MRM Restorations, MRM Balance Report, Responses & API Log, Bank Code Master, and Intimation Settings. A hold's **GRM / MRM & Documents** tab opens a grievance or an MRM assessment, downloads the account statement and holds the IO / court orders. --- ## 3. Configuration (`application.properties`, all values from the environment) | Key / env var | Meaning | |---|---| | `fineract.i4c.enabled` / `FINERACT_I4C_ENABLED` | Master switch. Default `false`. | | `fineract.i4c.tenant-identifier` / `FINERACT_I4C_TENANT` | Tenant that serves I4C's calls. | | `fineract.i4c.bank-code` / `FINERACT_I4C_BANK_CODE` | This bank's code in the I4C Bank Code Master (`root_bankid`). | | `fineract.i4c.bank-name` | Name reported for internal transfers. Defaults to the master's name for the code. | | `…inbound.username` / `FINERACT_I4C_INBOUND_USERNAME` | Username the bank issues to I4C. | | `…inbound.password-hash` / `FINERACT_I4C_INBOUND_PASSWORD_HASH` | **bcrypt** hash of that password (production). | | `…inbound.password` / `FINERACT_I4C_INBOUND_PASSWORD` | Plain password. Used only when no hash is set (UAT). | | `…inbound.token-ttl-seconds` | Token lifetime. Default 900. | | `…inbound.allowed-ips` / `FINERACT_I4C_ALLOWED_IPS` | Comma-separated I4C source IPs. **Blank allows any IP, which is for UAT only.** | | `…inbound.trust-forwarded-for` | Set `true` only behind a reverse proxy that sets `X-Forwarded-For`. | | `…inbound.auth-modes` / `FINERACT_I4C_AUTH_MODES` | `TOKEN,BASIC,BODY` (default). List only the forms I4C uses. | | `…inbound.client-name` / `FINERACT_I4C_CLIENT_NAME` | Client name registered with I4C. The client ID and secret are the inbound username and password. | | `…crypto.mode` / `FINERACT_I4C_CRYPTO_MODE` | `FIELDS` (username/password encrypted, as in the spec), `ENVELOPE` (whole body as `{"data":"…"}`), or `NONE`. | | `…crypto.passphrase` / `FINERACT_I4C_CRYPTO_PASSPHRASE` | The "encryption logic" passphrase I4C shares. | | `…outbound.base-url` / `FINERACT_I4C_OUTBOUND_BASE_URL` | I4C's base URL (from I4C). `internal-simulator` routes to the local simulator. | | `…outbound.auth-path`, `…outbound.response-path` | Paths under the base URL. Defaults `/auth/token`, `/bank/response`. **Confirm with I4C.** | | `…outbound.ack-path` / `FINERACT_I4C_OUTBOUND_ACK_PATH` | Path for a separate ack call. Blank (default) = none. | | `…outbound.username`, `…outbound.password` | Credentials I4C issues to the bank. | | `…outbound.timeout-ms`, `…outbound.max-attempts` | Defaults 15000 ms and 8 attempts (backoff 30 s → 1 h). | | `…match.date-window-days` | ± days for amount/date matching. Default 2. | | `…ack.code-style` / `FINERACT_I4C_ACK_CODE_STYLE` | `SECTION5` (always 11) or `SECTION7` (40001 / 40004 / 40005). Default `SECTION5`. | | `…ack.duplicate-mode` / `FINERACT_I4C_ACK_DUPLICATE_MODE` | `SAME_JOB` (00 + original job_id) or `CODE_50003`. Default `SAME_JOB`. | | `…ack.max-future-days` | A `transaction_date` further ahead than this is refused (40005). Default 1. | | `…hold.review-days`, `…hold.intimation-hours` | Defaults 90 and 24. | | `…restoration.fir-threshold` | MRM: above this amount in one complaint the IO must give an FIR. Default 50000. | | `…simulator.enabled` | Local stand-in for I4C. **Never `true` in production.** | To generate the bcrypt hash: `htpasswd -bnBC 10 "" 'the-password' | tr -d ':\n'` (or any bcrypt tool). Paste the result as `FINERACT_I4C_INBOUND_PASSWORD_HASH`. ### Jobs (Scheduler Jobs screen) - **I4C Dispatch**, every minute: - processes complaints the immediate worker did not finish; - sends responses waiting in the outbox; - purges expired tokens. - **I4C Hold Review**, every 15 minutes: tops up prospective holds, retries automatic intimation, and settles outward MRM restorations the fund-transfer module has finished. - Both do nothing while the integration is disabled. ### Database - **Migration `0610_cfcfrms_i4c_integration.xml`:** - tables `m_i4c_access_token`, `m_i4c_request`, `m_i4c_incident`, `m_i4c_finding`, `m_i4c_hold`, `m_i4c_hold_txn`, `m_i4c_hold_action_request`, `m_i4c_outbox`, `m_i4c_api_log`, `m_i4c_bank_master`; - five permissions; - two jobs. - **Migration `0611_i4c_bank_master_seed.xml`:** - the specification's Bank Code Master (491 entries); - the IFSC prefixes of 57 banks whose prefix is certain. - **Migration `0612_i4c_intimation_channels.xml`:** - `m_i4c_intimation_channel` (EMAIL switched on with a default subject and message; SMS and WHATSAPP switched off); - `m_i4c_intimation_log` (every notice sent, failed or recorded, per hold); - column `m_i4c_hold.auto_intimation_attempts`; - permission `CONFIGURE_I4C`. - **Migration `0617_i4c_ifsc_bank_code_map.xml`:** `m_i4c_ifsc_map` (the bank's own IFSC prefix / full IFSC -> I4C bank code). - **Migration `0613_i4c_grm_mrm.xml`:** - `m_i4c_grievance`, `m_i4c_grievance_event` (GRM), `m_i4c_restoration` (MRM, beneficiary and victim bank), `m_i4c_document` (files, with SHA-256); - columns `m_i4c_hold.released_amount`, `restored_amount`; `m_i4c_hold_action_request.amount`, `restoration_id`, `grievance_id`; - permissions `MANAGE_I4CGRIEVANCE`, `MANAGE_I4CRESTORATION`. ### Accounting (Organisation > Clearing GL Configuration) Map these GL roles (migration 0614 adds them unmapped): | Role | GL (example from the chart) | Use | |---|---|---| | `FUNDTRANSFER_OUTWARD_SUSPENSE` | 200502 NEFT OUTWARD SUSPENSE (liability) | money sent, not yet settled | | `FUNDTRANSFER_SETTLEMENT` | the sponsor bank / RBI current account (asset), e.g. 100201 | settled outward transfers | | `I4C_RESTORATION_PAYABLE` | a sundry liability, e.g. under 200900 | restored money owed to victims in other banks | | `I4C_SHADOW_CREDIT` | a receivable (asset) | shadow credits to our customers on reported fraud | Entries: - Hold / release: none (a lien on the account, memo only). - Restore to a victim here: Dr suspect's Savings / Current Control, Cr victim's control (account transfer). - Restore to another bank: approval Dr suspect's control, Cr I4C Restoration Payable; sending Dr Payable, Cr Outward Suspense; settlement Dr Outward Suspense, Cr Settlement; failure Dr Outward Suspense, Cr Payable. - Every outward NEFT / IMPS / RTGS (not only I4C) now books the same way once `FUNDTRANSFER_OUTWARD_SUSPENSE` is mapped: Dr payer's Savings Control, Cr Outward Suspense; settlement on SUCCESS; failure / recall credit the payer back. Unmapped, the earlier savings "suspense account" behaviour continues. - Shadow credit: Dr I4C Shadow Credit, Cr customer's control; regularisation when restored money arrives: Dr customer's control, Cr I4C Shadow Credit. Also fixed in the CBS core: a book debit (demand draft, outward transfer) now honours every hold and the debit block on savings accounts, and a block on a current account (debit freeze) is enforced on all its debits. ### MHA SOP duties tracked (migration 0614, permission `MANAGE_I4CRISK`) - **15-day credit** to the victim after the IO's notice; **15-day challenge** by the suspect after the refund. - **90-day lapse** counted from the bank's GRM grievance; notice to the LEA due 15 days before; an LEA extension recorded; a release "90-day review" without an order is allowed only after the lapse with no extension. - **Digital banking suspension** on a repeatedly reported account (hold action SUSPEND_DIGITAL / RESTORE_DIGITAL, maker-checker); lifted with the last hold on the account. - **Mule register**: an account in 2+ complaints is flagged SUSPECTED automatically; confirm or clear with the basis. - **Fraud reporting** (RBI (UCB - Fraud Risk Management) Directions, 2026, RBI/DoS/2026-27/439, 31.07.2026): opening the case starts the FIU-IND STR (7 days, PML Rules); the account holder gets a show-cause notice with at least **21 days** to reply and a reasoned order before classification; the RBI FMR is then due within **14 days**. - **Linked alerts:** after the reported credit, the Hold Review job raises an alert for each transfer from a held account to a loan here (SOP 9.1(v)) or to another savings / current account here. - **Victim bank, customer protection**: the framework follows the transaction date. - Before 01.01.2027 - the 2017 framework: zero liability on bank fault or a third-party breach reported within 3 working days, limited liability for 4-7 working days (Rs 5,000 BSBD / 10,000 savings / 25,000 current), shadow reversal within 10 working days, resolution within 90 days. - From 01.01.2027 - RBI (UCB - Responsible Business Conduct) Third Amendment Directions, 2026 (RBI/2026-27/172, 24.06.2026): zero liability on bank negligence or a third-party breach reported within **5 calendar days**; resolution within **45 calendar days** (60 cross-border); compensation for customer-negligence losses up to Rs 50,000 = 85% of the net loss or Rs 25,000, whichever is less, once in a lifetime, reported to the bank and NCRP / 1930 within 5 calendar days, paid within **5 calendar days** of the application (maker-checker), shared RBI / this bank / beneficiary bank and claimed quarterly (CSV: `compensation-claims.csv`); for frauds up to 31.12.2027. - A shadow reversal is provisional: it is credited with a lien so the customer cannot use it until zero liability is established ("make final") or it is recovered from restored money. - GL roles: `I4C_SHADOW_CREDIT`, `I4C_COMPENSATION_RECEIVABLE` (RBI and beneficiary-bank shares), `I4C_COMPENSATION_EXPENSE` (this bank's share). - **Nodal / pool / escrow accounts** (migration 0615), listed on Risk Registers (SOP 9.1(iii)): - the **disputed amount is held** like on any other account; - **no prospective hold** is placed on future credits; - LEAs normally do not ask for suspension or seizure. (Corrected 23.10.2026: an earlier build skipped holds on these accounts altogether. That contradicted SOP 9.1(iii).) - **GRM deadlines** as the GRM manual's flow and SOP 9.1 set them: the bank raises the grievance within **7 days** of the customer approaching it, the IO decides within **15 days** (15 more if investigating), and an IO who does not act in time can be escalated to the District GRO. ### Bank-initiated suspected-mule holds (RBI draft, 11.09.2026) RBI's draft amendment to the KYC Directions (Annex III, effective 01.04.2027 if finalised; banks may adopt earlier) lets the bank itself hold a suspected money-mule transaction. Risk Registers → *Bank-initiated holds*: 1. Hold a flagged transaction of Rs 1,000 or more at once (its amount), or all debits on the account as a last resort, with the reason. The customer is told on the automatic intimation channels (or the officer records it). 2. The customer has **20 days** to explain; the bank decides within **10 days** of the explanation (**30 days** from the hold without one). 3. Satisfied: lift. Not satisfied: refer to the police through NCRP; the police have **30 days** to instruct, otherwise the hold lifts on day 31. 4. Never more than **60 days** without an LEA instruction. The I4C Hold Review job lifts holds at the end of these periods. 5. Grievances are resolved by the nodal officer within **30 days**. Not for nodal, pool or escrow accounts. ### Sources and versions this is built on (checked 23.10.2026) | Source | Version used | |---|---| | RBI letter on NCRP-CFCFRMS API integration | CO.DOS.KYC.No.S6675, 05.12.2025 | | I4C-MHA API Specification (Banks) | 2.0 (no later public version found) | | MHA SOP on NCRP-CFCFRMS custody, restoration and grievance | 02.01.2026 (I4C CEO letter of 03.07.2026 implements it; no revision found) | | GRM user manual / MRM user manual | V3 03.06.2026 / V4 30.07.2026 (as supplied) | | Customer protection in fraudulent EBTs (UCBs) | RBI/2026-27/172, 24.06.2026, from 01.01.2027; 2017 framework before | | Fraud risk management (UCBs) | RBI/DoS/2026-27/439, 31.07.2026 | | Money-mule temporary debit holds | RBI draft amendment to KYC Directions, 11.09.2026 (still a draft; final directions to be issued) | | Supreme Court, digital arrest suo motu | Order of 04.08.2026 (GRM / MRM operationalisation, RBI mule SOP) | ### GRM: grievances against a hold GRM and MRM are I4C web portals with no API. Officers act there; the CBS keeps the bank's record, the deadlines and the documents, and carries orders out on the account through maker-checker. 1. The customer asks the branch to remove the hold / unfreeze. Open a grievance on the hold (Holds Register → hold → **GRM / MRM**). 2. **EDD:** KYC re-verified, the customer's explanation of the credit, risk, and whether the bank supports the request. 3. Raise it on GRM with the KYC, application, photograph and bank statement (attach the same here; the statement downloads from the hold), then record the **GR id**. Stage NODAL_REVIEW. 4. The bank's GRM nodal officer approves (stage WITH_IO, **7 days**) or rejects. 5. The IO decides; if the IO investigates (notice, VC, Samanvaya) record it (**+15 days**). VC requests and information sought are noted in the history. 6. Decision as GRM shows it: *remove put on hold*, *unfreeze, hold continues*, *unfreeze and remove hold*, *keep a smaller amount* (partial), *rejected*, or *seek information*. An allowing decision needs the order number and the signed order copy, and starts a **48-hour** compliance clock. 7. **Raise the account change**: the request goes to a second officer on Approvals; approval changes the account and closes the grievance. 8. Rejected: escalate to the District GRO, then the State GRO (**15 days** each); after the State GRO the customer may go to court. ### MRM: money restoration **Beneficiary bank** (the money is held here): 1. **Assess** each complaint (hold → GRM / MRM → *Assess for MRM*): *possible by attribution*, *possible pro-rata* (the calculator splits what is held on an account by disputed amount), or *not possible* (already refunded / no balance / other) with the reason. Enter the same on MRM; the **MRM Balance Report** page gives every held account's balances for MRM's *Update Account Balance* (CSV). 2. When the victim's request comes from the IO, record the MR id, the refund account (IFSC blank = this bank), the IO, and the papers: a **court order**, or the IO's **BNSS 106 notice** with the victim's **indemnity bond**, plus the **FIR** above Rs 50,000. Attach their copies. 3. **Bank action:** *Compliance* checks the papers and raises a RESTORE request; *not complied* and *refer back to LEA* need the bank statement; *refer back to victim bank* and *consensus* are recorded for MRM. 4. A second officer approves the RESTORE on Approvals. In the same transaction the amount is released from hold and leaves the suspect account: straight to the victim's account if it is here (RESTORED at once, transaction id `CBS-`), otherwise book-debited to the **I4C Restoration Payable** GL (IN_POOL) and paid by NEFT / IMPS / RTGS from that GL through the fund-transfer module (its time window and payee screening apply). TRANSFER_PENDING becomes RESTORED when the transfer settles (Hold Review job). A failed or refused outward returns the money to the payable for **Send again**, possibly to a corrected account; it never goes back to the suspect. The bank must credit the victim within **15 days** of the IO's notice (the notice date is recorded with the request), and after the refund the suspect has **15 days** to contest it (recorded on the restoration). 5. Enter amount, transaction id / UTR and time on MRM, then mark **Entered on MRM** to close. **Victim bank** (our customer lost the money, layer 0): open a victim-bank case with the complaint's ack no., record restored money received (amount, UTR, date, account), or why none is expected (already refunded through bank / police notice / court order, declined, declined to pursue, received through court, not reachable). ### Automatic customer intimation - **Where it is set:** CFCFRMS → I4C Live → **Intimation Settings**. Each channel (Email, SMS, WhatsApp) has an on/off switch, an order, and an editable message. Placeholders such as `{customerName}`, `{amountHeld}`, `{ackNo}` and `{reviewDate}` are filled per hold. Email has a "Send test email" box. - **What happens:** right after the response for a complaint goes to I4C, each new hold with money held is sent to the switched-on channels in order. The first one that reaches the customer marks the hold intimated (mode, "Sent automatically to …", user `system`). - **When it does not reach the customer** (no email on file, mail server down): the attempt is logged as FAILED or SKIPPED_NO_DESTINATION, and the Hold Review job retries up to 3 times. After that, or when no channel is switched on, the hold stays "not yet told". An officer then calls the customer (or writes, or meets them) and records it on the hold's **Customer Intimation** tab. - **Today:** only Email is in use. It goes through the CBS mail settings (`spring.mail.*`), the same ones the KYC notices use. SMS and WhatsApp use the CBS notification gateways and should be switched on only once those gateways are live. - Phone, letter and in-person notices can never be sent by the system. They are always recorded by hand. --- ## 4. Onboarding checklist 1. **Nodal officer / SPOC** registered with I4C. I4C emails them: - user ids and passwords (both directions); - the encryption logic / passphrase; - its public IPs; - UAT URLs. 2. **Give I4C what it needs from the bank:** - the endpoint base: `https:///fineract-provider/api/v1/i4c`; - the bank's public IP; - the client name, client id and client secret it asks for (the inbound username/password above). 3. **Connectivity.** The C-DOT *AWS Client VPN* SOP (IAM Identity Center SSO + MFA) is for **people**: developers and officers testing against UAT. A server cannot run it unattended. For the CBS server, ask C-DOT/I4C for IP whitelisting of the bank's egress IP, or a site-to-site tunnel. Open the firewall both ways: - inbound from I4C's IPs to the CBS on 443; - outbound from the CBS to I4C. 4. **Set the environment variables** in §3, with `fineract.i4c.enabled=true`, and restart. Check **I4C Live → Dashboard → Setup still needed**: it lists anything missing. 5. **Grant permissions:** - `READ_I4C` to the fraud cell and branch managers; - `REQUESTACTION_I4CHOLD` to makers; - `APPROVEACTION_I4CHOLD` to checkers (different people); - `INTIMATE_I4CHOLD` to branch staff; - `RETRY_I4C` to IT operations. 6. **Set the branch IFSCs** in Branch Profile (`m_office_profile.ifsc_code`). They are reported as `ifsc_code` / `root_ifsc_code`. 7. **Test in UAT** with I4C (§5), then switch to production URLs and credentials. --- ## 5. Testing without I4C (local / UAT) Set: ``` FINERACT_I4C_ENABLED=true FINERACT_I4C_SIMULATOR_ENABLED=true FINERACT_I4C_OUTBOUND_BASE_URL=internal-simulator FINERACT_I4C_INBOUND_USERNAME=i4c-test FINERACT_I4C_INBOUND_PASSWORD=i4c-test-pass FINERACT_I4C_BANK_CODE= ``` Then, with a funded active savings account `000000012345`: ```bash H='https://localhost:8443/fineract-provider/api/v1' # 1. Simulate a layer-1 push (fraud money credited to this account) curl -k -X POST "$H/i4c-sim/push" -H 'Content-Type: application/json' \ -d '{"accountNumber":"000000012345","amount":"2500.00","disputedAmount":"2500.00","rrn":"612345678901","transactionDate":"2026-10-20","layer":"1"}' # 2. Or go through the real inbound API exactly as I4C would TOKEN=$(curl -sk -X POST "$H/i4c/auth/token" -H 'Content-Type: application/json' \ -d '{"username":"i4c-test","password":"i4c-test-pass"}' | sed 's/.*"access_token":"\([^"]*\)".*/\1/') curl -k -X POST "$H/i4c/complaints" -H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' -d @complaint.json # 3. See what the bank sent "I4C" curl -k -X POST "$H/i4c-sim/received" ``` What to check: - The ack is `00` with a `job_id`. - The account shows an *Amount Hold* transaction, and its available balance dropped by the held amount. - A withdrawal above the remaining balance is refused. - **I4C Live → Complaints** shows the complaint as PROCESSED, with the response SENT. - **Holds Register** shows the hold. Raise a release as one user. Approving it as the same user is refused; approving it as a second user releases the amount on the account. The backend unit tests are under `fineract-provider/src/test/java/org/apache/fineract/portfolio/cfcfrms/i4c/`: ``` ./gradlew :fineract-provider:test --tests "org.apache.fineract.portfolio.cfcfrms.i4c.*" ``` --- ## 6. Daily operations - **Dashboard.** Watch these figures: - *Customer intimation overdue*; - *Holds due for 90-day review*; - *Release / freeze awaiting approval*; - *Responses failed*. The API performance table (calls, failures, average and max ms in each direction, average complaint-to-response time) is the RBI para 4 periodic review. Export it monthly for the Board / IT committee. - **Holds Register:** - customer intimation: check the automatic notice in the hold's intimation history, press **Send now** to resend, or call the customer and record it; - raise releases on IO / court orders (in full, or the amount the order releases); GRM and MRM orders are raised from their records; - raise a full debit freeze only on an explicit direction. - **GRM Grievances / MRM Restorations:** filter *Past deadline only* every morning; carry out allowed orders within 48 hours; send restorations held in the payable again; mark restorations entered on MRM; record LEA notices 15 days before a hold lapses; review suspected mules; file FMR / STR by their due dates; action loan-repayment alerts (Risk Registers page). - **Approvals:** a second officer approves or rejects. Approval acts on the account immediately. If the account refuses (closed, hold already released at the counter), the request becomes FAILED with the reason. - **Responses & API Log:** - retry FAILED responses; - open any call to see the masked request and response. --- ## 7. Security notes - I4C's token is 32 random bytes. Only its SHA-256 hash is stored. It expires after `token-ttl-seconds`. - Every inbound call checks the token **and** the source IP allowlist. - Credentials, tokens and PANs are masked in the API log and in the stored payload. - The inbound password should be supplied as a bcrypt hash. All secrets come from environment variables; none are committed defaults. - Holds placed automatically are attributed to the CBS **system** user. Releases and freezes are attributed to the approving officer, with the maker recorded. - The simulator endpoints answer 404 unless `fineract.i4c.simulator.enabled=true`. --- ## 8. Known limits of this CBS (stated honestly) - **No RRN/UTR on account transactions.** - A reported credit is matched by RRN only where the CBS holds one: outward fund transfers (`m_fund_transfer.utr/txn_ref`), or an RRN quoted in a transaction note or transfer description. - Otherwise it is matched by **account, exact amount and date ± window**. The match method is shown per incident. - Inbound IMPS is a stub that posts no credit. - **Follow-up:** store the UTR/RRN on inward postings. - **No UPI, ATM, POS or AEPS channels.** The response formats for ATM (5), AEPS (6) and POS (11) are built and tested, but nothing produces them until such a channel exists. - **Branch cash withdrawals** have no specification type. Above Rs 500 they are reported as type 14 with an empty cheque number and remarks "Cash withdrawal at branch counter (no cheque)". **Confirm with I4C.** - **Current-account transfer legs** are not linked transaction-to-transaction, so onward tracing from a current account is limited to counter withdrawals. - **Payee bank name:** IFSCs not in the seeded prefixes or the bank's own IFSC map are reported with code 0 and the IFSC in full. Until they are mapped, CFCFRMS may not be able to route the next-layer complaint by code. They are listed on Bank Master. - **GRM and MRM** are I4C portal modules with no published API. Officers act there and record it here; nothing is sent to the portals automatically. - **Outward restorations** need the GLs below mapped; without them only restorations to accounts in this bank can be approved. - **Digital-channel suspension** is enforced on the CBS's own fund transfers. UPI, ATM, cards and AePS are not run by this CBS; the suspension is listed for the systems that run them. - **Shadow credits** are booked to savings accounts; working days skip weekends only (bank holidays are not known to the CBS). - **RBI FMR / FIU-IND STR** are filed on their own portals; the CBS keeps the due dates, references and a CSV of the cases. --- ## 9. Questions to settle with I4C / C-DOT 1. Exact URL paths for the bank → I4C token, response and (if any) ack calls, and the token lifetime. 2. Is the section 5 ack the synchronous HTTP response to the push, or also a separate call? Both are supported; set `outbound.ack-path` for the second. 3. Encryption: only username/password (assumed, `FIELDS`), or the whole payload? Is it the CryptoJS/OpenSSL passphrase format (AES-256-CBC, EVP_BytesToKey-MD5) as the samples suggest? 4. Which code set does the ack use, `00`/`11` (section 5) or the section 7 codes (`40001`…)? Both are supported (`ack.code-style`). And does a repeat push get `00` or `50003` (`ack.duplicate-mode`)? Which auth form will I4C use: token, Basic, or credentials in the message (`inbound.auth-modes`)? 5. The specification's samples have typos (`mode_of _payment`, `acknowledment_no`, `approval_code.`). Which spelling will I4C actually send? The parser accepts all of them. 6. The date format for `transaction_datetime` (`yyyy-MM-dd HH:mm:ss` assumed). 7. How to report branch cash withdrawals and debits of unknown channel. 8. For layer ≥ 1, is `payer_account_number` the account at the receiving bank (assumed)? 9. Production IP whitelisting or site-to-site connectivity in place of the user VPN.