Developer Guide
An in-depth developer guide for Portfolio Connect SDK
Portfolio Connect is a drop-in UI widget that lets your users import their investment holdings from any Indian financial statement. Once the user completes the import, your app receives structured JSON data.
Live Demo: https://connect.casparser.in
Supported Statements
|
Statement |
Covers |
Import Methods |
|---|---|---|
|
CAMS / KFintech CAS |
All mutual fund holdings across AMCs |
PDF upload, Email request |
|
CDSL CAS |
Stocks, ETFs, Bonds, SGBs, REITs, InvITs, Demat MFs, AIFs |
PDF upload, OTP fetch |
|
NSDL CAS |
Stocks, ETFs, Bonds, SGBs, REITs, InvITs, Demat MFs, AIFs |
PDF upload |
Installation
npm / yarn
npm install @cas-parser/connect
# or
yarn add @cas-parser/connect
CDN (Vanilla JS / Angular / Vue)
Standalone Bundle (Recommended) — no extra dependencies:
<script src="https://cdn.jsdelivr.net/npm/@cas-parser/connect/dist/portfolio-connect.standalone.min.js"></script>
Lightweight Bundle — If you already have React 18+ on your page:
<script src="https://cdn.jsdelivr.net/npm/@cas-parser/connect/dist/portfolio-connect.umd.min.js"></script>
Bundle Sizes:
-
Standalone: ~54KB gzipped
-
UMD: ~9KB gzipped (requires React 18+)
Quick Start
React / Next.js
import { PortfolioConnect } from '@cas-parser/connect';
function App() {
return (
<PortfolioConnect
accessToken="your_access_token"
config={{
enableGenerator: true, // MF email fetch
enableCdslFetch: true, // CDSL OTP fetch
}}
onSuccess={(data, metadata) => {
console.log('Holdings:', data.holdings);
console.log('Total Value:', data.summary?.total_value);
console.log('Parser Type:', metadata.parser_type);
}}
onError={(error) => {
console.error('Import failed:', error.message);
}}
>
{({ open, isReady }) => (
<button onClick={open} disabled={!isReady}>
Import Investments
</button>
)}
</PortfolioConnect>
);
}
Vanilla JavaScript
<script src="https://cdn.jsdelivr.net/npm/@cas-parser/connect/dist/portfolio-connect.standalone.min.js"></script>
<button id="import-btn">Import Portfolio</button>
<script>
document.getElementById('import-btn').onclick = async () => {
try {
const { data, metadata } = await PortfolioConnect.open({
accessToken: 'your_access_token',
config: { enableCdslFetch: true }
});
console.log('Holdings:', data.holdings);
console.log('Type:', metadata.parser_type);
} catch (error) {
if (error.message === 'Widget closed by user') {
console.log('User cancelled');
} else {
console.error('Error:', error.message);
}
}
};
</script>
Vue 3
<template>
<button @click="openPortfolioConnect">Import Portfolio</button>
</template>
<script setup>
const openPortfolioConnect = async () => {
try {
const { data } = await PortfolioConnect.open({
accessToken: 'your_access_token',
config: { enableCdslFetch: true }
});
console.log('Portfolio:', data);
} catch (error) {
console.error(error);
}
};
</script>
Angular
// component.ts
import { Component } from '@angular/core';
declare const PortfolioConnect: any;
@Component({
selector: 'app-import',
template: `<button (click)="openWidget()">Import Portfolio</button>`
})
export class ImportComponent {
async openWidget() {
try {
const { data } = await PortfolioConnect.open({
accessToken: 'your_access_token',
config: { enableCdslFetch: true }
});
console.log('Portfolio:', data);
} catch (error) {
console.error(error);
}
}
}
Configuration
Full Configuration Example
<PortfolioConnect
accessToken="your_access_token"
config={{
// Statement types to allow
allowedTypes: ['CAMS_KFINTECH', 'CDSL', 'NSDL'],
// Features
enableGenerator: true, // MF statement via email (KFintech)
enableCdslFetch: true, // CDSL statement via OTP
// UI Options
showShortcuts: true, // Email search shortcuts (Gmail, Outlook)
showPortalLinks: true, // Links to CAS download portals
// Branding
logoUrl: 'https://yourapp.com/logo.png',
title: 'Import Your Investments',
subtitle: 'Mutual Funds, Stocks, Bonds — all in one place',
// Pre-fill user data (optional)
prefill: {
pan: 'ABCDE1234F',
email: 'user@example.com',
phone: '9876543210',
boId: '1234567890123456', // CDSL BO ID (16 digits)
dob: '1990-01-15', // Date of birth (YYYY-MM-DD)
},
// MF Generator options
generator: {
fromDate: '2020-01-01', // Statement start date
toDate: '2024-12-31', // Statement end date
},
// Custom broker list (optional)
brokers: [
{ name: 'Zerodha', depository: 'CDSL', logo: 'https://...' },
{ name: 'Groww', depository: 'CDSL', logo: 'https://...' },
],
}}
onSuccess={handleSuccess}
onError={handleError}
onEvent={handleEvent}
onExit={handleExit}
>
{({ open }) => <button onClick={open}>Import</button>}
</PortfolioConnect>
Props Reference
|
Prop |
Type |
Required |
Description |
|---|---|---|---|
|
|
|
Yes |
Your access token (get one) |
|
|
|
No |
Configuration options (see below) |
|
|
|
Yes |
Called when import succeeds |
|
|
|
No |
Called when import fails |
|
|
|
No |
Called when widget is closed |
|
|
|
No |
Called for analytics/tracking |
|
|
|
Yes |
Render prop for trigger button |
Config Options
|
Option |
Type |
Default |
Description |
|---|---|---|---|
|
|
|
All types |
Restrict to specific statement types |
|
|
|
|
Enable MF statement request via email |
|
|
|
|
Enable CDSL statement fetch via OTP |
|
|
|
|
Show email search shortcuts |
|
|
|
|
Show links to CAS download portals |
|
|
|
CASParser logo |
Your brand logo URL |
|
|
|
"Import Your Investments" |
Widget title |
|
|
|
"Mutual Funds, Stocks..." |
Widget subtitle |
|
|
|
- |
Pre-fill user details |
|
|
|
- |
MF generator date range options |
|
|
|
Default list |
Custom broker list |
Prefill Options
|
Field |
Type |
Description |
|---|---|---|
|
|
|
User's PAN (for CAMS/KFintech and CDSL) |
|
|
|
User's email address |
|
|
|
User's mobile number |
|
|
|
CDSL BO ID (16 digits) |
|
|
|
Date of birth (YYYY-MM-DD format) |
Response Data
Success Callback
onSuccess={(data, metadata) => {
// data: ParsedData
// metadata: { filename, parser_type, parse_duration_ms }
}}
ParsedData Structure
interface ParsedData {
cas_type: 'CAMS_KFINTECH' | 'CDSL' | 'NSDL';
status: 'success' | 'failed';
investor_info?: {
name: string;
email?: string;
mobile?: string;
pan?: string;
address?: string;
};
// For Mutual Funds (CAMS_KFINTECH)
folios?: Array<{
folio_number: string;
amc: string;
schemes: Array<{
scheme_name: string;
isin: string;
units: number;
nav: number;
value: number;
}>;
}>;
// For Demat (CDSL/NSDL)
holdings?: Array<{
isin: string;
name: string;
quantity: number;
value: number;
}>;
summary?: {
total_value: number;
as_on_date: string;
};
}
Full JSON schema: docs.casparser.in/reference
Events & Analytics
Track user journey with the onEvent callback:
onEvent={(event, metadata) => {
analytics.track(event, metadata);
}}
Available Events
|
Event |
Description |
|---|---|
|
|
Widget opened |
|
|
Widget closed |
|
|
User switched between Upload/Fetch modes |
|
|
User changed portfolio type (MF/CDSL/NSDL) |
|
|
User selected a broker |
|
|
User clicked email search shortcut |
|
|
User clicked portal link |
|
|
User selected a file |
|
|
User removed selected file |
|
|
File upload began |
|
|
Upload progress update |
|
|
Parsing started |
|
|
Parsing completed successfully |
|
|
Parsing failed |
|
|
MF email request initiated |
|
|
MF email request sent |
|
|
MF email request failed |
|
|
CDSL OTP flow initiated |
|
|
CDSL OTP sent to user |
|
|
CDSL OTP verified |
|
|
CDSL holdings fetched |
|
|
CDSL fetch failed |
Error Handling
Error Object
interface PortfolioConnectError {
code: string;
message: string;
details?: any;
}
Common Error Codes
|
Code |
Description |
|---|---|
|
|
Wrong PDF password |
|
|
Failed to parse the PDF |
|
|
PDF format not supported |
|
|
Network connectivity issue |
|
|
CDSL OTP expired |
|
|
Invalid OTP entered |
|
|
MF email request failed |
Error Handling Example
onError={(error) => {
switch (error.code) {
case 'INVALID_PASSWORD':
showToast('Incorrect password. Try your PAN.');
break;
case 'OTP_EXPIRED':
showToast('OTP expired. Please try again.');
break;
default:
showToast(error.message);
}
}}
Framework Support
|
Framework |
Support |
Method |
|---|---|---|
|
React |
✅ Native |
npm package |
|
Next.js |
✅ Native |
npm package |
|
Angular |
✅ Via CDN |
Standalone bundle |
|
Vue |
✅ Via CDN |
Standalone bundle |
|
Svelte |
✅ Via CDN |
Standalone bundle |
|
Vanilla JS |
✅ Via CDN |
Standalone bundle |
|
React Native |
✅ WebView |
See below |
|
Flutter |
✅ WebView |
See below |
React Native / Flutter
Load the widget in a WebView pointing to a hosted HTML page:
<!-- host this page and load in WebView -->
<!DOCTYPE html>
<html>
<head>
<script src="https://cdn.jsdelivr.net/npm/@cas-parser/connect/dist/portfolio-connect.standalone.min.js"></script>
</head>
<body>
<script>
// Listen for messages from native app
window.addEventListener('message', async (e) => {
if (e.data.action === 'open') {
try {
const result = await PortfolioConnect.open({
accessToken: e.data.accessToken,
config: e.data.config
});
// Send result back to native app
window.ReactNativeWebView?.postMessage(JSON.stringify({ type: 'success', data: result }));
} catch (error) {
window.ReactNativeWebView?.postMessage(JSON.stringify({ type: 'error', error }));
}
}
});
</script>
</body>
</html>
Security
|
Feature |
Details |
|---|---|
|
Data Hosting |
100% India-hosted infrastructure |
|
Encryption |
TLS 1.3 in transit, AES-256 at rest |
|
Compliance |
SOC 2 Type II, ISO 27001 |
|
Data Retention |
PDFs deleted immediately after parsing |
|
Credentials |
We never store user passwords |
Access Tokens
For frontend use, generate short-lived access tokens (max 60 minutes) from your backend:
// Your backend
const token = await casparserAPI.createAccessToken({
expiresIn: '60m'
});
// Send to frontend
res.json({ accessToken: token });
Troubleshooting
Widget not opening?
-
Check that
accessTokenis valid -
Ensure the CDN script loaded (check console for errors)
-
Verify
isReadyistruebefore allowing button clicks
PDF parsing fails?
-
Ensure PDF is a valid CAS statement (CAMS, KFintech, CDSL, or NSDL)
-
Check if password is correct (try PAN in uppercase)
-
For encrypted PDFs, password is required
CDSL OTP not received?
-
Verify BO ID is exactly 16 digits
-
Check mobile number is registered with CDSL
-
OTP expires in 3 minutes — retry if expired
Live Demo
Try the widget: connect.casparser.in
Support
-
Documentation: docs.casparser.in
-
API Reference: docs.casparser.in/reference
-
Email: sameer@casparser.in
-
GitHub: CASParser/cas-connect
Changelog
v1.1.0
-
Added standalone bundle (no React dependency needed)
-
Added broker selection UI
-
Added
brokersconfig option -
Added
phoneanddobprefill options -
Improved error handling
v1.0.0
-
Initial release
-
React component + UMD bundle
-
CAMS/KFintech, CDSL, NSDL support
-
PDF upload, MF generator, CDSL OTP fetch