main
md 263 lines 17.7 KB
Rendered Raw
1 # Phase 5D.2 — ICICI Direct / Breeze API Contract Verification
2
3 Research date: 2026-08-29
4 Status: research/design only; no live request or production integration was performed.
5
6 ## Executive decision
7
8 The current official retail product is **Breeze API by ICICI Securities / ICICI Direct**. It provides official HTTP documentation and Java, Python, and JavaScript SDKs. Verified reads include Customer Details, Demat Holdings, Portfolio Holdings, Portfolio Positions, Funds, Margin, Quotes, and instrument lookup/master data.
9
10 Use a narrowly allowlisted direct Java HTTP client in the future. Authentication is interactive and daily: the user logs into the official Breeze page with the AppKey, completes ICICI Direct credentials plus OTP, receives an `API_Session`, and exchanges it through Customer Details for the signed-request `session_token`. The session key expires 24 hours after generation or at midnight, whichever is earlier.
11
12 Live implementation remains blocked by two material gaps: no documented API logout/revocation operation; and Demat Holdings expose ISIN/quantity but no prices, while Portfolio Holdings expose prices but no ISIN/token. Joining by ticker alone is unsafe. Use ISIN as equity identity and never fabricate price/cost data.
13
14 ## A. Official API/product name
15
16 **Breeze API**, ICICI Direct's retail API and the ICICI Securities trading API named by the official SDKs. The portal indicates that an ICICI Direct account is required and links account opening.
17
18 ## B. Official sources consulted
19
20 - [Breeze HTTP API Reference](https://api.icicidirect.com/breezeapi/documents/index.html)
21 - [Breeze API portal](https://api.icicidirect.com/apiuser/home)
22 - [Official session-key article](https://www.icicidirect.com/futures-and-options/api/breeze/article/what-is-a-session-key-and-how-to-generate-it-for-using-breezeapi)
23 - [Official Breeze Java SDK](https://github.com/Idirect-Tech/Breeze-Java-SDK)
24 - [Official Breeze Python SDK](https://github.com/Idirect-Tech/Breeze-Python-SDK)
25 - [Official Python SDK examples](https://github.com/Idirect-Tech/Breeze-Python-SDK/blob/main/README.md)
26 - [Official Python SDK endpoint configuration](https://github.com/Idirect-Tech/Breeze-Python-SDK/blob/main/breeze_connect/config.py)
27
28 The older [ICICIDirect API document](https://api.icicidirect.com/apiuser/ICICIDirectAPIDOC.htm) was checked only for legacy differences. Current Breeze documentation/SDKs take precedence.
29
30 ## C. Authentication flow
31
32 The official reference calls the mechanism OAuth 2.0, but documents a Breeze-specific flow—not a standard authorization-code/token endpoint exchange. Do not assume generic Spring OAuth behavior.
33
34 1. The ICICI Direct customer logs into the Breeze portal and registers an app with app name and redirect URL.
35 2. Registration issues an **AppKey** and **secret_key** unique to the app.
36 3. Send the user's browser to `https://api.icicidirect.com/apiuser/login?api_key=<URL-ENCODED-APPKEY>`.
37 4. On the official page, the user enters ICICI Direct credentials, generates an OTP, and logs in. Our platform must never collect or automate those values.
38 5. Successful login exposes an **API_Session** in the resulting URL/page. Exact callback method and parameter encoding are **UNVERIFIED**.
39 6. Call Customer Details with JSON fields `SessionToken=<API_Session>` and `AppKey=<AppKey>`. The official reference specifies `GET /breezeapi/api/v1/customerdetails`, JSON content type, and no signed common headers.
40 7. Customer Details returns `Success.session_token`, used as `X-SessionToken` thereafter. The SDK decodes it internally into user ID/session key; direct REST uses the returned value.
41 8. For each subsequent v1 request, serialize the exact JSON body, create UTC ISO-8601 time with zero milliseconds (for example `2024-06-01T10:23:56.000Z`), and compute `SHA-256(timestamp + exactJsonBody + secret_key)`.
42 9. Send `Content-Type: application/json`, `X-Checksum: token <hex-digest>`, `X-Timestamp`, `X-AppKey`, and `X-SessionToken`. Client/server time must be within 60 seconds.
43
44 | Authentication item | Verification |
45 |---|---|
46 | AppKey, secret_key, registered redirect URL | VERIFIED |
47 | Browser login URL, ICICI credentials and OTP on official page | VERIFIED |
48 | API_Session and Customer Details exchange | VERIFIED |
49 | Signed headers and SHA-256 checksum | VERIFIED |
50 | Generic OAuth refresh token | UNVERIFIED; none documented |
51 | Programmatic credential/OTP authentication | UNVERIFIED and prohibited here |
52 | Exact callback method/parameter encoding | UNVERIFIED |
53
54 ## D. Session lifecycle
55
56 - Session key validity: **24 hours after generation or midnight, whichever is earlier**.
57 - A new key is required for the next trading day; the app need not be recreated but must be active.
58 - Known expiry/auth failures map to `SESSION_EXPIRED` and require interactive login.
59 - Refresh token/silent renewal: **UNVERIFIED / unsupported**.
60 - REST logout/revocation: **UNVERIFIED**. Disconnect must erase local secrets and must not claim server revocation.
61 - Whether a new session invalidates old sessions and permitted concurrency: **UNVERIFIED**.
62
63 ## E. Required credential/configuration fields
64
65 | Current field | Decision | Reason |
66 |---|---|---|
67 | `enabled` | KEEP | Local feature gate. |
68 | `baseUrl` | KEEP | Official v1 base is `https://api.icicidirect.com/breezeapi/api/v1/`; retain a test/config seam. |
69 | `clientId` | RENAME to `appKey` | Official term is AppKey. |
70 | `callbackUrl` | RENAME to `redirectUrl` | Official registration term. |
71 | `authMethod` | KEEP as fixed strategy selector | Use a local value such as `breeze-interactive-session`, not generic authorization-code OAuth. |
72 | `officialDocumentationVerified` | KEEP | Existing safety gate. |
73
74 ## F. Recommended future `ICICIDirectProviderProperties`
75
76 Future conceptual properties:
77
78 ```text
79 enabled
80 baseUrl
81 loginUrl
82 appKey
83 redirectUrl
84 authMethod = breeze-interactive-session
85 secretKeyReference
86 officialDocumentationVerified
87 ```
88
89 `secretKeyReference` is an **ADD** and resolves through a secret provider. Add `loginUrl` only as an allowlisted test seam. Do not add persisted API/session tokens, ICICI password, OTP, or speculative security ID. Runtime session material does not belong in provider properties.
90
91 ## G. Account contract
92
93 Verified operation: `GET /breezeapi/api/v1/customerdetails` with `{"SessionToken":"<API_Session>","AppKey":"<AppKey>"}`.
94
95 Verified response fields include `idirect_userid`, `idirect_user_name`, `idirect_lastlogin_time`, `segments_allowed`, exchange dates/status, and `session_token`.
96
97 Safe normalized mapping:
98
99 | `BrokerAccount` field | Mapping |
100 |---|---|
101 | `brokerAccountId` | Deterministic internal ID from provider + `idirect_userid`. |
102 | `userId` | Authenticated application user only. |
103 | `brokerType` | `ICICI_DIRECT`. |
104 | `externalAccountReference` | Masked `idirect_userid`; whether this is a trading-account number is **UNVERIFIED**. |
105 | `displayName` | `idirect_user_name`, with privacy controls. |
106 | `baseCurrency` | INR is implied by Funds' rupee semantics, but Customer Details has no currency field: **UNVERIFIED as an account attribute**. |
107 | `status` | Active only after current-session Customer Details succeeds. |
108
109 Account type, Demat/trading account number, multiple-account enumeration, and explicit base currency are **UNVERIFIED**. The endpoint describes the authenticated customer, not an account list.
110
111 ## H. Holdings contracts
112
113 ### Demat Holdings — preferred long-term ownership source
114
115 `GET /breezeapi/api/v1/dematholdings`, signed headers, exact `{}` body.
116
117 Verified fields: `stock_code`, `stock_ISIN`, `quantity`, `demat_total_bulk_quantity`, `demat_avail_quantity`, `blocked_quantity`, `demat_allocated_quantity`.
118
119 This explicitly represents Demat holdings and supplies ISIN. It does not supply average cost, price, value, or exchange. Use documented `quantity`; no official semantics justify a derived net formula from the other quantities. Preserve them as metadata when supported.
120
121 ### Portfolio Holdings — richer portfolio/valuation view
122
123 `GET /breezeapi/api/v1/portfolioholdings` with `exchange_code` (`NSE`/`NFO`), ISO dates, `stock_code`, and optional `portfolio_type`. The reference marks dates/stock code required while official SDK examples use blank stock code for all; complete-query semantics are **UNVERIFIED**.
124
125 Responses include stock/exchange code, quantity, average/current price, product and derivative fields, realized/unrealized P&L, and open-position value. Official SDK guidance says `NSE` for equity holdings. It returns no ISIN/token, so it must not independently create canonical equity positions. A ticker-only join to Demat Holdings is unsafe and **UNVERIFIED**.
126
127 ## I. Positions contract
128
129 `GET /breezeapi/api/v1/portfoliopositions`, signed headers, `{}` body.
130
131 Responses include segment/product/exchange/stock, derivative attributes, action, quantity, average price, LTP/price, settlement/margin, cover/stop-loss, MTF, pledge, P&L, and order-related fields.
132
133 This is distinct from Demat Holdings and represents trading positions, including derivative and potentially intraday/MTF state. It must not substitute for delivery holdings. Initial live scope should exclude derivatives and leveraged/intraday products until their identity/lifecycle are separately designed.
134
135 ## J. Funds/cash contract
136
137 Verified read: `GET /breezeapi/api/v1/funds`, signed headers, empty JSON body.
138
139 Verified fields: `bank_account`, `total_bank_balance`, segment allocations, per-segment trade blocks, `block_by_trade_balance`, and `unallocated_balance`. The write form documents amounts as rupees, supporting INR normalization. Do not invent arithmetic relationships.
140
141 Safe initial mapping: INR; reported cash from `total_bank_balance` with provider-label provenance; settled cash `null`; preserve allocations/blocks/unallocated separately rather than calling them available or settled cash. Never invoke `POST /funds` (`setFunds`). Official `GET /margin` fields are margin, not cash, and must not populate settled cash.
142
143 ## K. Stable instrument identity
144
145 For delivery equities:
146
147 ```text
148 provider = ICICI_DIRECT
149 externalInstrumentId = ISIN:<normalized stock_ISIN>
150 ```
151
152 `stock_ISIN` is an official Demat Holdings field and is a security identifier, unlike display ticker. Remove formatting whitespace and validate ISIN shape. Ticker alone is forbidden.
153
154 Official `get_names` maps exchange/stock code to `isec_stock_code` and `isec_token`; the master is updated daily at 08:00. Token stability across corporate actions/exchanges is **UNVERIFIED**. Portfolio responses expose neither ISIN nor token, and their safe mapping to Demat ISIN is **UNVERIFIED**. Reject/quarantine records without verified ISIN rather than invent identity. Canonical derivative identity is also **UNVERIFIED**.
155
156 The existing normalizer may structurally accept broker security ID or ISIN, but the future delivery-equity adapter should supply only verified ISIN unless ICICI documents token stability.
157
158 ## L. Exchange representation
159
160 The HTTP reference verifies `NSE` cash equity and `NFO` derivatives, and says BSE/MCX securities are currently unavailable. Current official SDK notes also list BSE/BFO, and Customer Details samples expose BSE/FNO/NDX status. Therefore:
161
162 - `NSE`: VERIFIED for cash equity.
163 - `NFO`: VERIFIED for NSE derivatives.
164 - BSE/BFO: **UNVERIFIED due to conflicting official sources**.
165 - MCX/NDX applicability: **UNVERIFIED for Phase 5D.3**.
166
167 Preserve upstream exchange codes and normalize only explicitly supported values; do not hard-code the broader SDK list yet.
168
169 ## M. Rate limits and operational rules
170
171 - REST limit: **100 calls/minute and 5,000 calls/day**.
172 - Timestamp skew: at most **60 seconds**.
173 - Session: **24 hours or midnight, whichever is earlier**.
174 - App must be active to generate a session.
175 - Session concurrency, login conflicts, read trading-hour restrictions, maintenance SLA, and throttling response/`Retry-After`: **UNVERIFIED**.
176 - Static IP is officially required for order requests. Its applicability to read-only calls is **UNVERIFIED**.
177
178 Enforce per-user/per-connection budgets beneath both limits. Do not retry authentication failures; only bounded/backoff retries for safe reads.
179
180 ## N. SDK vs direct HTTP recommendation
181
182 Choose **A. direct Java HTTP client**.
183
184 Direct REST is officially documented. The official Java SDK exists but is documented as a manually included JAR and exposes trading mutations alongside reads. A small allowlisted Spring client can expose only approved reads, avoids a Python sidecar, and simplifies Kubernetes deployment, observability, timeouts, connection pooling, and scoped sessions. The official Java SDK remains a contract oracle for signing/serialization tests.
185
186 ## O. Connector mapping
187
188 | Connector method | Verified upstream/design |
189 |---|---|
190 | `status(userId, connectionId)` | No status endpoint. Check scoped local expiry; optionally probe Customer Details within limits. Missing session → `AUTHENTICATION_REQUIRED`; known expiry/auth failure → `SESSION_EXPIRED`; transport/5xx → unavailable; success → `CONNECTED`. |
191 | `fetchAccounts(...)` | Customer Details; produce one current-customer account without fabricating type/number. |
192 | `fetchPositions(...)` | Initial delivery source is Demat Holdings. Portfolio Holdings may enrich only after safe identity joining; Portfolio Positions remains a distinct trading-position feed. |
193 | `fetchCashBalances(...)` | Funds GET; preserve semantic distinctions and never treat Margin as cash. |
194 | `disconnect(...)` | Erase scoped local API_Session/session token. Server logout is **UNVERIFIED**. |
195
196 All state remains keyed by `(authenticated application userId, broker connectionId)`. No singleton client may hold mutable session credentials.
197
198 ## P. Read-only capabilities
199
200 Advertise only after implementation and fixture tests:
201
202 | Capability | Safe decision |
203 |---|---|
204 | `ACCOUNTS_READ` | Supported via Customer Details, subject to one-current-customer limitation. |
205 | `ACCOUNT_METADATA_READ` | Supported for returned identity/name/segment/exchange metadata. |
206 | `POSITIONS_READ` | Supported only with explicit distinction between delivery Demat holdings and trading positions. |
207 | `CASH_READ` | Supported via Funds, without a settled-cash claim. |
208 | `PORTFOLIO_READ` | Enable only when ISIN holdings can be represented without fabricated price/cost; may require normalized-model adjustment or verified enrichment. |
209 | `ORDER_EXECUTION` | Always absent. |
210
211 Until documentation, configuration, authentication, connector implementation, and tests are satisfied, capabilities remain empty.
212
213 ## Q. Security/session storage
214
215 - No secrets/session values in Git, logs, errors, API responses, portfolio-service, or portfolio tables.
216 - Handle AppKey conservatively; resolve `secret_key` through `secretKeyReference`.
217 - Production: Kubernetes Secret; future Azure Key Vault via existing secret-provider abstraction; local: ignored environment/process configuration.
218 - Encrypt API_Session/session token at rest, keyed by `(userId, connectionId)`, with created/expiry timestamps. Erase on disconnect/expiry.
219 - Build login URLs only on the allowlisted official host and URL-encode AppKey.
220 - Correlate callback completion to authenticated user/connection with one-time state/nonce. Upstream callback details remain **UNVERIFIED**.
221 - Never receive/store/proxy ICICI password or OTP.
222 - Sign exact sent bytes and redact AppKey, API_Session, session token, secret, checksum, bank account, and customer identifiers.
223 - Require TLS validation, strict timeouts, response-size limits, rate limits, and host allowlists.
224
225 ## R. Consolidated UNVERIFIED items
226
227 1. Exact redirect callback method, parameter encoding, and error payload.
228 2. Refresh/silent renewal; REST logout/revocation.
229 3. Concurrent-session limits and web/mobile login interaction.
230 4. Whether `idirect_userid` is a formal trading-account number; account type, multiple accounts, explicit currency.
231 5. Demat quantity reconciliation beyond the reported `quantity`.
232 6. Safe Portfolio stock-code → Demat ISIN join; ISEC token/code stability.
233 7. Canonical derivative identity.
234 8. BSE/BFO read support due to conflicting official material; MCX/NDX Phase 5D.3 applicability.
235 9. Read-call static-IP requirement, trading-hour restrictions, maintenance/SLA, throttle response.
236 10. Whether `total_bank_balance` is withdrawable/available trading cash.
237 11. Whether blank filters/date ranges always yield the complete current equity Portfolio Holdings set.
238
239 Resolve these through updated official documentation or written clarification from `breezeapi@icicisecurities.com`, not experiments against a funded account.
240
241 ## S. Proposed Phase 5D.3
242
243 1. Obtain written clarification for callback, logout, account ID/currency, complete holdings, instrument/token stability, and BSE.
244 2. Capture sanitized official fixtures for success, empty, malformed, expired, throttled, and unavailable responses—no live calls in automated tests.
245 3. Rename properties and add secret references; never store runtime tokens as properties.
246 4. Implement allowlisted read-only Java HTTP signing with injected clock.
247 5. Implement interactive login correlation and encrypted scoped sessions.
248 6. Normalize Customer Details and Funds.
249 7. Implement Demat Holdings with ISIN-only identity; decide how to represent missing cost/price before portfolio import.
250 8. Keep Portfolio Holdings enrichment and Portfolio Positions under separate semantics/tests.
251 9. Add dual-window limits, midnight/24-hour expiry, redaction, isolation, and no-order tests.
252 10. Advertise capabilities one by one only after contract tests.
253
254 Out of scope: fund mutations, margin additions, order/trade mutations, order streams/webhooks, and all order execution.
255
256 ## T. Safety confirmation
257
258 - No live brokerage action or ICICI API request was performed.
259 - No order was placed and no production trading capability was enabled.
260 - No endpoint/schema/identity guarantee/auth behavior was invented; gaps are marked **UNVERIFIED**.
261 - IBKR code was not changed.
262 - Phase 5C portfolio persistence was not changed.
263 - No production code changed in Phase 5D.2; only this research document changed.