Skip to main content

Props Reference

PropTypeRequiredDescription
accessTokenstringYesAccess token from /v1/token (starts with at_)
configobjectNoFeature configuration
prefillobjectNoPre-fill form fields
onSuccessfunctionNoCalled when import completes
onErrorfunctionNoCalled on error
onClosefunctionNoCalled when modal closes

Config Options

interface Config {
  // Enable/disable import methods
  enableUpload?: boolean;      // PDF upload (default: true)
  enableGenerator?: boolean;   // KFintech email request (default: false)
  enableCdslFetch?: boolean;   // CDSL OTP fetch (default: false)
  enableInbox?: boolean;       // Gmail inbox import (default: false)
  
  // UI customization
  primaryColor?: string;       // Hex color (default: #2563EB)
  logo?: string;               // URL to your logo
  title?: string;              // Modal title
  
  // Behavior
  closeOnSuccess?: boolean;    // Auto-close on success (default: false)
  showSummary?: boolean;       // Show summary after parse (default: true)
}

Prefill Options

interface Prefill {
  email?: string;              // For KFintech generator
  pan?: string;                // For CDSL fetch
  dob?: string;                // For CDSL fetch (YYYY-MM-DD)
}

Event Callbacks

onSuccess

interface SuccessPayload {
  data: ParsedData;            // Parsed portfolio data
  metadata: {
    method: 'upload' | 'generator' | 'cdsl_fetch' | 'inbox';
    cas_type: 'cdsl' | 'nsdl' | 'cams_kfintech';
    processing_time_ms: number;
  };
}

onSuccess={(payload) => {
  console.log(`Total value: ₹${payload.data.summary.total_value}`);
  console.log(`Method: ${payload.metadata.method}`);
}}

onError

interface ErrorPayload {
  code: string;
  message: string;
  details?: object;
}

onError={(error) => {
  if (error.code === 'INVALID_PASSWORD') {
    alert('Wrong password. Check your CAS email for the correct password.');
  }
}}

onClose

onClose={({ completed }) => {
  if (!completed) {
    console.log('User cancelled import');
  }
}}

Error Codes

CodeDescriptionUser Action
INVALID_PASSWORDWrong PDF passwordRe-enter correct password
INVALID_PDFCorrupted or scanned PDFUse original digital PDF
UNSUPPORTED_FORMATNot a CAS fileUpload CAS, not bank statement
TOKEN_EXPIREDAccess token expiredGenerate new token
RATE_LIMITEDToo many requestsWait and retry
INSUFFICIENT_CREDITSNo credits remainingUpgrade plan

Framework Examples

React

import { PortfolioConnect } from '@cas-parser/connect';

function ImportButton() {
  const [accessToken, setAccessToken] = useState(null);
  
  useEffect(() => {
    // Fetch token from your backend
    fetch('/api/casparser/token')
      .then(r => r.json())
      .then(d => setAccessToken(d.access_token));
  }, []);
  
  if (!accessToken) return <button disabled>Loading...</button>;
  
  return (
    <PortfolioConnect
      accessToken={accessToken}
      config={{
        enableCdslFetch: true,
        enableInbox: true,
        primaryColor: '#10B981'
      }}
      onSuccess={({ data }) => {
        // Save to your backend
        fetch('/api/portfolio', {
          method: 'POST',
          body: JSON.stringify(data)
        });
      }}
    >
      {({ open }) => (
        <button onClick={open}>
          Import Portfolio
        </button>
      )}
    </PortfolioConnect>
  );
}

Next.js (App Router)

'use client';

import { PortfolioConnect } from '@cas-parser/connect';
import { useState } from 'react';

export function ImportWidget({ accessToken }: { accessToken: string }) {
  const [data, setData] = useState(null);
  
  return (
    <>
      <PortfolioConnect
        accessToken={accessToken}
        onSuccess={({ data }) => setData(data)}
      >
        {({ open }) => <button onClick={open}>Import</button>}
      </PortfolioConnect>
      
      {data && (
        <div>
          <h2>Portfolio Value: ₹{data.summary.total_value.toLocaleString()}</h2>
        </div>
      )}
    </>
  );
}

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>
const accessToken = 'at_xxx'; // From your backend

document.getElementById('import-btn').onclick = async () => {
  try {
    const { data, metadata } = await PortfolioConnect.open({
      accessToken,
      config: {
        enableCdslFetch: true,
        primaryColor: '#8B5CF6'
      }
    });
    
    console.log('Success:', data.summary.total_value);
    
  } catch (error) {
    if (error.message === 'Widget closed by user') {
      console.log('Cancelled');
    } else {
      console.error('Error:', error);
    }
  }
};
</script>

Vue 3

<template>
  <button @click="openWidget">Import Portfolio</button>
</template>

<script setup>
import { ref, onMounted } from 'vue';

const accessToken = ref(null);

onMounted(async () => {
  const res = await fetch('/api/casparser/token');
  const data = await res.json();
  accessToken.value = data.access_token;
});

const openWidget = async () => {
  const { PortfolioConnect } = await import('@cas-parser/connect');
  
  try {
    const { data } = await PortfolioConnect.open({
      accessToken: accessToken.value,
      config: { enableCdslFetch: true }
    });
    
    console.log(data);
  } catch (e) {
    console.error(e);
  }
};
</script>

Access Token Generation

Never expose your API key to the frontend. Generate short-lived access tokens server-side.
# Backend endpoint
from flask import Flask, jsonify
import requests

app = Flask(__name__)

@app.route('/api/casparser/token')
def get_token():
    response = requests.post(
        'https://api.casparser.in/v1/token',
        headers={'x-api-key': os.environ['CASPARSER_API_KEY']},
        json={'expiry_minutes': 30}
    )
    return jsonify(response.json())
Access tokens:
  • Prefix: at_
  • Max TTL: 60 minutes
  • Can be used on all /v4/* endpoints
  • Cannot generate other tokens

Styling

The widget uses your primaryColor for buttons and accents. For deeper customization, use CSS variables:
:root {
  --pc-primary: #2563EB;
  --pc-background: #FFFFFF;
  --pc-text: #1F2937;
  --pc-border-radius: 8px;
}

Next steps