Documentation Index Fetch the complete documentation index at: https://casparser.in/docs/llms.txt
Use this file to discover all available pages before exploring further.
This page covers @cas-parser/connect v2.x. For the underlying APIs the widget calls, see the API Reference .
Authentication
Never expose your raw API key to the browser. Mint a short-lived access token (at_*) on your backend via POST /v1/token and pass that to the widget instead.
The widget accepts either credential — both are sent as the x-api-key header. At least one of accessToken / apiKey is required.
# Backend — Python (Flask)
import os, requests
from flask import jsonify
@app.route ( '/api/casparser/token' )
def get_token ():
res = requests.post(
'https://api.casparser.in/v1/token' ,
headers = { 'x-api-key' : os.environ[ 'CASPARSER_API_KEY' ]},
json = { 'expiry_minutes' : 30 },
)
return jsonify(res.json())
Access tokens:
Prefix: at_
TTL: 1–60 minutes (default: 60)
Drop-in replacement for x-api-key on /v4/* endpoints
Cannot mint other tokens
Props
Prop Type Required Description accessTokenstringOne of these two Short-lived token from POST /v1/token. Recommended for browser embeds. apiKeystringOne of these two Raw API key. For server-side or trusted-environment use only. apiBaseUrlstringNo Override API base URL (for dedicated / self-hosted instances). Defaults to https://api.casparser.in. configPortfolioConnectConfigNo Feature flags, branding, prefill, theme, etc. See Config . onSuccess(data, metadata) => voidYes Called when an import completes successfully. onError(error) => voidNo Called on any structured error. onExit() => voidNo Called when the widget closes (regardless of success/cancel). onEvent(event, metadata) => voidNo Fires for every widget event. Useful for analytics. onSubmit(input, password, onProgress?) => Promise<any>No v2.1+ — replaces the default parse API call across all intercept-capable flows. See onSubmit .children(renderProps) => ReactNodeYes (React)Render-prop. Receives { open, isReady, isOpen }.
Config
interface PortfolioConnectConfig {
// Import methods
enableGenerator ?: boolean ; // MF statement via email (default: false)
enableCdslFetch ?: boolean ; // CDSL OTP fetch (default: false)
enableInbox ?: boolean ; // Gmail OAuth import (default: false)
enableInboundEmail ?: boolean ; // Forward-email flow (default: false)
// Restrict portfolio types surfaced in the UI
allowedTypes ?: ( 'CAMS_KFINTECH' | 'CDSL' | 'NSDL' )[];
// Home-screen layout
homeLayout ?: 'actions' | 'asset-type' | 'unified' ;
// 'actions' (default) — three action cards: File / Email / Fetch
// 'asset-type' — two tiles: Mutual Funds / Stocks & Demat (advisor-friendly)
// 'unified' — single drop zone with secondary chips below
// Demat broker picker
showBrokerPicker ?: boolean ; // default: true
brokers ?: BrokerInfo []; // override the default broker list
showShortcuts ?: boolean ; // email-search shortcuts (default: true)
showPortalLinks ?: boolean ; // links to CAS generation portals (default: true)
// Pre-fill user details (used across CDSL OTP, MF email, inbound forms)
prefill ?: {
pan ?: string ;
email ?: string ;
phone ?: string ;
boId ?: string ; // CDSL BO ID (16 digits)
dob ?: string ; // YYYY-MM-DD
};
// Sub-flow configs
generator ?: {
fromDate ?: string ; // YYYY-MM-DD (default: 2000-01-01)
toDate ?: string ; // YYYY-MM-DD (default: today)
password ?: string ; // PDF password (default: 'Abcdefghi12$')
};
inbox ?: {
redirectUri : string ; // REQUIRED — OAuth callback (must call SDK's handleInboxCallback)
casTypes ?: ( 'cdsl' | 'nsdl' | 'cams' | 'kfintech' )[];
startDate ?: string ; // YYYY-MM-DD (default: 30 days ago)
endDate ?: string ; // YYYY-MM-DD (default: today)
};
inboundEmail ?: {
callbackUrl ?: string ; // optional webhook; SDK still receives the file in-widget
existingId ?: string ; // inject an existing inbound email — SDK won't create or delete
email ?: string ; // pair with existingId to skip a network call
allowedSources ?: ( 'cdsl' | 'nsdl' | 'cams' | 'kfintech' )[];
reference ?: string ; // your internal user/account ID
metadata ?: Record < string , string >; // max 10 keys
pollIntervalMs ?: number ; // default: 3000 (3s)
sessionTimeoutMs ?: number ; // default: 1800000 (30 min, matches backend TTL)
};
// Branding
logoUrl ?: string ;
title ?: string ; // default: "Import Your Investments"
subtitle ?: string ; // default: "Mutual Funds, Stocks, Bonds — all in one place"
// Theme — emits CSS variables on the modal root
theme ?: {
mode ?: 'light' | 'dark' | 'auto' ;
primary ?: string ;
primaryHover ?: string ;
primaryForeground ?: string ;
accent ?: string ;
radius ?: number ;
fontFamily ?: string ;
};
// Success screen
successBehavior ?: 'summary' | 'close' ; // default: 'close'
successAutoCloseMs ?: number ; // default: 10000 (10s) — used when behavior === 'summary'
successCta ?: {
label : string ;
onClick : () => void ;
};
// Misc
hideFooter ?: boolean ; // default: false. Please keep visible unless co-branded.
}
Inbound email config
When enableInboundEmail: true, the widget creates a one-time forwarding address via POST /v4/inbound-email, polls GET /files, and parses the first file it receives. This works without a callback_url.
You can also pass an existingId (and optionally email) to inject an inbound email you’ve already created on your backend — useful when you want a stable address per user, or when you’re driving the API yourself and only need the SDK for the polling/UI.
The callbackUrl here is the underlying API’s callback_url — set it if you want both webhook delivery to your backend and in-widget reception. Omit for a pure-frontend integration.
Events
onSuccess
onSuccess : ( data : ParsedData , metadata : ParseMetadata ) => void ;
interface ParsedData {
cas_type : 'CAMS_KFINTECH' | 'CDSL' | 'NSDL' ;
status : 'success' | 'failed' ;
investor_info ?: { name : string ; email ?: string ; mobile ?: string ; pan ?: string ; address ?: string };
folios ?: any [];
holdings ?: any [];
summary ?: { total_value : number ; as_on_date : string };
raw_response ?: any ; // full API response
}
interface ParseMetadata {
filename : string ;
parser_type : 'CAMS_KFINTECH' | 'CDSL' | 'NSDL' ;
parse_duration_ms : number ;
}
Fires once per successful import. The signature is two arguments , not a single payload object.
onError
onError : ( error : PortfolioConnectError ) => void ;
interface PortfolioConnectError {
code : string ; // see codes below; type widened to string for forward compat
message : string ;
title ?: string ; // short headline suitable for a banner
remediation ?: string ; // what the user can try next
retryable ?: boolean ;
details ?: any ; // raw API payload when available
}
Error codes the SDK emits:
Code When it fires INVALID_PASSWORDAPI response message explicitly mentions “password” AUTHENTICATIONHTTP 401/403 RATE_LIMITEDHTTP 429 NETWORKNetwork failure / timeout PARSE_ERROR/v4/*/parse endpoint failedGENERATOR_ERRORKFintech mailback failed CDSL_FETCH_ERRORCDSL OTP / fetch flow failed INBOX_ERRORGmail inbox flow failed INBOUND_EMAIL_ERRORInbound email provisioning / polling failed UNKNOWNCatch-all
The type is widened to | string, so future codes won’t break exhaustive switches.
onExit
Fires whenever the widget closes — after success, after error, or on user cancel. No argument is passed; combine with state set in onSuccess / onError if you need to distinguish.
onEvent
onEvent : ( event : PortfolioConnectEvent | string , metadata : EventMetadata ) => void ;
Fires for every widget event. Useful for analytics integrations. Selected events:
Lifecycle : WIDGET_OPENED, WIDGET_CLOSED, MODE_SWITCHED, ASSET_SELECTED, BROKER_SELECTED
Upload : FILE_SELECTED, FILE_REMOVED, UPLOAD_STARTED, UPLOAD_PROGRESS, PARSE_STARTED, PARSE_SUCCESS, PARSE_ERROR
Generator : GENERATOR_STARTED, GENERATOR_SUCCESS, GENERATOR_ERROR
CDSL fetch : CDSL_FETCH_STARTED, CDSL_OTP_SENT, CDSL_OTP_VERIFIED, CDSL_FETCH_SUCCESS, CDSL_FETCH_ERROR
Gmail inbox : INBOX_CONNECT_STARTED, INBOX_CONNECTED, INBOX_FILES_LOADED, INBOX_FILE_SELECTED, INBOX_DISCONNECTED, INBOX_ERROR
Inbound email : INBOUND_EMAIL_CREATED, INBOUND_EMAIL_COPIED, INBOUND_EMAIL_POLLING, INBOUND_EMAIL_FILE_RECEIVED, INBOUND_EMAIL_TIMEOUT, INBOUND_EMAIL_ERROR
Cross-flow handoffs — measure how often the widget’s nudges convert: GENERATOR_TO_INBOUND_HANDOFF, INBOX_TO_GENERATOR_HANDOFF, INBOUND_TO_UPLOAD_HANDOFF, UPLOAD_TO_INBOUND_HANDOFF
The metadata object always includes timestamp and may include event-specific fields.
onSubmit — collect PDFs without parsing
v2.1+. When onSubmit is provided, the SDK routes every intercept-capable flow through it instead of calling the parse API itself. Useful for:
Forwarding PDFs to your own backend without burning parse credits
Custom retry / queueing logic
Storing a copy of the PDF before parsing
type SubmitInput =
| { kind : 'file' ; file : File ; filename : string ; source : 'UPLOAD' }
| {
kind : 'url' ;
pdfUrl : string ; // presigned, valid 24–48h
filename : string ;
source : 'INBOX' | 'INBOUND_EMAIL' | 'CDSL_FETCH' ;
metadata ?: Record < string , unknown >; // cas_type, message_id, received_at, etc.
};
onSubmit = { async ( input , password , onProgress ) => {
if (input.kind === 'file' ) {
// User uploaded the PDF directly
const form = new FormData ();
form . append ( 'file' , input . file );
form . append ( 'password' , password );
return fetch ( '/api/portfolio/upload' , { method: 'POST' , body: form }). then ( r => r . json ());
}
// input.kind === 'url' — fetched from inbox / inbound email / CDSL
return fetch ( '/api/portfolio/from-url' , {
method : 'POST' ,
body : JSON . stringify ({
pdfUrl: input . pdfUrl ,
password ,
source: input . source ,
}),
}). then ( r => r . json ());
}}
The MF generator (KFintech mailback) flow stays out of scope — the CAS is delivered to the investor’s email out of band, so the SDK never sees a PDF.
The return value is forwarded to onSuccess. Throw to trigger onError.
Framework integration
React / Next.js
import { PortfolioConnect } from '@cas-parser/connect' ;
function ImportButton ({ accessToken } : { accessToken : string }) {
return (
< PortfolioConnect
accessToken = { accessToken }
config = { {
enableCdslFetch: true ,
enableInboundEmail: true ,
homeLayout: 'asset-type' ,
theme: { primary: '#10B981' , mode: 'auto' },
} }
onSuccess = { ( data , metadata ) => {
console . log ( `Total: ₹ ${ data . summary ?. total_value } ( ${ metadata . parser_type } )` );
} }
onError = { ( err ) => console . error ( err . code , err . message ) }
onExit = { () => console . log ( 'widget closed' ) }
>
{ ({ open , isReady }) => (
< button onClick = { open } disabled = { ! isReady } >
Import Portfolio
</ button >
) }
</ PortfolioConnect >
);
}
For Next.js App Router , mark the parent component 'use client' and fetch the access token from a server route.
Vanilla JS / Angular / Vue (Imperative API)
The standalone bundle ships its own React copy — no host-page React required.
< script src = "https://unpkg.com/@cas-parser/connect/dist/portfolio-connect.standalone.min.js" ></ script >
< button id = "import-btn" > Import Portfolio </ button >
< script >
document . getElementById ( 'import-btn' ). onclick = async () => {
// open() resolves with a discriminated result — never rejects on close.
const result = await PortfolioConnect . open ({
accessToken: 'at_xxxxx' ,
config: { enableCdslFetch: true },
});
if ( result . status === 'success' ) {
console . log ( 'Portfolio:' , result . data );
} else if ( result . status === 'closed' ) {
console . log ( 'User cancelled' );
} else {
console . error ( 'Error:' , result . error );
}
};
</ script >
If your page already has React 18+, use the lighter UMD bundle:
< script src = "https://unpkg.com/react@18/umd/react.production.min.js" ></ script >
< script src = "https://unpkg.com/react-dom@18/umd/react-dom.production.min.js" ></ script >
< script src = "https://unpkg.com/@cas-parser/connect/dist/portfolio-connect.umd.min.js" ></ script >
Bundle sizes (gzipped):
Standalone: ~71 KB (includes React)
UMD: ~27 KB (requires React 18+ on the page)
The imperative open() accepts the same accessToken / apiKey / apiBaseUrl / config / onSubmit / onEvent as the React props, plus an optional onOpen lifecycle hook. The success / error / cancel callbacks are intentionally dropped — the returned OpenResult covers all three outcomes.
If you need manual control (open the widget later, dispose without opening), use PortfolioConnect.create(config) which returns a { open, destroy } handle.
React Native / Flutter
Both are supported via WebView. See the @cas-parser/connect examples for working setups.
Theming
The recommended way to brand the widget is the config.theme object — it emits CSS variables on the modal root and derives the full palette (hover, soft, surface, border, text) from your primary colour automatically.
config = {{
theme : {
mode : 'auto' , // 'light' | 'dark' | 'auto' (follows prefers-color-scheme)
primary : '#10B981' ,
primaryHover : '#059669' , // optional — derived if omitted
primaryForeground : '#FFFFFF' ,
accent : '#F59E0B' ,
radius : 12 , // base radius in px; sm/md/lg/xl/pill are derived
fontFamily : 'Inter, system-ui, sans-serif' ,
},
}}
CSS variables
If you need to override individual tokens beyond what theme exposes, set the CSS variables directly. The full list emitted on the modal root:
/* Primary palette */
--pc-color-primary
--pc-color-primary-hover
--pc-color-primary-foreground
--pc-color-primary-soft /* low-alpha tint of primary */
--pc-color-primary-soft-border
--pc-color-accent
/* Surfaces & borders (driven by mode) */
--pc-color-surface, --pc-color-surface-subtle, --pc-color-surface-muted, --pc-color-surface-strong
--pc-color-border, --pc-color-border-strong
/* Text */
--pc-color-text, --pc-color-text-muted, --pc-color-text-faded
/* Status */
--pc-color-success, --pc-color-success-bg, --pc-color-success-border
--pc-color-error, --pc-color-error-bg, --pc-color-error-border
--pc-color-warning, --pc-color-warning-bg, --pc-color-warning-border
/* Geometry & font */
--pc-radius-sm, --pc-radius-md, --pc-radius-lg, --pc-radius-xl, --pc-radius-pill
--pc-font-family
--pc-shadow-card, --pc-shadow-button
theme.mode: 'auto' follows the user’s prefers-color-scheme media query.
Next steps
Inbound Email Guide Forward-email flow used by enableInboundEmail
API Reference Endpoints the widget calls under the hood