diff --git a/.release-please-manifest.json b/.release-please-manifest.json
index 2aca35a..d04f223 100644
--- a/.release-please-manifest.json
+++ b/.release-please-manifest.json
@@ -1,3 +1,3 @@
{
- ".": "0.5.0"
+ ".": "0.5.1"
}
\ No newline at end of file
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 22156c8..1e63704 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,16 @@
# Changelog
+## 0.5.1 (2026-03-03)
+
+Full Changelog: [v0.5.0...v0.5.1](https://github.com/CASParser/cas-parser-java/compare/v0.5.0...v0.5.1)
+
+### Chores
+
+* drop apache dependency ([2ea7ec5](https://github.com/CASParser/cas-parser-java/commit/2ea7ec51ef82bacadd0df76d7886e7c52e1ab902))
+* **internal:** codegen related update ([e8504a7](https://github.com/CASParser/cas-parser-java/commit/e8504a766d553e1c253e504df23c21f99d89c5da))
+* **internal:** expand imports ([fdca7cd](https://github.com/CASParser/cas-parser-java/commit/fdca7cd3a6a27f31b5b163b456eccf5c956db18f))
+* make `Properties` more resilient to `null` ([72f0c67](https://github.com/CASParser/cas-parser-java/commit/72f0c67cb321fb2ee5832c9c3f775d0b82353a0d))
+
## 0.5.0 (2026-02-23)
Full Changelog: [v0.4.0...v0.5.0](https://github.com/CASParser/cas-parser-java/compare/v0.4.0...v0.5.0)
diff --git a/README.md b/README.md
index bcad7a7..1e5410a 100644
--- a/README.md
+++ b/README.md
@@ -2,8 +2,8 @@
-[](https://central.sonatype.com/artifact/com.cas_parser.api/cas-parser-java/0.5.0)
-[](https://javadoc.io/doc/com.cas_parser.api/cas-parser-java/0.5.0)
+[](https://central.sonatype.com/artifact/com.cas_parser.api/cas-parser-java/0.5.1)
+[](https://javadoc.io/doc/com.cas_parser.api/cas-parser-java/0.5.1)
@@ -22,7 +22,7 @@ Use the Cas Parser MCP Server to enable AI assistants to interact with this API,
-The REST API documentation can be found on [casparser.in](https://casparser.in/docs). Javadocs are available on [javadoc.io](https://javadoc.io/doc/com.cas_parser.api/cas-parser-java/0.5.0).
+The REST API documentation can be found on [casparser.in](https://casparser.in/docs). Javadocs are available on [javadoc.io](https://javadoc.io/doc/com.cas_parser.api/cas-parser-java/0.5.1).
@@ -33,7 +33,7 @@ The REST API documentation can be found on [casparser.in](https://casparser.in/d
### Gradle
```kotlin
-implementation("com.cas_parser.api:cas-parser-java:0.5.0")
+implementation("com.cas_parser.api:cas-parser-java:0.5.1")
```
### Maven
@@ -42,7 +42,7 @@ implementation("com.cas_parser.api:cas-parser-java:0.5.0")
com.cas_parser.api
cas-parser-java
- 0.5.0
+ 0.5.1
```
diff --git a/build.gradle.kts b/build.gradle.kts
index 4aa1227..cc1e16d 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -9,7 +9,7 @@ repositories {
allprojects {
group = "com.cas_parser.api"
- version = "0.5.0" // x-release-please-version
+ version = "0.5.1" // x-release-please-version
}
subprojects {
diff --git a/cas-parser-java-core/build.gradle.kts b/cas-parser-java-core/build.gradle.kts
index 5c40ab2..ccacdad 100644
--- a/cas-parser-java-core/build.gradle.kts
+++ b/cas-parser-java-core/build.gradle.kts
@@ -27,8 +27,6 @@ dependencies {
implementation("com.fasterxml.jackson.datatype:jackson-datatype-jdk8:2.18.2")
implementation("com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.18.2")
implementation("com.fasterxml.jackson.module:jackson-module-kotlin:2.18.2")
- implementation("org.apache.httpcomponents.core5:httpcore5:5.2.4")
- implementation("org.apache.httpcomponents.client5:httpclient5:5.3.1")
testImplementation(kotlin("test"))
testImplementation(project(":cas-parser-java-client-okhttp"))
diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/client/CasParserClient.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/client/CasParserClient.kt
index df928cf..6c17b45 100644
--- a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/client/CasParserClient.kt
+++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/client/CasParserClient.kt
@@ -53,28 +53,97 @@ interface CasParserClient {
*/
fun withOptions(modifier: Consumer): CasParserClient
+ /**
+ * Endpoints for checking API quota and credits usage. These endpoints help you monitor your API
+ * usage and remaining quota.
+ */
fun credits(): CreditService
+ /**
+ * Endpoints for checking API quota and credits usage. These endpoints help you monitor your API
+ * usage and remaining quota.
+ */
fun logs(): LogService
+ /**
+ * Endpoints for managing access tokens for the Portfolio Connect SDK. Use these to generate
+ * short-lived `at_` prefixed tokens that can be safely passed to frontend applications. Access
+ * tokens can be used in place of API keys on all v4 endpoints.
+ */
fun accessToken(): AccessTokenService
+ /**
+ * Endpoints for managing access tokens for the Portfolio Connect SDK. Use these to generate
+ * short-lived `at_` prefixed tokens that can be safely passed to frontend applications. Access
+ * tokens can be used in place of API keys on all v4 endpoints.
+ */
fun verifyToken(): VerifyTokenService
+ /** Endpoints for parsing CAS PDF files from different sources. */
fun camsKfintech(): CamsKfintechService
+ /** Endpoints for parsing CAS PDF files from different sources. */
fun cdsl(): CdslService
+ /**
+ * Endpoints for parsing Contract Note PDF files from various SEBI brokers like Zerodha, Groww,
+ * Upstox, ICICI etc.
+ */
fun contractNote(): ContractNoteService
+ /**
+ * Endpoints for importing CAS files directly from user email inboxes.
+ *
+ * **Supported Providers:** Gmail (more coming soon)
+ *
+ * **How it works:**
+ * 1. Call `POST /v4/inbox/connect` to get an OAuth URL
+ * 2. Redirect user to the OAuth URL for consent
+ * 3. User is redirected back to your `redirect_uri` with an encrypted `inbox_token`
+ * 4. Use the token to list/fetch CAS files from their inbox (`/v4/inbox/cas`)
+ * 5. Files are uploaded to temporary cloud storage (URLs expire in 24 hours)
+ *
+ * **Security:**
+ * - Read-only access (we cannot send emails)
+ * - Tokens are encrypted with server-side secret
+ * - User can revoke access anytime via `/v4/inbox/disconnect`
+ */
fun inbox(): InboxService
+ /** Endpoints for generating new CAS documents via email mailback (KFintech). */
fun kfintech(): KfintechService
+ /** Endpoints for parsing CAS PDF files from different sources. */
fun nsdl(): NsdlService
+ /** Endpoints for parsing CAS PDF files from different sources. */
fun smart(): SmartService
+ /**
+ * Create dedicated inbound email addresses for investors to forward their CAS statements.
+ *
+ * **Use Case:** Your app wants to collect CAS statements from users without requiring OAuth or
+ * file upload.
+ *
+ * **How it works:**
+ * 1. Call `POST /v4/inbound-email` to create a unique inbound email address
+ * 2. Display this email to your user: "Forward your CAS statement to
+ * ie_xxx@import.casparser.in"
+ * 3. When user forwards a CAS email, we verify sender authenticity (SPF/DKIM) and call your
+ * webhook
+ * 4. Your webhook receives email metadata + attachment download URLs
+ *
+ * **Sender Validation:**
+ * - Only emails from verified CAS authorities are processed:
+ * - CDSL: `eCAS@cdslstatement.com`
+ * - NSDL: `NSDL-CAS@nsdl.co.in`
+ * - CAMS: `donotreply@camsonline.com`
+ * - KFintech: `samfS@kfintech.com`
+ * - Emails failing SPF/DKIM/DMARC are rejected
+ * - Forwarded emails must contain the original sender in headers
+ *
+ * **Billing:** 0.2 credits per successfully processed valid email
+ */
fun inboundEmail(): InboundEmailService
/**
@@ -100,28 +169,97 @@ interface CasParserClient {
*/
fun withOptions(modifier: Consumer): CasParserClient.WithRawResponse
+ /**
+ * Endpoints for checking API quota and credits usage. These endpoints help you monitor your
+ * API usage and remaining quota.
+ */
fun credits(): CreditService.WithRawResponse
+ /**
+ * Endpoints for checking API quota and credits usage. These endpoints help you monitor your
+ * API usage and remaining quota.
+ */
fun logs(): LogService.WithRawResponse
+ /**
+ * Endpoints for managing access tokens for the Portfolio Connect SDK. Use these to generate
+ * short-lived `at_` prefixed tokens that can be safely passed to frontend applications.
+ * Access tokens can be used in place of API keys on all v4 endpoints.
+ */
fun accessToken(): AccessTokenService.WithRawResponse
+ /**
+ * Endpoints for managing access tokens for the Portfolio Connect SDK. Use these to generate
+ * short-lived `at_` prefixed tokens that can be safely passed to frontend applications.
+ * Access tokens can be used in place of API keys on all v4 endpoints.
+ */
fun verifyToken(): VerifyTokenService.WithRawResponse
+ /** Endpoints for parsing CAS PDF files from different sources. */
fun camsKfintech(): CamsKfintechService.WithRawResponse
+ /** Endpoints for parsing CAS PDF files from different sources. */
fun cdsl(): CdslService.WithRawResponse
+ /**
+ * Endpoints for parsing Contract Note PDF files from various SEBI brokers like Zerodha,
+ * Groww, Upstox, ICICI etc.
+ */
fun contractNote(): ContractNoteService.WithRawResponse
+ /**
+ * Endpoints for importing CAS files directly from user email inboxes.
+ *
+ * **Supported Providers:** Gmail (more coming soon)
+ *
+ * **How it works:**
+ * 1. Call `POST /v4/inbox/connect` to get an OAuth URL
+ * 2. Redirect user to the OAuth URL for consent
+ * 3. User is redirected back to your `redirect_uri` with an encrypted `inbox_token`
+ * 4. Use the token to list/fetch CAS files from their inbox (`/v4/inbox/cas`)
+ * 5. Files are uploaded to temporary cloud storage (URLs expire in 24 hours)
+ *
+ * **Security:**
+ * - Read-only access (we cannot send emails)
+ * - Tokens are encrypted with server-side secret
+ * - User can revoke access anytime via `/v4/inbox/disconnect`
+ */
fun inbox(): InboxService.WithRawResponse
+ /** Endpoints for generating new CAS documents via email mailback (KFintech). */
fun kfintech(): KfintechService.WithRawResponse
+ /** Endpoints for parsing CAS PDF files from different sources. */
fun nsdl(): NsdlService.WithRawResponse
+ /** Endpoints for parsing CAS PDF files from different sources. */
fun smart(): SmartService.WithRawResponse
+ /**
+ * Create dedicated inbound email addresses for investors to forward their CAS statements.
+ *
+ * **Use Case:** Your app wants to collect CAS statements from users without requiring OAuth
+ * or file upload.
+ *
+ * **How it works:**
+ * 1. Call `POST /v4/inbound-email` to create a unique inbound email address
+ * 2. Display this email to your user: "Forward your CAS statement to
+ * ie_xxx@import.casparser.in"
+ * 3. When user forwards a CAS email, we verify sender authenticity (SPF/DKIM) and call your
+ * webhook
+ * 4. Your webhook receives email metadata + attachment download URLs
+ *
+ * **Sender Validation:**
+ * - Only emails from verified CAS authorities are processed:
+ * - CDSL: `eCAS@cdslstatement.com`
+ * - NSDL: `NSDL-CAS@nsdl.co.in`
+ * - CAMS: `donotreply@camsonline.com`
+ * - KFintech: `samfS@kfintech.com`
+ * - Emails failing SPF/DKIM/DMARC are rejected
+ * - Forwarded emails must contain the original sender in headers
+ *
+ * **Billing:** 0.2 credits per successfully processed valid email
+ */
fun inboundEmail(): InboundEmailService.WithRawResponse
}
}
diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/client/CasParserClientAsync.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/client/CasParserClientAsync.kt
index 4d828d5..575d503 100644
--- a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/client/CasParserClientAsync.kt
+++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/client/CasParserClientAsync.kt
@@ -53,28 +53,97 @@ interface CasParserClientAsync {
*/
fun withOptions(modifier: Consumer): CasParserClientAsync
+ /**
+ * Endpoints for checking API quota and credits usage. These endpoints help you monitor your API
+ * usage and remaining quota.
+ */
fun credits(): CreditServiceAsync
+ /**
+ * Endpoints for checking API quota and credits usage. These endpoints help you monitor your API
+ * usage and remaining quota.
+ */
fun logs(): LogServiceAsync
+ /**
+ * Endpoints for managing access tokens for the Portfolio Connect SDK. Use these to generate
+ * short-lived `at_` prefixed tokens that can be safely passed to frontend applications. Access
+ * tokens can be used in place of API keys on all v4 endpoints.
+ */
fun accessToken(): AccessTokenServiceAsync
+ /**
+ * Endpoints for managing access tokens for the Portfolio Connect SDK. Use these to generate
+ * short-lived `at_` prefixed tokens that can be safely passed to frontend applications. Access
+ * tokens can be used in place of API keys on all v4 endpoints.
+ */
fun verifyToken(): VerifyTokenServiceAsync
+ /** Endpoints for parsing CAS PDF files from different sources. */
fun camsKfintech(): CamsKfintechServiceAsync
+ /** Endpoints for parsing CAS PDF files from different sources. */
fun cdsl(): CdslServiceAsync
+ /**
+ * Endpoints for parsing Contract Note PDF files from various SEBI brokers like Zerodha, Groww,
+ * Upstox, ICICI etc.
+ */
fun contractNote(): ContractNoteServiceAsync
+ /**
+ * Endpoints for importing CAS files directly from user email inboxes.
+ *
+ * **Supported Providers:** Gmail (more coming soon)
+ *
+ * **How it works:**
+ * 1. Call `POST /v4/inbox/connect` to get an OAuth URL
+ * 2. Redirect user to the OAuth URL for consent
+ * 3. User is redirected back to your `redirect_uri` with an encrypted `inbox_token`
+ * 4. Use the token to list/fetch CAS files from their inbox (`/v4/inbox/cas`)
+ * 5. Files are uploaded to temporary cloud storage (URLs expire in 24 hours)
+ *
+ * **Security:**
+ * - Read-only access (we cannot send emails)
+ * - Tokens are encrypted with server-side secret
+ * - User can revoke access anytime via `/v4/inbox/disconnect`
+ */
fun inbox(): InboxServiceAsync
+ /** Endpoints for generating new CAS documents via email mailback (KFintech). */
fun kfintech(): KfintechServiceAsync
+ /** Endpoints for parsing CAS PDF files from different sources. */
fun nsdl(): NsdlServiceAsync
+ /** Endpoints for parsing CAS PDF files from different sources. */
fun smart(): SmartServiceAsync
+ /**
+ * Create dedicated inbound email addresses for investors to forward their CAS statements.
+ *
+ * **Use Case:** Your app wants to collect CAS statements from users without requiring OAuth or
+ * file upload.
+ *
+ * **How it works:**
+ * 1. Call `POST /v4/inbound-email` to create a unique inbound email address
+ * 2. Display this email to your user: "Forward your CAS statement to
+ * ie_xxx@import.casparser.in"
+ * 3. When user forwards a CAS email, we verify sender authenticity (SPF/DKIM) and call your
+ * webhook
+ * 4. Your webhook receives email metadata + attachment download URLs
+ *
+ * **Sender Validation:**
+ * - Only emails from verified CAS authorities are processed:
+ * - CDSL: `eCAS@cdslstatement.com`
+ * - NSDL: `NSDL-CAS@nsdl.co.in`
+ * - CAMS: `donotreply@camsonline.com`
+ * - KFintech: `samfS@kfintech.com`
+ * - Emails failing SPF/DKIM/DMARC are rejected
+ * - Forwarded emails must contain the original sender in headers
+ *
+ * **Billing:** 0.2 credits per successfully processed valid email
+ */
fun inboundEmail(): InboundEmailServiceAsync
/**
@@ -104,28 +173,97 @@ interface CasParserClientAsync {
modifier: Consumer
): CasParserClientAsync.WithRawResponse
+ /**
+ * Endpoints for checking API quota and credits usage. These endpoints help you monitor your
+ * API usage and remaining quota.
+ */
fun credits(): CreditServiceAsync.WithRawResponse
+ /**
+ * Endpoints for checking API quota and credits usage. These endpoints help you monitor your
+ * API usage and remaining quota.
+ */
fun logs(): LogServiceAsync.WithRawResponse
+ /**
+ * Endpoints for managing access tokens for the Portfolio Connect SDK. Use these to generate
+ * short-lived `at_` prefixed tokens that can be safely passed to frontend applications.
+ * Access tokens can be used in place of API keys on all v4 endpoints.
+ */
fun accessToken(): AccessTokenServiceAsync.WithRawResponse
+ /**
+ * Endpoints for managing access tokens for the Portfolio Connect SDK. Use these to generate
+ * short-lived `at_` prefixed tokens that can be safely passed to frontend applications.
+ * Access tokens can be used in place of API keys on all v4 endpoints.
+ */
fun verifyToken(): VerifyTokenServiceAsync.WithRawResponse
+ /** Endpoints for parsing CAS PDF files from different sources. */
fun camsKfintech(): CamsKfintechServiceAsync.WithRawResponse
+ /** Endpoints for parsing CAS PDF files from different sources. */
fun cdsl(): CdslServiceAsync.WithRawResponse
+ /**
+ * Endpoints for parsing Contract Note PDF files from various SEBI brokers like Zerodha,
+ * Groww, Upstox, ICICI etc.
+ */
fun contractNote(): ContractNoteServiceAsync.WithRawResponse
+ /**
+ * Endpoints for importing CAS files directly from user email inboxes.
+ *
+ * **Supported Providers:** Gmail (more coming soon)
+ *
+ * **How it works:**
+ * 1. Call `POST /v4/inbox/connect` to get an OAuth URL
+ * 2. Redirect user to the OAuth URL for consent
+ * 3. User is redirected back to your `redirect_uri` with an encrypted `inbox_token`
+ * 4. Use the token to list/fetch CAS files from their inbox (`/v4/inbox/cas`)
+ * 5. Files are uploaded to temporary cloud storage (URLs expire in 24 hours)
+ *
+ * **Security:**
+ * - Read-only access (we cannot send emails)
+ * - Tokens are encrypted with server-side secret
+ * - User can revoke access anytime via `/v4/inbox/disconnect`
+ */
fun inbox(): InboxServiceAsync.WithRawResponse
+ /** Endpoints for generating new CAS documents via email mailback (KFintech). */
fun kfintech(): KfintechServiceAsync.WithRawResponse
+ /** Endpoints for parsing CAS PDF files from different sources. */
fun nsdl(): NsdlServiceAsync.WithRawResponse
+ /** Endpoints for parsing CAS PDF files from different sources. */
fun smart(): SmartServiceAsync.WithRawResponse
+ /**
+ * Create dedicated inbound email addresses for investors to forward their CAS statements.
+ *
+ * **Use Case:** Your app wants to collect CAS statements from users without requiring OAuth
+ * or file upload.
+ *
+ * **How it works:**
+ * 1. Call `POST /v4/inbound-email` to create a unique inbound email address
+ * 2. Display this email to your user: "Forward your CAS statement to
+ * ie_xxx@import.casparser.in"
+ * 3. When user forwards a CAS email, we verify sender authenticity (SPF/DKIM) and call your
+ * webhook
+ * 4. Your webhook receives email metadata + attachment download URLs
+ *
+ * **Sender Validation:**
+ * - Only emails from verified CAS authorities are processed:
+ * - CDSL: `eCAS@cdslstatement.com`
+ * - NSDL: `NSDL-CAS@nsdl.co.in`
+ * - CAMS: `donotreply@camsonline.com`
+ * - KFintech: `samfS@kfintech.com`
+ * - Emails failing SPF/DKIM/DMARC are rejected
+ * - Forwarded emails must contain the original sender in headers
+ *
+ * **Billing:** 0.2 credits per successfully processed valid email
+ */
fun inboundEmail(): InboundEmailServiceAsync.WithRawResponse
}
}
diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/client/CasParserClientAsyncImpl.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/client/CasParserClientAsyncImpl.kt
index b10af0a..eed1327 100644
--- a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/client/CasParserClientAsyncImpl.kt
+++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/client/CasParserClientAsyncImpl.kt
@@ -96,28 +96,97 @@ class CasParserClientAsyncImpl(private val clientOptions: ClientOptions) : CasPa
override fun withOptions(modifier: Consumer): CasParserClientAsync =
CasParserClientAsyncImpl(clientOptions.toBuilder().apply(modifier::accept).build())
+ /**
+ * Endpoints for checking API quota and credits usage. These endpoints help you monitor your API
+ * usage and remaining quota.
+ */
override fun credits(): CreditServiceAsync = credits
+ /**
+ * Endpoints for checking API quota and credits usage. These endpoints help you monitor your API
+ * usage and remaining quota.
+ */
override fun logs(): LogServiceAsync = logs
+ /**
+ * Endpoints for managing access tokens for the Portfolio Connect SDK. Use these to generate
+ * short-lived `at_` prefixed tokens that can be safely passed to frontend applications. Access
+ * tokens can be used in place of API keys on all v4 endpoints.
+ */
override fun accessToken(): AccessTokenServiceAsync = accessToken
+ /**
+ * Endpoints for managing access tokens for the Portfolio Connect SDK. Use these to generate
+ * short-lived `at_` prefixed tokens that can be safely passed to frontend applications. Access
+ * tokens can be used in place of API keys on all v4 endpoints.
+ */
override fun verifyToken(): VerifyTokenServiceAsync = verifyToken
+ /** Endpoints for parsing CAS PDF files from different sources. */
override fun camsKfintech(): CamsKfintechServiceAsync = camsKfintech
+ /** Endpoints for parsing CAS PDF files from different sources. */
override fun cdsl(): CdslServiceAsync = cdsl
+ /**
+ * Endpoints for parsing Contract Note PDF files from various SEBI brokers like Zerodha, Groww,
+ * Upstox, ICICI etc.
+ */
override fun contractNote(): ContractNoteServiceAsync = contractNote
+ /**
+ * Endpoints for importing CAS files directly from user email inboxes.
+ *
+ * **Supported Providers:** Gmail (more coming soon)
+ *
+ * **How it works:**
+ * 1. Call `POST /v4/inbox/connect` to get an OAuth URL
+ * 2. Redirect user to the OAuth URL for consent
+ * 3. User is redirected back to your `redirect_uri` with an encrypted `inbox_token`
+ * 4. Use the token to list/fetch CAS files from their inbox (`/v4/inbox/cas`)
+ * 5. Files are uploaded to temporary cloud storage (URLs expire in 24 hours)
+ *
+ * **Security:**
+ * - Read-only access (we cannot send emails)
+ * - Tokens are encrypted with server-side secret
+ * - User can revoke access anytime via `/v4/inbox/disconnect`
+ */
override fun inbox(): InboxServiceAsync = inbox
+ /** Endpoints for generating new CAS documents via email mailback (KFintech). */
override fun kfintech(): KfintechServiceAsync = kfintech
+ /** Endpoints for parsing CAS PDF files from different sources. */
override fun nsdl(): NsdlServiceAsync = nsdl
+ /** Endpoints for parsing CAS PDF files from different sources. */
override fun smart(): SmartServiceAsync = smart
+ /**
+ * Create dedicated inbound email addresses for investors to forward their CAS statements.
+ *
+ * **Use Case:** Your app wants to collect CAS statements from users without requiring OAuth or
+ * file upload.
+ *
+ * **How it works:**
+ * 1. Call `POST /v4/inbound-email` to create a unique inbound email address
+ * 2. Display this email to your user: "Forward your CAS statement to
+ * ie_xxx@import.casparser.in"
+ * 3. When user forwards a CAS email, we verify sender authenticity (SPF/DKIM) and call your
+ * webhook
+ * 4. Your webhook receives email metadata + attachment download URLs
+ *
+ * **Sender Validation:**
+ * - Only emails from verified CAS authorities are processed:
+ * - CDSL: `eCAS@cdslstatement.com`
+ * - NSDL: `NSDL-CAS@nsdl.co.in`
+ * - CAMS: `donotreply@camsonline.com`
+ * - KFintech: `samfS@kfintech.com`
+ * - Emails failing SPF/DKIM/DMARC are rejected
+ * - Forwarded emails must contain the original sender in headers
+ *
+ * **Billing:** 0.2 credits per successfully processed valid email
+ */
override fun inboundEmail(): InboundEmailServiceAsync = inboundEmail
override fun close() = clientOptions.close()
@@ -180,28 +249,97 @@ class CasParserClientAsyncImpl(private val clientOptions: ClientOptions) : CasPa
clientOptions.toBuilder().apply(modifier::accept).build()
)
+ /**
+ * Endpoints for checking API quota and credits usage. These endpoints help you monitor your
+ * API usage and remaining quota.
+ */
override fun credits(): CreditServiceAsync.WithRawResponse = credits
+ /**
+ * Endpoints for checking API quota and credits usage. These endpoints help you monitor your
+ * API usage and remaining quota.
+ */
override fun logs(): LogServiceAsync.WithRawResponse = logs
+ /**
+ * Endpoints for managing access tokens for the Portfolio Connect SDK. Use these to generate
+ * short-lived `at_` prefixed tokens that can be safely passed to frontend applications.
+ * Access tokens can be used in place of API keys on all v4 endpoints.
+ */
override fun accessToken(): AccessTokenServiceAsync.WithRawResponse = accessToken
+ /**
+ * Endpoints for managing access tokens for the Portfolio Connect SDK. Use these to generate
+ * short-lived `at_` prefixed tokens that can be safely passed to frontend applications.
+ * Access tokens can be used in place of API keys on all v4 endpoints.
+ */
override fun verifyToken(): VerifyTokenServiceAsync.WithRawResponse = verifyToken
+ /** Endpoints for parsing CAS PDF files from different sources. */
override fun camsKfintech(): CamsKfintechServiceAsync.WithRawResponse = camsKfintech
+ /** Endpoints for parsing CAS PDF files from different sources. */
override fun cdsl(): CdslServiceAsync.WithRawResponse = cdsl
+ /**
+ * Endpoints for parsing Contract Note PDF files from various SEBI brokers like Zerodha,
+ * Groww, Upstox, ICICI etc.
+ */
override fun contractNote(): ContractNoteServiceAsync.WithRawResponse = contractNote
+ /**
+ * Endpoints for importing CAS files directly from user email inboxes.
+ *
+ * **Supported Providers:** Gmail (more coming soon)
+ *
+ * **How it works:**
+ * 1. Call `POST /v4/inbox/connect` to get an OAuth URL
+ * 2. Redirect user to the OAuth URL for consent
+ * 3. User is redirected back to your `redirect_uri` with an encrypted `inbox_token`
+ * 4. Use the token to list/fetch CAS files from their inbox (`/v4/inbox/cas`)
+ * 5. Files are uploaded to temporary cloud storage (URLs expire in 24 hours)
+ *
+ * **Security:**
+ * - Read-only access (we cannot send emails)
+ * - Tokens are encrypted with server-side secret
+ * - User can revoke access anytime via `/v4/inbox/disconnect`
+ */
override fun inbox(): InboxServiceAsync.WithRawResponse = inbox
+ /** Endpoints for generating new CAS documents via email mailback (KFintech). */
override fun kfintech(): KfintechServiceAsync.WithRawResponse = kfintech
+ /** Endpoints for parsing CAS PDF files from different sources. */
override fun nsdl(): NsdlServiceAsync.WithRawResponse = nsdl
+ /** Endpoints for parsing CAS PDF files from different sources. */
override fun smart(): SmartServiceAsync.WithRawResponse = smart
+ /**
+ * Create dedicated inbound email addresses for investors to forward their CAS statements.
+ *
+ * **Use Case:** Your app wants to collect CAS statements from users without requiring OAuth
+ * or file upload.
+ *
+ * **How it works:**
+ * 1. Call `POST /v4/inbound-email` to create a unique inbound email address
+ * 2. Display this email to your user: "Forward your CAS statement to
+ * ie_xxx@import.casparser.in"
+ * 3. When user forwards a CAS email, we verify sender authenticity (SPF/DKIM) and call your
+ * webhook
+ * 4. Your webhook receives email metadata + attachment download URLs
+ *
+ * **Sender Validation:**
+ * - Only emails from verified CAS authorities are processed:
+ * - CDSL: `eCAS@cdslstatement.com`
+ * - NSDL: `NSDL-CAS@nsdl.co.in`
+ * - CAMS: `donotreply@camsonline.com`
+ * - KFintech: `samfS@kfintech.com`
+ * - Emails failing SPF/DKIM/DMARC are rejected
+ * - Forwarded emails must contain the original sender in headers
+ *
+ * **Billing:** 0.2 credits per successfully processed valid email
+ */
override fun inboundEmail(): InboundEmailServiceAsync.WithRawResponse = inboundEmail
}
}
diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/client/CasParserClientImpl.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/client/CasParserClientImpl.kt
index db32253..75ee210 100644
--- a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/client/CasParserClientImpl.kt
+++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/client/CasParserClientImpl.kt
@@ -90,28 +90,97 @@ class CasParserClientImpl(private val clientOptions: ClientOptions) : CasParserC
override fun withOptions(modifier: Consumer): CasParserClient =
CasParserClientImpl(clientOptions.toBuilder().apply(modifier::accept).build())
+ /**
+ * Endpoints for checking API quota and credits usage. These endpoints help you monitor your API
+ * usage and remaining quota.
+ */
override fun credits(): CreditService = credits
+ /**
+ * Endpoints for checking API quota and credits usage. These endpoints help you monitor your API
+ * usage and remaining quota.
+ */
override fun logs(): LogService = logs
+ /**
+ * Endpoints for managing access tokens for the Portfolio Connect SDK. Use these to generate
+ * short-lived `at_` prefixed tokens that can be safely passed to frontend applications. Access
+ * tokens can be used in place of API keys on all v4 endpoints.
+ */
override fun accessToken(): AccessTokenService = accessToken
+ /**
+ * Endpoints for managing access tokens for the Portfolio Connect SDK. Use these to generate
+ * short-lived `at_` prefixed tokens that can be safely passed to frontend applications. Access
+ * tokens can be used in place of API keys on all v4 endpoints.
+ */
override fun verifyToken(): VerifyTokenService = verifyToken
+ /** Endpoints for parsing CAS PDF files from different sources. */
override fun camsKfintech(): CamsKfintechService = camsKfintech
+ /** Endpoints for parsing CAS PDF files from different sources. */
override fun cdsl(): CdslService = cdsl
+ /**
+ * Endpoints for parsing Contract Note PDF files from various SEBI brokers like Zerodha, Groww,
+ * Upstox, ICICI etc.
+ */
override fun contractNote(): ContractNoteService = contractNote
+ /**
+ * Endpoints for importing CAS files directly from user email inboxes.
+ *
+ * **Supported Providers:** Gmail (more coming soon)
+ *
+ * **How it works:**
+ * 1. Call `POST /v4/inbox/connect` to get an OAuth URL
+ * 2. Redirect user to the OAuth URL for consent
+ * 3. User is redirected back to your `redirect_uri` with an encrypted `inbox_token`
+ * 4. Use the token to list/fetch CAS files from their inbox (`/v4/inbox/cas`)
+ * 5. Files are uploaded to temporary cloud storage (URLs expire in 24 hours)
+ *
+ * **Security:**
+ * - Read-only access (we cannot send emails)
+ * - Tokens are encrypted with server-side secret
+ * - User can revoke access anytime via `/v4/inbox/disconnect`
+ */
override fun inbox(): InboxService = inbox
+ /** Endpoints for generating new CAS documents via email mailback (KFintech). */
override fun kfintech(): KfintechService = kfintech
+ /** Endpoints for parsing CAS PDF files from different sources. */
override fun nsdl(): NsdlService = nsdl
+ /** Endpoints for parsing CAS PDF files from different sources. */
override fun smart(): SmartService = smart
+ /**
+ * Create dedicated inbound email addresses for investors to forward their CAS statements.
+ *
+ * **Use Case:** Your app wants to collect CAS statements from users without requiring OAuth or
+ * file upload.
+ *
+ * **How it works:**
+ * 1. Call `POST /v4/inbound-email` to create a unique inbound email address
+ * 2. Display this email to your user: "Forward your CAS statement to
+ * ie_xxx@import.casparser.in"
+ * 3. When user forwards a CAS email, we verify sender authenticity (SPF/DKIM) and call your
+ * webhook
+ * 4. Your webhook receives email metadata + attachment download URLs
+ *
+ * **Sender Validation:**
+ * - Only emails from verified CAS authorities are processed:
+ * - CDSL: `eCAS@cdslstatement.com`
+ * - NSDL: `NSDL-CAS@nsdl.co.in`
+ * - CAMS: `donotreply@camsonline.com`
+ * - KFintech: `samfS@kfintech.com`
+ * - Emails failing SPF/DKIM/DMARC are rejected
+ * - Forwarded emails must contain the original sender in headers
+ *
+ * **Billing:** 0.2 credits per successfully processed valid email
+ */
override fun inboundEmail(): InboundEmailService = inboundEmail
override fun close() = clientOptions.close()
@@ -174,28 +243,97 @@ class CasParserClientImpl(private val clientOptions: ClientOptions) : CasParserC
clientOptions.toBuilder().apply(modifier::accept).build()
)
+ /**
+ * Endpoints for checking API quota and credits usage. These endpoints help you monitor your
+ * API usage and remaining quota.
+ */
override fun credits(): CreditService.WithRawResponse = credits
+ /**
+ * Endpoints for checking API quota and credits usage. These endpoints help you monitor your
+ * API usage and remaining quota.
+ */
override fun logs(): LogService.WithRawResponse = logs
+ /**
+ * Endpoints for managing access tokens for the Portfolio Connect SDK. Use these to generate
+ * short-lived `at_` prefixed tokens that can be safely passed to frontend applications.
+ * Access tokens can be used in place of API keys on all v4 endpoints.
+ */
override fun accessToken(): AccessTokenService.WithRawResponse = accessToken
+ /**
+ * Endpoints for managing access tokens for the Portfolio Connect SDK. Use these to generate
+ * short-lived `at_` prefixed tokens that can be safely passed to frontend applications.
+ * Access tokens can be used in place of API keys on all v4 endpoints.
+ */
override fun verifyToken(): VerifyTokenService.WithRawResponse = verifyToken
+ /** Endpoints for parsing CAS PDF files from different sources. */
override fun camsKfintech(): CamsKfintechService.WithRawResponse = camsKfintech
+ /** Endpoints for parsing CAS PDF files from different sources. */
override fun cdsl(): CdslService.WithRawResponse = cdsl
+ /**
+ * Endpoints for parsing Contract Note PDF files from various SEBI brokers like Zerodha,
+ * Groww, Upstox, ICICI etc.
+ */
override fun contractNote(): ContractNoteService.WithRawResponse = contractNote
+ /**
+ * Endpoints for importing CAS files directly from user email inboxes.
+ *
+ * **Supported Providers:** Gmail (more coming soon)
+ *
+ * **How it works:**
+ * 1. Call `POST /v4/inbox/connect` to get an OAuth URL
+ * 2. Redirect user to the OAuth URL for consent
+ * 3. User is redirected back to your `redirect_uri` with an encrypted `inbox_token`
+ * 4. Use the token to list/fetch CAS files from their inbox (`/v4/inbox/cas`)
+ * 5. Files are uploaded to temporary cloud storage (URLs expire in 24 hours)
+ *
+ * **Security:**
+ * - Read-only access (we cannot send emails)
+ * - Tokens are encrypted with server-side secret
+ * - User can revoke access anytime via `/v4/inbox/disconnect`
+ */
override fun inbox(): InboxService.WithRawResponse = inbox
+ /** Endpoints for generating new CAS documents via email mailback (KFintech). */
override fun kfintech(): KfintechService.WithRawResponse = kfintech
+ /** Endpoints for parsing CAS PDF files from different sources. */
override fun nsdl(): NsdlService.WithRawResponse = nsdl
+ /** Endpoints for parsing CAS PDF files from different sources. */
override fun smart(): SmartService.WithRawResponse = smart
+ /**
+ * Create dedicated inbound email addresses for investors to forward their CAS statements.
+ *
+ * **Use Case:** Your app wants to collect CAS statements from users without requiring OAuth
+ * or file upload.
+ *
+ * **How it works:**
+ * 1. Call `POST /v4/inbound-email` to create a unique inbound email address
+ * 2. Display this email to your user: "Forward your CAS statement to
+ * ie_xxx@import.casparser.in"
+ * 3. When user forwards a CAS email, we verify sender authenticity (SPF/DKIM) and call your
+ * webhook
+ * 4. Your webhook receives email metadata + attachment download URLs
+ *
+ * **Sender Validation:**
+ * - Only emails from verified CAS authorities are processed:
+ * - CDSL: `eCAS@cdslstatement.com`
+ * - NSDL: `NSDL-CAS@nsdl.co.in`
+ * - CAMS: `donotreply@camsonline.com`
+ * - KFintech: `samfS@kfintech.com`
+ * - Emails failing SPF/DKIM/DMARC are rejected
+ * - Forwarded emails must contain the original sender in headers
+ *
+ * **Billing:** 0.2 credits per successfully processed valid email
+ */
override fun inboundEmail(): InboundEmailService.WithRawResponse = inboundEmail
}
}
diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/core/Properties.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/core/Properties.kt
index dc9a375..f946e80 100644
--- a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/core/Properties.kt
+++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/core/Properties.kt
@@ -34,9 +34,9 @@ fun getOsName(): String {
}
}
-fun getOsVersion(): String = System.getProperty("os.version", "unknown")
+fun getOsVersion(): String = System.getProperty("os.version", "unknown") ?: "unknown"
fun getPackageVersion(): String =
- CasParserClient::class.java.`package`.implementationVersion ?: "unknown"
+ CasParserClient::class.java.`package`?.implementationVersion ?: "unknown"
-fun getJavaVersion(): String = System.getProperty("java.version", "unknown")
+fun getJavaVersion(): String = System.getProperty("java.version", "unknown") ?: "unknown"
diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/core/http/HttpRequestBodies.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/core/http/HttpRequestBodies.kt
index 34ce9d6..6f7c370 100644
--- a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/core/http/HttpRequestBodies.kt
+++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/core/http/HttpRequestBodies.kt
@@ -5,16 +5,16 @@
package com.cas_parser.api.core.http
import com.cas_parser.api.core.MultipartField
+import com.cas_parser.api.core.toImmutable
import com.cas_parser.api.errors.CasParserInvalidDataException
import com.fasterxml.jackson.databind.JsonNode
import com.fasterxml.jackson.databind.json.JsonMapper
import com.fasterxml.jackson.databind.node.JsonNodeType
+import java.io.ByteArrayInputStream
import java.io.InputStream
import java.io.OutputStream
+import java.util.UUID
import kotlin.jvm.optionals.getOrNull
-import org.apache.hc.client5.http.entity.mime.MultipartEntityBuilder
-import org.apache.hc.core5.http.ContentType
-import org.apache.hc.core5.http.HttpEntity
@JvmSynthetic
internal inline fun json(jsonMapper: JsonMapper, value: T): HttpRequestBody =
@@ -37,94 +37,231 @@ internal fun multipartFormData(
jsonMapper: JsonMapper,
fields: Map>,
): HttpRequestBody =
- object : HttpRequestBody {
- private val entity: HttpEntity by lazy {
- MultipartEntityBuilder.create()
- .apply {
- fields.forEach { (name, field) ->
- val knownValue = field.value.asKnown().getOrNull()
- val parts =
- if (knownValue is InputStream) {
- // Read directly from the `InputStream` instead of reading it all
- // into memory due to the `jsonMapper` serialization below.
- sequenceOf(name to knownValue)
- } else {
- val node = jsonMapper.valueToTree(field.value)
- serializePart(name, node)
+ MultipartBody.Builder()
+ .apply {
+ fields.forEach { (name, field) ->
+ val knownValue = field.value.asKnown().getOrNull()
+ val parts =
+ if (knownValue is InputStream) {
+ // Read directly from the `InputStream` instead of reading it all
+ // into memory due to the `jsonMapper` serialization below.
+ sequenceOf(name to knownValue)
+ } else {
+ val node = jsonMapper.valueToTree(field.value)
+ serializePart(name, node)
+ }
+
+ parts.forEach { (name, bytes) ->
+ val partBody =
+ if (bytes is ByteArrayInputStream) {
+ val byteArray = bytes.readBytes()
+
+ object : HttpRequestBody {
+
+ override fun writeTo(outputStream: OutputStream) {
+ outputStream.write(byteArray)
+ }
+
+ override fun contentType(): String = field.contentType
+
+ override fun contentLength(): Long = byteArray.size.toLong()
+
+ override fun repeatable(): Boolean = true
+
+ override fun close() {}
}
+ } else {
+ object : HttpRequestBody {
+
+ override fun writeTo(outputStream: OutputStream) {
+ bytes.copyTo(outputStream)
+ }
+
+ override fun contentType(): String = field.contentType
+
+ override fun contentLength(): Long = -1L
- parts.forEach { (name, bytes) ->
- addBinaryBody(
- name,
- bytes,
- ContentType.parseLenient(field.contentType),
- field.filename().getOrNull(),
- )
+ override fun repeatable(): Boolean = false
+
+ override fun close() = bytes.close()
+ }
}
- }
+
+ addPart(
+ MultipartBody.Part.create(
+ name,
+ field.filename().getOrNull(),
+ field.contentType,
+ partBody,
+ )
+ )
}
- .build()
+ }
}
+ .build()
- private fun serializePart(
- name: String,
- node: JsonNode,
- ): Sequence> =
- when (node.nodeType) {
- JsonNodeType.MISSING,
- JsonNodeType.NULL -> emptySequence()
- JsonNodeType.BINARY -> sequenceOf(name to node.binaryValue().inputStream())
- JsonNodeType.STRING -> sequenceOf(name to node.textValue().inputStream())
- JsonNodeType.BOOLEAN ->
- sequenceOf(name to node.booleanValue().toString().inputStream())
- JsonNodeType.NUMBER ->
- sequenceOf(name to node.numberValue().toString().inputStream())
- JsonNodeType.ARRAY ->
- sequenceOf(
- name to
- node
- .elements()
- .asSequence()
- .mapNotNull { element ->
- when (element.nodeType) {
- JsonNodeType.MISSING,
- JsonNodeType.NULL -> null
- JsonNodeType.STRING -> node.textValue()
- JsonNodeType.BOOLEAN -> node.booleanValue().toString()
- JsonNodeType.NUMBER -> node.numberValue().toString()
- null,
- JsonNodeType.BINARY,
- JsonNodeType.ARRAY,
- JsonNodeType.OBJECT,
- JsonNodeType.POJO ->
- throw CasParserInvalidDataException(
- "Unexpected JsonNode type in array: ${node.nodeType}"
- )
- }
- }
- .joinToString(",")
- .inputStream()
- )
- JsonNodeType.OBJECT ->
- node.fields().asSequence().flatMap { (key, value) ->
- serializePart("$name[$key]", value)
- }
- JsonNodeType.POJO,
- null ->
- throw CasParserInvalidDataException(
- "Unexpected JsonNode type: ${node.nodeType}"
- )
+private fun serializePart(name: String, node: JsonNode): Sequence> =
+ when (node.nodeType) {
+ JsonNodeType.MISSING,
+ JsonNodeType.NULL -> emptySequence()
+ JsonNodeType.BINARY -> sequenceOf(name to node.binaryValue().inputStream())
+ JsonNodeType.STRING -> sequenceOf(name to node.textValue().byteInputStream())
+ JsonNodeType.BOOLEAN -> sequenceOf(name to node.booleanValue().toString().byteInputStream())
+ JsonNodeType.NUMBER -> sequenceOf(name to node.numberValue().toString().byteInputStream())
+ JsonNodeType.ARRAY ->
+ sequenceOf(
+ name to
+ node
+ .elements()
+ .asSequence()
+ .mapNotNull { element ->
+ when (element.nodeType) {
+ JsonNodeType.MISSING,
+ JsonNodeType.NULL -> null
+ JsonNodeType.STRING -> element.textValue()
+ JsonNodeType.BOOLEAN -> element.booleanValue().toString()
+ JsonNodeType.NUMBER -> element.numberValue().toString()
+ null,
+ JsonNodeType.BINARY,
+ JsonNodeType.ARRAY,
+ JsonNodeType.OBJECT,
+ JsonNodeType.POJO ->
+ throw CasParserInvalidDataException(
+ "Unexpected JsonNode type in array: ${element.nodeType}"
+ )
+ }
+ }
+ .joinToString(",")
+ .byteInputStream()
+ )
+ JsonNodeType.OBJECT ->
+ node.fields().asSequence().flatMap { (key, value) ->
+ serializePart("$name[$key]", value)
}
+ JsonNodeType.POJO,
+ null -> throw CasParserInvalidDataException("Unexpected JsonNode type: ${node.nodeType}")
+ }
- private fun String.inputStream(): InputStream = toByteArray().inputStream()
+private class MultipartBody
+private constructor(private val boundary: String, private val parts: List) : HttpRequestBody {
+ private val boundaryBytes: ByteArray = boundary.toByteArray()
+ private val contentType = "multipart/form-data; boundary=$boundary"
- override fun writeTo(outputStream: OutputStream) = entity.writeTo(outputStream)
+ // This must remain in sync with `contentLength`.
+ override fun writeTo(outputStream: OutputStream) {
+ parts.forEach { part ->
+ outputStream.write(DASHDASH)
+ outputStream.write(boundaryBytes)
+ outputStream.write(CRLF)
- override fun contentType(): String = entity.contentType
+ outputStream.write(CONTENT_DISPOSITION)
+ outputStream.write(part.contentDisposition.toByteArray())
+ outputStream.write(CRLF)
- override fun contentLength(): Long = entity.contentLength
+ outputStream.write(CONTENT_TYPE)
+ outputStream.write(part.contentType.toByteArray())
+ outputStream.write(CRLF)
- override fun repeatable(): Boolean = entity.isRepeatable
+ outputStream.write(CRLF)
+ part.body.writeTo(outputStream)
+ outputStream.write(CRLF)
+ }
- override fun close() = entity.close()
+ outputStream.write(DASHDASH)
+ outputStream.write(boundaryBytes)
+ outputStream.write(DASHDASH)
+ outputStream.write(CRLF)
+ }
+
+ override fun contentType(): String = contentType
+
+ // This must remain in sync with `writeTo`.
+ override fun contentLength(): Long {
+ var byteCount = 0L
+
+ parts.forEach { part ->
+ val contentLength = part.body.contentLength()
+ if (contentLength == -1L) {
+ return -1L
+ }
+
+ byteCount +=
+ DASHDASH.size +
+ boundaryBytes.size +
+ CRLF.size +
+ CONTENT_DISPOSITION.size +
+ part.contentDisposition.toByteArray().size +
+ CRLF.size +
+ CONTENT_TYPE.size +
+ part.contentType.toByteArray().size +
+ CRLF.size +
+ CRLF.size +
+ contentLength +
+ CRLF.size
+ }
+
+ byteCount += DASHDASH.size + boundaryBytes.size + DASHDASH.size + CRLF.size
+ return byteCount
+ }
+
+ override fun repeatable(): Boolean = parts.all { it.body.repeatable() }
+
+ override fun close() {
+ parts.forEach { it.body.close() }
+ }
+
+ class Builder {
+ private val boundary = UUID.randomUUID().toString()
+ private val parts: MutableList = mutableListOf()
+
+ fun addPart(part: Part) = apply { parts.add(part) }
+
+ fun build() = MultipartBody(boundary, parts.toImmutable())
+ }
+
+ class Part
+ private constructor(
+ val contentDisposition: String,
+ val contentType: String,
+ val body: HttpRequestBody,
+ ) {
+ companion object {
+ fun create(
+ name: String,
+ filename: String?,
+ contentType: String,
+ body: HttpRequestBody,
+ ): Part {
+ val disposition = buildString {
+ append("form-data; name=")
+ appendQuotedString(name)
+ if (filename != null) {
+ append("; filename=")
+ appendQuotedString(filename)
+ }
+ }
+ return Part(disposition, contentType, body)
+ }
+ }
+ }
+
+ companion object {
+ private val CRLF = byteArrayOf('\r'.code.toByte(), '\n'.code.toByte())
+ private val DASHDASH = byteArrayOf('-'.code.toByte(), '-'.code.toByte())
+ private val CONTENT_DISPOSITION = "Content-Disposition: ".toByteArray()
+ private val CONTENT_TYPE = "Content-Type: ".toByteArray()
+
+ private fun StringBuilder.appendQuotedString(key: String) {
+ append('"')
+ for (ch in key) {
+ when (ch) {
+ '\n' -> append("%0A")
+ '\r' -> append("%0D")
+ '"' -> append("%22")
+ else -> append(ch)
+ }
+ }
+ append('"')
+ }
}
+}
diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/core/http/RetryingHttpClient.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/core/http/RetryingHttpClient.kt
index 3f34f6e..7b39c62 100644
--- a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/core/http/RetryingHttpClient.kt
+++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/core/http/RetryingHttpClient.kt
@@ -1,3 +1,5 @@
+// File generated from our OpenAPI spec by Stainless.
+
package com.cas_parser.api.core.http
import com.cas_parser.api.core.DefaultSleeper
diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/async/AccessTokenServiceAsync.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/async/AccessTokenServiceAsync.kt
index acfdef1..6f0e9db 100644
--- a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/async/AccessTokenServiceAsync.kt
+++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/async/AccessTokenServiceAsync.kt
@@ -10,6 +10,11 @@ import com.cas_parser.api.models.accesstoken.AccessTokenCreateResponse
import java.util.concurrent.CompletableFuture
import java.util.function.Consumer
+/**
+ * Endpoints for managing access tokens for the Portfolio Connect SDK. Use these to generate
+ * short-lived `at_` prefixed tokens that can be safely passed to frontend applications. Access
+ * tokens can be used in place of API keys on all v4 endpoints.
+ */
interface AccessTokenServiceAsync {
/**
diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/async/AccessTokenServiceAsyncImpl.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/async/AccessTokenServiceAsyncImpl.kt
index ec51534..a36cbb7 100644
--- a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/async/AccessTokenServiceAsyncImpl.kt
+++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/async/AccessTokenServiceAsyncImpl.kt
@@ -20,6 +20,11 @@ import com.cas_parser.api.models.accesstoken.AccessTokenCreateResponse
import java.util.concurrent.CompletableFuture
import java.util.function.Consumer
+/**
+ * Endpoints for managing access tokens for the Portfolio Connect SDK. Use these to generate
+ * short-lived `at_` prefixed tokens that can be safely passed to frontend applications. Access
+ * tokens can be used in place of API keys on all v4 endpoints.
+ */
class AccessTokenServiceAsyncImpl internal constructor(private val clientOptions: ClientOptions) :
AccessTokenServiceAsync {
diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/async/CamsKfintechServiceAsync.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/async/CamsKfintechServiceAsync.kt
index c7651f1..e8d7cc2 100644
--- a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/async/CamsKfintechServiceAsync.kt
+++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/async/CamsKfintechServiceAsync.kt
@@ -10,6 +10,7 @@ import com.cas_parser.api.models.camskfintech.UnifiedResponse
import java.util.concurrent.CompletableFuture
import java.util.function.Consumer
+/** Endpoints for parsing CAS PDF files from different sources. */
interface CamsKfintechServiceAsync {
/**
diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/async/CamsKfintechServiceAsyncImpl.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/async/CamsKfintechServiceAsyncImpl.kt
index ef05059..df53226 100644
--- a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/async/CamsKfintechServiceAsyncImpl.kt
+++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/async/CamsKfintechServiceAsyncImpl.kt
@@ -20,6 +20,7 @@ import com.cas_parser.api.models.camskfintech.UnifiedResponse
import java.util.concurrent.CompletableFuture
import java.util.function.Consumer
+/** Endpoints for parsing CAS PDF files from different sources. */
class CamsKfintechServiceAsyncImpl internal constructor(private val clientOptions: ClientOptions) :
CamsKfintechServiceAsync {
diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/async/CdslServiceAsync.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/async/CdslServiceAsync.kt
index b00e28d..dd5e7d3 100644
--- a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/async/CdslServiceAsync.kt
+++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/async/CdslServiceAsync.kt
@@ -11,6 +11,7 @@ import com.cas_parser.api.services.async.cdsl.FetchServiceAsync
import java.util.concurrent.CompletableFuture
import java.util.function.Consumer
+/** Endpoints for parsing CAS PDF files from different sources. */
interface CdslServiceAsync {
/**
@@ -25,6 +26,10 @@ interface CdslServiceAsync {
*/
fun withOptions(modifier: Consumer): CdslServiceAsync
+ /**
+ * Endpoints for fetching CAS documents with instant download. Currently supports CDSL via OTP
+ * authentication.
+ */
fun fetch(): FetchServiceAsync
/**
@@ -58,6 +63,10 @@ interface CdslServiceAsync {
*/
fun withOptions(modifier: Consumer): CdslServiceAsync.WithRawResponse
+ /**
+ * Endpoints for fetching CAS documents with instant download. Currently supports CDSL via
+ * OTP authentication.
+ */
fun fetch(): FetchServiceAsync.WithRawResponse
/**
diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/async/CdslServiceAsyncImpl.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/async/CdslServiceAsyncImpl.kt
index 2531112..0bb2f66 100644
--- a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/async/CdslServiceAsyncImpl.kt
+++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/async/CdslServiceAsyncImpl.kt
@@ -22,6 +22,7 @@ import com.cas_parser.api.services.async.cdsl.FetchServiceAsyncImpl
import java.util.concurrent.CompletableFuture
import java.util.function.Consumer
+/** Endpoints for parsing CAS PDF files from different sources. */
class CdslServiceAsyncImpl internal constructor(private val clientOptions: ClientOptions) :
CdslServiceAsync {
@@ -36,6 +37,10 @@ class CdslServiceAsyncImpl internal constructor(private val clientOptions: Clien
override fun withOptions(modifier: Consumer): CdslServiceAsync =
CdslServiceAsyncImpl(clientOptions.toBuilder().apply(modifier::accept).build())
+ /**
+ * Endpoints for fetching CAS documents with instant download. Currently supports CDSL via OTP
+ * authentication.
+ */
override fun fetch(): FetchServiceAsync = fetch
override fun parsePdf(
@@ -62,6 +67,10 @@ class CdslServiceAsyncImpl internal constructor(private val clientOptions: Clien
clientOptions.toBuilder().apply(modifier::accept).build()
)
+ /**
+ * Endpoints for fetching CAS documents with instant download. Currently supports CDSL via
+ * OTP authentication.
+ */
override fun fetch(): FetchServiceAsync.WithRawResponse = fetch
private val parsePdfHandler: Handler =
diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/async/ContractNoteServiceAsync.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/async/ContractNoteServiceAsync.kt
index 6168665..2fee7ca 100644
--- a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/async/ContractNoteServiceAsync.kt
+++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/async/ContractNoteServiceAsync.kt
@@ -10,6 +10,10 @@ import com.cas_parser.api.models.contractnote.ContractNoteParseResponse
import java.util.concurrent.CompletableFuture
import java.util.function.Consumer
+/**
+ * Endpoints for parsing Contract Note PDF files from various SEBI brokers like Zerodha, Groww,
+ * Upstox, ICICI etc.
+ */
interface ContractNoteServiceAsync {
/**
diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/async/ContractNoteServiceAsyncImpl.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/async/ContractNoteServiceAsyncImpl.kt
index 7b8d062..b9c1014 100644
--- a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/async/ContractNoteServiceAsyncImpl.kt
+++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/async/ContractNoteServiceAsyncImpl.kt
@@ -20,6 +20,10 @@ import com.cas_parser.api.models.contractnote.ContractNoteParseResponse
import java.util.concurrent.CompletableFuture
import java.util.function.Consumer
+/**
+ * Endpoints for parsing Contract Note PDF files from various SEBI brokers like Zerodha, Groww,
+ * Upstox, ICICI etc.
+ */
class ContractNoteServiceAsyncImpl internal constructor(private val clientOptions: ClientOptions) :
ContractNoteServiceAsync {
diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/async/CreditServiceAsync.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/async/CreditServiceAsync.kt
index dd5a54f..e737edd 100644
--- a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/async/CreditServiceAsync.kt
+++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/async/CreditServiceAsync.kt
@@ -10,6 +10,10 @@ import com.cas_parser.api.models.credits.CreditCheckResponse
import java.util.concurrent.CompletableFuture
import java.util.function.Consumer
+/**
+ * Endpoints for checking API quota and credits usage. These endpoints help you monitor your API
+ * usage and remaining quota.
+ */
interface CreditServiceAsync {
/**
diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/async/CreditServiceAsyncImpl.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/async/CreditServiceAsyncImpl.kt
index 02b806c..e585dd5 100644
--- a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/async/CreditServiceAsyncImpl.kt
+++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/async/CreditServiceAsyncImpl.kt
@@ -20,6 +20,10 @@ import com.cas_parser.api.models.credits.CreditCheckResponse
import java.util.concurrent.CompletableFuture
import java.util.function.Consumer
+/**
+ * Endpoints for checking API quota and credits usage. These endpoints help you monitor your API
+ * usage and remaining quota.
+ */
class CreditServiceAsyncImpl internal constructor(private val clientOptions: ClientOptions) :
CreditServiceAsync {
diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/async/InboundEmailServiceAsync.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/async/InboundEmailServiceAsync.kt
index 8ff0edb..2c1b443 100644
--- a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/async/InboundEmailServiceAsync.kt
+++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/async/InboundEmailServiceAsync.kt
@@ -16,6 +16,29 @@ import com.cas_parser.api.models.inboundemail.InboundEmailRetrieveResponse
import java.util.concurrent.CompletableFuture
import java.util.function.Consumer
+/**
+ * Create dedicated inbound email addresses for investors to forward their CAS statements.
+ *
+ * **Use Case:** Your app wants to collect CAS statements from users without requiring OAuth or file
+ * upload.
+ *
+ * **How it works:**
+ * 1. Call `POST /v4/inbound-email` to create a unique inbound email address
+ * 2. Display this email to your user: "Forward your CAS statement to ie_xxx@import.casparser.in"
+ * 3. When user forwards a CAS email, we verify sender authenticity (SPF/DKIM) and call your webhook
+ * 4. Your webhook receives email metadata + attachment download URLs
+ *
+ * **Sender Validation:**
+ * - Only emails from verified CAS authorities are processed:
+ * - CDSL: `eCAS@cdslstatement.com`
+ * - NSDL: `NSDL-CAS@nsdl.co.in`
+ * - CAMS: `donotreply@camsonline.com`
+ * - KFintech: `samfS@kfintech.com`
+ * - Emails failing SPF/DKIM/DMARC are rejected
+ * - Forwarded emails must contain the original sender in headers
+ *
+ * **Billing:** 0.2 credits per successfully processed valid email
+ */
interface InboundEmailServiceAsync {
/**
diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/async/InboundEmailServiceAsyncImpl.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/async/InboundEmailServiceAsyncImpl.kt
index a199b5f..9131ae8 100644
--- a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/async/InboundEmailServiceAsyncImpl.kt
+++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/async/InboundEmailServiceAsyncImpl.kt
@@ -28,6 +28,29 @@ import java.util.concurrent.CompletableFuture
import java.util.function.Consumer
import kotlin.jvm.optionals.getOrNull
+/**
+ * Create dedicated inbound email addresses for investors to forward their CAS statements.
+ *
+ * **Use Case:** Your app wants to collect CAS statements from users without requiring OAuth or file
+ * upload.
+ *
+ * **How it works:**
+ * 1. Call `POST /v4/inbound-email` to create a unique inbound email address
+ * 2. Display this email to your user: "Forward your CAS statement to ie_xxx@import.casparser.in"
+ * 3. When user forwards a CAS email, we verify sender authenticity (SPF/DKIM) and call your webhook
+ * 4. Your webhook receives email metadata + attachment download URLs
+ *
+ * **Sender Validation:**
+ * - Only emails from verified CAS authorities are processed:
+ * - CDSL: `eCAS@cdslstatement.com`
+ * - NSDL: `NSDL-CAS@nsdl.co.in`
+ * - CAMS: `donotreply@camsonline.com`
+ * - KFintech: `samfS@kfintech.com`
+ * - Emails failing SPF/DKIM/DMARC are rejected
+ * - Forwarded emails must contain the original sender in headers
+ *
+ * **Billing:** 0.2 credits per successfully processed valid email
+ */
class InboundEmailServiceAsyncImpl internal constructor(private val clientOptions: ClientOptions) :
InboundEmailServiceAsync {
diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/async/InboxServiceAsync.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/async/InboxServiceAsync.kt
index b488405..d2a2a85 100644
--- a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/async/InboxServiceAsync.kt
+++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/async/InboxServiceAsync.kt
@@ -16,6 +16,23 @@ import com.cas_parser.api.models.inbox.InboxListCasFilesResponse
import java.util.concurrent.CompletableFuture
import java.util.function.Consumer
+/**
+ * Endpoints for importing CAS files directly from user email inboxes.
+ *
+ * **Supported Providers:** Gmail (more coming soon)
+ *
+ * **How it works:**
+ * 1. Call `POST /v4/inbox/connect` to get an OAuth URL
+ * 2. Redirect user to the OAuth URL for consent
+ * 3. User is redirected back to your `redirect_uri` with an encrypted `inbox_token`
+ * 4. Use the token to list/fetch CAS files from their inbox (`/v4/inbox/cas`)
+ * 5. Files are uploaded to temporary cloud storage (URLs expire in 24 hours)
+ *
+ * **Security:**
+ * - Read-only access (we cannot send emails)
+ * - Tokens are encrypted with server-side secret
+ * - User can revoke access anytime via `/v4/inbox/disconnect`
+ */
interface InboxServiceAsync {
/**
diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/async/InboxServiceAsyncImpl.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/async/InboxServiceAsyncImpl.kt
index 29c7d03..f3f2599 100644
--- a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/async/InboxServiceAsyncImpl.kt
+++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/async/InboxServiceAsyncImpl.kt
@@ -26,6 +26,23 @@ import com.cas_parser.api.models.inbox.InboxListCasFilesResponse
import java.util.concurrent.CompletableFuture
import java.util.function.Consumer
+/**
+ * Endpoints for importing CAS files directly from user email inboxes.
+ *
+ * **Supported Providers:** Gmail (more coming soon)
+ *
+ * **How it works:**
+ * 1. Call `POST /v4/inbox/connect` to get an OAuth URL
+ * 2. Redirect user to the OAuth URL for consent
+ * 3. User is redirected back to your `redirect_uri` with an encrypted `inbox_token`
+ * 4. Use the token to list/fetch CAS files from their inbox (`/v4/inbox/cas`)
+ * 5. Files are uploaded to temporary cloud storage (URLs expire in 24 hours)
+ *
+ * **Security:**
+ * - Read-only access (we cannot send emails)
+ * - Tokens are encrypted with server-side secret
+ * - User can revoke access anytime via `/v4/inbox/disconnect`
+ */
class InboxServiceAsyncImpl internal constructor(private val clientOptions: ClientOptions) :
InboxServiceAsync {
diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/async/KfintechServiceAsync.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/async/KfintechServiceAsync.kt
index fa22355..e2bfdc7 100644
--- a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/async/KfintechServiceAsync.kt
+++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/async/KfintechServiceAsync.kt
@@ -10,6 +10,7 @@ import com.cas_parser.api.models.kfintech.KfintechGenerateCasResponse
import java.util.concurrent.CompletableFuture
import java.util.function.Consumer
+/** Endpoints for generating new CAS documents via email mailback (KFintech). */
interface KfintechServiceAsync {
/**
diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/async/KfintechServiceAsyncImpl.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/async/KfintechServiceAsyncImpl.kt
index 2445261..df04e64 100644
--- a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/async/KfintechServiceAsyncImpl.kt
+++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/async/KfintechServiceAsyncImpl.kt
@@ -20,6 +20,7 @@ import com.cas_parser.api.models.kfintech.KfintechGenerateCasResponse
import java.util.concurrent.CompletableFuture
import java.util.function.Consumer
+/** Endpoints for generating new CAS documents via email mailback (KFintech). */
class KfintechServiceAsyncImpl internal constructor(private val clientOptions: ClientOptions) :
KfintechServiceAsync {
diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/async/LogServiceAsync.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/async/LogServiceAsync.kt
index 3f2546a..c74e947 100644
--- a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/async/LogServiceAsync.kt
+++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/async/LogServiceAsync.kt
@@ -12,6 +12,10 @@ import com.cas_parser.api.models.logs.LogGetSummaryResponse
import java.util.concurrent.CompletableFuture
import java.util.function.Consumer
+/**
+ * Endpoints for checking API quota and credits usage. These endpoints help you monitor your API
+ * usage and remaining quota.
+ */
interface LogServiceAsync {
/**
diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/async/LogServiceAsyncImpl.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/async/LogServiceAsyncImpl.kt
index a4bf0f2..f2cc6a7 100644
--- a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/async/LogServiceAsyncImpl.kt
+++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/async/LogServiceAsyncImpl.kt
@@ -22,6 +22,10 @@ import com.cas_parser.api.models.logs.LogGetSummaryResponse
import java.util.concurrent.CompletableFuture
import java.util.function.Consumer
+/**
+ * Endpoints for checking API quota and credits usage. These endpoints help you monitor your API
+ * usage and remaining quota.
+ */
class LogServiceAsyncImpl internal constructor(private val clientOptions: ClientOptions) :
LogServiceAsync {
diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/async/NsdlServiceAsync.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/async/NsdlServiceAsync.kt
index d9571cb..cb33723 100644
--- a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/async/NsdlServiceAsync.kt
+++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/async/NsdlServiceAsync.kt
@@ -10,6 +10,7 @@ import com.cas_parser.api.models.nsdl.NsdlParseParams
import java.util.concurrent.CompletableFuture
import java.util.function.Consumer
+/** Endpoints for parsing CAS PDF files from different sources. */
interface NsdlServiceAsync {
/**
diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/async/NsdlServiceAsyncImpl.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/async/NsdlServiceAsyncImpl.kt
index 8fd0b0c..ee79b39 100644
--- a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/async/NsdlServiceAsyncImpl.kt
+++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/async/NsdlServiceAsyncImpl.kt
@@ -20,6 +20,7 @@ import com.cas_parser.api.models.nsdl.NsdlParseParams
import java.util.concurrent.CompletableFuture
import java.util.function.Consumer
+/** Endpoints for parsing CAS PDF files from different sources. */
class NsdlServiceAsyncImpl internal constructor(private val clientOptions: ClientOptions) :
NsdlServiceAsync {
diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/async/SmartServiceAsync.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/async/SmartServiceAsync.kt
index 9fb8951..af7ed57 100644
--- a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/async/SmartServiceAsync.kt
+++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/async/SmartServiceAsync.kt
@@ -10,6 +10,7 @@ import com.cas_parser.api.models.smart.SmartParseCasPdfParams
import java.util.concurrent.CompletableFuture
import java.util.function.Consumer
+/** Endpoints for parsing CAS PDF files from different sources. */
interface SmartServiceAsync {
/**
diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/async/SmartServiceAsyncImpl.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/async/SmartServiceAsyncImpl.kt
index 618e4fc..02f8c0a 100644
--- a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/async/SmartServiceAsyncImpl.kt
+++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/async/SmartServiceAsyncImpl.kt
@@ -20,6 +20,7 @@ import com.cas_parser.api.models.smart.SmartParseCasPdfParams
import java.util.concurrent.CompletableFuture
import java.util.function.Consumer
+/** Endpoints for parsing CAS PDF files from different sources. */
class SmartServiceAsyncImpl internal constructor(private val clientOptions: ClientOptions) :
SmartServiceAsync {
diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/async/VerifyTokenServiceAsync.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/async/VerifyTokenServiceAsync.kt
index 3d8b41e..fa58dfc 100644
--- a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/async/VerifyTokenServiceAsync.kt
+++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/async/VerifyTokenServiceAsync.kt
@@ -10,6 +10,11 @@ import com.cas_parser.api.models.verifytoken.VerifyTokenVerifyResponse
import java.util.concurrent.CompletableFuture
import java.util.function.Consumer
+/**
+ * Endpoints for managing access tokens for the Portfolio Connect SDK. Use these to generate
+ * short-lived `at_` prefixed tokens that can be safely passed to frontend applications. Access
+ * tokens can be used in place of API keys on all v4 endpoints.
+ */
interface VerifyTokenServiceAsync {
/**
diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/async/VerifyTokenServiceAsyncImpl.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/async/VerifyTokenServiceAsyncImpl.kt
index 9a42c63..cf826af 100644
--- a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/async/VerifyTokenServiceAsyncImpl.kt
+++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/async/VerifyTokenServiceAsyncImpl.kt
@@ -20,6 +20,11 @@ import com.cas_parser.api.models.verifytoken.VerifyTokenVerifyResponse
import java.util.concurrent.CompletableFuture
import java.util.function.Consumer
+/**
+ * Endpoints for managing access tokens for the Portfolio Connect SDK. Use these to generate
+ * short-lived `at_` prefixed tokens that can be safely passed to frontend applications. Access
+ * tokens can be used in place of API keys on all v4 endpoints.
+ */
class VerifyTokenServiceAsyncImpl internal constructor(private val clientOptions: ClientOptions) :
VerifyTokenServiceAsync {
diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/async/cdsl/FetchServiceAsync.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/async/cdsl/FetchServiceAsync.kt
index 83b8d52..a60287d 100644
--- a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/async/cdsl/FetchServiceAsync.kt
+++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/async/cdsl/FetchServiceAsync.kt
@@ -12,6 +12,10 @@ import com.cas_parser.api.models.cdsl.fetch.FetchVerifyOtpResponse
import java.util.concurrent.CompletableFuture
import java.util.function.Consumer
+/**
+ * Endpoints for fetching CAS documents with instant download. Currently supports CDSL via OTP
+ * authentication.
+ */
interface FetchServiceAsync {
/**
diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/async/cdsl/FetchServiceAsyncImpl.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/async/cdsl/FetchServiceAsyncImpl.kt
index fb97ea9..9e9453a 100644
--- a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/async/cdsl/FetchServiceAsyncImpl.kt
+++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/async/cdsl/FetchServiceAsyncImpl.kt
@@ -24,6 +24,10 @@ import java.util.concurrent.CompletableFuture
import java.util.function.Consumer
import kotlin.jvm.optionals.getOrNull
+/**
+ * Endpoints for fetching CAS documents with instant download. Currently supports CDSL via OTP
+ * authentication.
+ */
class FetchServiceAsyncImpl internal constructor(private val clientOptions: ClientOptions) :
FetchServiceAsync {
diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/blocking/AccessTokenService.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/blocking/AccessTokenService.kt
index 784798e..cef38eb 100644
--- a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/blocking/AccessTokenService.kt
+++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/blocking/AccessTokenService.kt
@@ -10,6 +10,11 @@ import com.cas_parser.api.models.accesstoken.AccessTokenCreateResponse
import com.google.errorprone.annotations.MustBeClosed
import java.util.function.Consumer
+/**
+ * Endpoints for managing access tokens for the Portfolio Connect SDK. Use these to generate
+ * short-lived `at_` prefixed tokens that can be safely passed to frontend applications. Access
+ * tokens can be used in place of API keys on all v4 endpoints.
+ */
interface AccessTokenService {
/**
diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/blocking/AccessTokenServiceImpl.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/blocking/AccessTokenServiceImpl.kt
index 9fe0782..cc2c4c0 100644
--- a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/blocking/AccessTokenServiceImpl.kt
+++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/blocking/AccessTokenServiceImpl.kt
@@ -19,6 +19,11 @@ import com.cas_parser.api.models.accesstoken.AccessTokenCreateParams
import com.cas_parser.api.models.accesstoken.AccessTokenCreateResponse
import java.util.function.Consumer
+/**
+ * Endpoints for managing access tokens for the Portfolio Connect SDK. Use these to generate
+ * short-lived `at_` prefixed tokens that can be safely passed to frontend applications. Access
+ * tokens can be used in place of API keys on all v4 endpoints.
+ */
class AccessTokenServiceImpl internal constructor(private val clientOptions: ClientOptions) :
AccessTokenService {
diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/blocking/CamsKfintechService.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/blocking/CamsKfintechService.kt
index 294210c..c8841a5 100644
--- a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/blocking/CamsKfintechService.kt
+++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/blocking/CamsKfintechService.kt
@@ -10,6 +10,7 @@ import com.cas_parser.api.models.camskfintech.UnifiedResponse
import com.google.errorprone.annotations.MustBeClosed
import java.util.function.Consumer
+/** Endpoints for parsing CAS PDF files from different sources. */
interface CamsKfintechService {
/**
diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/blocking/CamsKfintechServiceImpl.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/blocking/CamsKfintechServiceImpl.kt
index 867c1c6..7796a20 100644
--- a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/blocking/CamsKfintechServiceImpl.kt
+++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/blocking/CamsKfintechServiceImpl.kt
@@ -19,6 +19,7 @@ import com.cas_parser.api.models.camskfintech.CamsKfintechParseParams
import com.cas_parser.api.models.camskfintech.UnifiedResponse
import java.util.function.Consumer
+/** Endpoints for parsing CAS PDF files from different sources. */
class CamsKfintechServiceImpl internal constructor(private val clientOptions: ClientOptions) :
CamsKfintechService {
diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/blocking/CdslService.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/blocking/CdslService.kt
index a8241be..7514c91 100644
--- a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/blocking/CdslService.kt
+++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/blocking/CdslService.kt
@@ -11,6 +11,7 @@ import com.cas_parser.api.services.blocking.cdsl.FetchService
import com.google.errorprone.annotations.MustBeClosed
import java.util.function.Consumer
+/** Endpoints for parsing CAS PDF files from different sources. */
interface CdslService {
/**
@@ -25,6 +26,10 @@ interface CdslService {
*/
fun withOptions(modifier: Consumer): CdslService
+ /**
+ * Endpoints for fetching CAS documents with instant download. Currently supports CDSL via OTP
+ * authentication.
+ */
fun fetch(): FetchService
/**
@@ -57,6 +62,10 @@ interface CdslService {
*/
fun withOptions(modifier: Consumer): CdslService.WithRawResponse
+ /**
+ * Endpoints for fetching CAS documents with instant download. Currently supports CDSL via
+ * OTP authentication.
+ */
fun fetch(): FetchService.WithRawResponse
/**
diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/blocking/CdslServiceImpl.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/blocking/CdslServiceImpl.kt
index 0155aa6..7bbc6a3 100644
--- a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/blocking/CdslServiceImpl.kt
+++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/blocking/CdslServiceImpl.kt
@@ -21,6 +21,7 @@ import com.cas_parser.api.services.blocking.cdsl.FetchService
import com.cas_parser.api.services.blocking.cdsl.FetchServiceImpl
import java.util.function.Consumer
+/** Endpoints for parsing CAS PDF files from different sources. */
class CdslServiceImpl internal constructor(private val clientOptions: ClientOptions) : CdslService {
private val withRawResponse: CdslService.WithRawResponse by lazy {
@@ -34,6 +35,10 @@ class CdslServiceImpl internal constructor(private val clientOptions: ClientOpti
override fun withOptions(modifier: Consumer): CdslService =
CdslServiceImpl(clientOptions.toBuilder().apply(modifier::accept).build())
+ /**
+ * Endpoints for fetching CAS documents with instant download. Currently supports CDSL via OTP
+ * authentication.
+ */
override fun fetch(): FetchService = fetch
override fun parsePdf(
@@ -60,6 +65,10 @@ class CdslServiceImpl internal constructor(private val clientOptions: ClientOpti
clientOptions.toBuilder().apply(modifier::accept).build()
)
+ /**
+ * Endpoints for fetching CAS documents with instant download. Currently supports CDSL via
+ * OTP authentication.
+ */
override fun fetch(): FetchService.WithRawResponse = fetch
private val parsePdfHandler: Handler =
diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/blocking/ContractNoteService.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/blocking/ContractNoteService.kt
index d7a1b64..bb49da3 100644
--- a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/blocking/ContractNoteService.kt
+++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/blocking/ContractNoteService.kt
@@ -10,6 +10,10 @@ import com.cas_parser.api.models.contractnote.ContractNoteParseResponse
import com.google.errorprone.annotations.MustBeClosed
import java.util.function.Consumer
+/**
+ * Endpoints for parsing Contract Note PDF files from various SEBI brokers like Zerodha, Groww,
+ * Upstox, ICICI etc.
+ */
interface ContractNoteService {
/**
diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/blocking/ContractNoteServiceImpl.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/blocking/ContractNoteServiceImpl.kt
index 978c42b..7d5555f 100644
--- a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/blocking/ContractNoteServiceImpl.kt
+++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/blocking/ContractNoteServiceImpl.kt
@@ -19,6 +19,10 @@ import com.cas_parser.api.models.contractnote.ContractNoteParseParams
import com.cas_parser.api.models.contractnote.ContractNoteParseResponse
import java.util.function.Consumer
+/**
+ * Endpoints for parsing Contract Note PDF files from various SEBI brokers like Zerodha, Groww,
+ * Upstox, ICICI etc.
+ */
class ContractNoteServiceImpl internal constructor(private val clientOptions: ClientOptions) :
ContractNoteService {
diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/blocking/CreditService.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/blocking/CreditService.kt
index 0c7a9da..d4459c9 100644
--- a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/blocking/CreditService.kt
+++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/blocking/CreditService.kt
@@ -10,6 +10,10 @@ import com.cas_parser.api.models.credits.CreditCheckResponse
import com.google.errorprone.annotations.MustBeClosed
import java.util.function.Consumer
+/**
+ * Endpoints for checking API quota and credits usage. These endpoints help you monitor your API
+ * usage and remaining quota.
+ */
interface CreditService {
/**
diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/blocking/CreditServiceImpl.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/blocking/CreditServiceImpl.kt
index c611592..6425e16 100644
--- a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/blocking/CreditServiceImpl.kt
+++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/blocking/CreditServiceImpl.kt
@@ -19,6 +19,10 @@ import com.cas_parser.api.models.credits.CreditCheckParams
import com.cas_parser.api.models.credits.CreditCheckResponse
import java.util.function.Consumer
+/**
+ * Endpoints for checking API quota and credits usage. These endpoints help you monitor your API
+ * usage and remaining quota.
+ */
class CreditServiceImpl internal constructor(private val clientOptions: ClientOptions) :
CreditService {
diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/blocking/InboundEmailService.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/blocking/InboundEmailService.kt
index c46275e..be0ff40 100644
--- a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/blocking/InboundEmailService.kt
+++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/blocking/InboundEmailService.kt
@@ -16,6 +16,29 @@ import com.cas_parser.api.models.inboundemail.InboundEmailRetrieveResponse
import com.google.errorprone.annotations.MustBeClosed
import java.util.function.Consumer
+/**
+ * Create dedicated inbound email addresses for investors to forward their CAS statements.
+ *
+ * **Use Case:** Your app wants to collect CAS statements from users without requiring OAuth or file
+ * upload.
+ *
+ * **How it works:**
+ * 1. Call `POST /v4/inbound-email` to create a unique inbound email address
+ * 2. Display this email to your user: "Forward your CAS statement to ie_xxx@import.casparser.in"
+ * 3. When user forwards a CAS email, we verify sender authenticity (SPF/DKIM) and call your webhook
+ * 4. Your webhook receives email metadata + attachment download URLs
+ *
+ * **Sender Validation:**
+ * - Only emails from verified CAS authorities are processed:
+ * - CDSL: `eCAS@cdslstatement.com`
+ * - NSDL: `NSDL-CAS@nsdl.co.in`
+ * - CAMS: `donotreply@camsonline.com`
+ * - KFintech: `samfS@kfintech.com`
+ * - Emails failing SPF/DKIM/DMARC are rejected
+ * - Forwarded emails must contain the original sender in headers
+ *
+ * **Billing:** 0.2 credits per successfully processed valid email
+ */
interface InboundEmailService {
/**
diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/blocking/InboundEmailServiceImpl.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/blocking/InboundEmailServiceImpl.kt
index b0aefb3..704f560 100644
--- a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/blocking/InboundEmailServiceImpl.kt
+++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/blocking/InboundEmailServiceImpl.kt
@@ -27,6 +27,29 @@ import com.cas_parser.api.models.inboundemail.InboundEmailRetrieveResponse
import java.util.function.Consumer
import kotlin.jvm.optionals.getOrNull
+/**
+ * Create dedicated inbound email addresses for investors to forward their CAS statements.
+ *
+ * **Use Case:** Your app wants to collect CAS statements from users without requiring OAuth or file
+ * upload.
+ *
+ * **How it works:**
+ * 1. Call `POST /v4/inbound-email` to create a unique inbound email address
+ * 2. Display this email to your user: "Forward your CAS statement to ie_xxx@import.casparser.in"
+ * 3. When user forwards a CAS email, we verify sender authenticity (SPF/DKIM) and call your webhook
+ * 4. Your webhook receives email metadata + attachment download URLs
+ *
+ * **Sender Validation:**
+ * - Only emails from verified CAS authorities are processed:
+ * - CDSL: `eCAS@cdslstatement.com`
+ * - NSDL: `NSDL-CAS@nsdl.co.in`
+ * - CAMS: `donotreply@camsonline.com`
+ * - KFintech: `samfS@kfintech.com`
+ * - Emails failing SPF/DKIM/DMARC are rejected
+ * - Forwarded emails must contain the original sender in headers
+ *
+ * **Billing:** 0.2 credits per successfully processed valid email
+ */
class InboundEmailServiceImpl internal constructor(private val clientOptions: ClientOptions) :
InboundEmailService {
diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/blocking/InboxService.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/blocking/InboxService.kt
index e2b5ce0..051365a 100644
--- a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/blocking/InboxService.kt
+++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/blocking/InboxService.kt
@@ -16,6 +16,23 @@ import com.cas_parser.api.models.inbox.InboxListCasFilesResponse
import com.google.errorprone.annotations.MustBeClosed
import java.util.function.Consumer
+/**
+ * Endpoints for importing CAS files directly from user email inboxes.
+ *
+ * **Supported Providers:** Gmail (more coming soon)
+ *
+ * **How it works:**
+ * 1. Call `POST /v4/inbox/connect` to get an OAuth URL
+ * 2. Redirect user to the OAuth URL for consent
+ * 3. User is redirected back to your `redirect_uri` with an encrypted `inbox_token`
+ * 4. Use the token to list/fetch CAS files from their inbox (`/v4/inbox/cas`)
+ * 5. Files are uploaded to temporary cloud storage (URLs expire in 24 hours)
+ *
+ * **Security:**
+ * - Read-only access (we cannot send emails)
+ * - Tokens are encrypted with server-side secret
+ * - User can revoke access anytime via `/v4/inbox/disconnect`
+ */
interface InboxService {
/**
diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/blocking/InboxServiceImpl.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/blocking/InboxServiceImpl.kt
index 27ebb03..4abb1e6 100644
--- a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/blocking/InboxServiceImpl.kt
+++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/blocking/InboxServiceImpl.kt
@@ -25,6 +25,23 @@ import com.cas_parser.api.models.inbox.InboxListCasFilesParams
import com.cas_parser.api.models.inbox.InboxListCasFilesResponse
import java.util.function.Consumer
+/**
+ * Endpoints for importing CAS files directly from user email inboxes.
+ *
+ * **Supported Providers:** Gmail (more coming soon)
+ *
+ * **How it works:**
+ * 1. Call `POST /v4/inbox/connect` to get an OAuth URL
+ * 2. Redirect user to the OAuth URL for consent
+ * 3. User is redirected back to your `redirect_uri` with an encrypted `inbox_token`
+ * 4. Use the token to list/fetch CAS files from their inbox (`/v4/inbox/cas`)
+ * 5. Files are uploaded to temporary cloud storage (URLs expire in 24 hours)
+ *
+ * **Security:**
+ * - Read-only access (we cannot send emails)
+ * - Tokens are encrypted with server-side secret
+ * - User can revoke access anytime via `/v4/inbox/disconnect`
+ */
class InboxServiceImpl internal constructor(private val clientOptions: ClientOptions) :
InboxService {
diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/blocking/KfintechService.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/blocking/KfintechService.kt
index 0bc26c9..b4ac124 100644
--- a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/blocking/KfintechService.kt
+++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/blocking/KfintechService.kt
@@ -10,6 +10,7 @@ import com.cas_parser.api.models.kfintech.KfintechGenerateCasResponse
import com.google.errorprone.annotations.MustBeClosed
import java.util.function.Consumer
+/** Endpoints for generating new CAS documents via email mailback (KFintech). */
interface KfintechService {
/**
diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/blocking/KfintechServiceImpl.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/blocking/KfintechServiceImpl.kt
index 679e4d8..aeccbb9 100644
--- a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/blocking/KfintechServiceImpl.kt
+++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/blocking/KfintechServiceImpl.kt
@@ -19,6 +19,7 @@ import com.cas_parser.api.models.kfintech.KfintechGenerateCasParams
import com.cas_parser.api.models.kfintech.KfintechGenerateCasResponse
import java.util.function.Consumer
+/** Endpoints for generating new CAS documents via email mailback (KFintech). */
class KfintechServiceImpl internal constructor(private val clientOptions: ClientOptions) :
KfintechService {
diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/blocking/LogService.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/blocking/LogService.kt
index 7361743..6c7468b 100644
--- a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/blocking/LogService.kt
+++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/blocking/LogService.kt
@@ -12,6 +12,10 @@ import com.cas_parser.api.models.logs.LogGetSummaryResponse
import com.google.errorprone.annotations.MustBeClosed
import java.util.function.Consumer
+/**
+ * Endpoints for checking API quota and credits usage. These endpoints help you monitor your API
+ * usage and remaining quota.
+ */
interface LogService {
/**
diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/blocking/LogServiceImpl.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/blocking/LogServiceImpl.kt
index 4dce634..d481476 100644
--- a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/blocking/LogServiceImpl.kt
+++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/blocking/LogServiceImpl.kt
@@ -21,6 +21,10 @@ import com.cas_parser.api.models.logs.LogGetSummaryParams
import com.cas_parser.api.models.logs.LogGetSummaryResponse
import java.util.function.Consumer
+/**
+ * Endpoints for checking API quota and credits usage. These endpoints help you monitor your API
+ * usage and remaining quota.
+ */
class LogServiceImpl internal constructor(private val clientOptions: ClientOptions) : LogService {
private val withRawResponse: LogService.WithRawResponse by lazy {
diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/blocking/NsdlService.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/blocking/NsdlService.kt
index 8ca3b7c..786d115 100644
--- a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/blocking/NsdlService.kt
+++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/blocking/NsdlService.kt
@@ -10,6 +10,7 @@ import com.cas_parser.api.models.nsdl.NsdlParseParams
import com.google.errorprone.annotations.MustBeClosed
import java.util.function.Consumer
+/** Endpoints for parsing CAS PDF files from different sources. */
interface NsdlService {
/**
diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/blocking/NsdlServiceImpl.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/blocking/NsdlServiceImpl.kt
index a887773..582c6df 100644
--- a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/blocking/NsdlServiceImpl.kt
+++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/blocking/NsdlServiceImpl.kt
@@ -19,6 +19,7 @@ import com.cas_parser.api.models.camskfintech.UnifiedResponse
import com.cas_parser.api.models.nsdl.NsdlParseParams
import java.util.function.Consumer
+/** Endpoints for parsing CAS PDF files from different sources. */
class NsdlServiceImpl internal constructor(private val clientOptions: ClientOptions) : NsdlService {
private val withRawResponse: NsdlService.WithRawResponse by lazy {
diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/blocking/SmartService.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/blocking/SmartService.kt
index fce30ed..79198c1 100644
--- a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/blocking/SmartService.kt
+++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/blocking/SmartService.kt
@@ -10,6 +10,7 @@ import com.cas_parser.api.models.smart.SmartParseCasPdfParams
import com.google.errorprone.annotations.MustBeClosed
import java.util.function.Consumer
+/** Endpoints for parsing CAS PDF files from different sources. */
interface SmartService {
/**
diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/blocking/SmartServiceImpl.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/blocking/SmartServiceImpl.kt
index 6808cd7..2c8c2d8 100644
--- a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/blocking/SmartServiceImpl.kt
+++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/blocking/SmartServiceImpl.kt
@@ -19,6 +19,7 @@ import com.cas_parser.api.models.camskfintech.UnifiedResponse
import com.cas_parser.api.models.smart.SmartParseCasPdfParams
import java.util.function.Consumer
+/** Endpoints for parsing CAS PDF files from different sources. */
class SmartServiceImpl internal constructor(private val clientOptions: ClientOptions) :
SmartService {
diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/blocking/VerifyTokenService.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/blocking/VerifyTokenService.kt
index e8135cd..468e98d 100644
--- a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/blocking/VerifyTokenService.kt
+++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/blocking/VerifyTokenService.kt
@@ -10,6 +10,11 @@ import com.cas_parser.api.models.verifytoken.VerifyTokenVerifyResponse
import com.google.errorprone.annotations.MustBeClosed
import java.util.function.Consumer
+/**
+ * Endpoints for managing access tokens for the Portfolio Connect SDK. Use these to generate
+ * short-lived `at_` prefixed tokens that can be safely passed to frontend applications. Access
+ * tokens can be used in place of API keys on all v4 endpoints.
+ */
interface VerifyTokenService {
/**
diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/blocking/VerifyTokenServiceImpl.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/blocking/VerifyTokenServiceImpl.kt
index bf3fce7..e76ff7d 100644
--- a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/blocking/VerifyTokenServiceImpl.kt
+++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/blocking/VerifyTokenServiceImpl.kt
@@ -19,6 +19,11 @@ import com.cas_parser.api.models.verifytoken.VerifyTokenVerifyParams
import com.cas_parser.api.models.verifytoken.VerifyTokenVerifyResponse
import java.util.function.Consumer
+/**
+ * Endpoints for managing access tokens for the Portfolio Connect SDK. Use these to generate
+ * short-lived `at_` prefixed tokens that can be safely passed to frontend applications. Access
+ * tokens can be used in place of API keys on all v4 endpoints.
+ */
class VerifyTokenServiceImpl internal constructor(private val clientOptions: ClientOptions) :
VerifyTokenService {
diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/blocking/cdsl/FetchService.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/blocking/cdsl/FetchService.kt
index 81f2bc3..b8f7006 100644
--- a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/blocking/cdsl/FetchService.kt
+++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/blocking/cdsl/FetchService.kt
@@ -12,6 +12,10 @@ import com.cas_parser.api.models.cdsl.fetch.FetchVerifyOtpResponse
import com.google.errorprone.annotations.MustBeClosed
import java.util.function.Consumer
+/**
+ * Endpoints for fetching CAS documents with instant download. Currently supports CDSL via OTP
+ * authentication.
+ */
interface FetchService {
/**
diff --git a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/blocking/cdsl/FetchServiceImpl.kt b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/blocking/cdsl/FetchServiceImpl.kt
index e9a3246..9c8db9f 100644
--- a/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/blocking/cdsl/FetchServiceImpl.kt
+++ b/cas-parser-java-core/src/main/kotlin/com/cas_parser/api/services/blocking/cdsl/FetchServiceImpl.kt
@@ -23,6 +23,10 @@ import com.cas_parser.api.models.cdsl.fetch.FetchVerifyOtpResponse
import java.util.function.Consumer
import kotlin.jvm.optionals.getOrNull
+/**
+ * Endpoints for fetching CAS documents with instant download. Currently supports CDSL via OTP
+ * authentication.
+ */
class FetchServiceImpl internal constructor(private val clientOptions: ClientOptions) :
FetchService {
diff --git a/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/core/http/HttpRequestBodiesTest.kt b/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/core/http/HttpRequestBodiesTest.kt
new file mode 100644
index 0000000..8b9e0fd
--- /dev/null
+++ b/cas-parser-java-core/src/test/kotlin/com/cas_parser/api/core/http/HttpRequestBodiesTest.kt
@@ -0,0 +1,729 @@
+// File generated from our OpenAPI spec by Stainless.
+
+package com.cas_parser.api.core.http
+
+import com.cas_parser.api.core.MultipartField
+import com.cas_parser.api.core.jsonMapper
+import java.io.ByteArrayOutputStream
+import java.io.InputStream
+import org.assertj.core.api.Assertions.assertThat
+import org.junit.jupiter.api.Test
+
+internal class HttpRequestBodiesTest {
+
+ @Test
+ fun multipartFormData_serializesFieldWithFilename() {
+ val body =
+ multipartFormData(
+ jsonMapper(),
+ mapOf(
+ "file" to
+ MultipartField.builder()
+ .value("hello")
+ .filename("hello.txt")
+ .contentType("text/plain")
+ .build()
+ ),
+ )
+
+ val output = ByteArrayOutputStream()
+ body.writeTo(output)
+
+ assertThat(body.repeatable()).isTrue()
+ assertThat(output.size().toLong()).isEqualTo(body.contentLength())
+ val boundary = body.contentType()!!.substringAfter("multipart/form-data; boundary=")
+ assertThat(output.toString("UTF-8"))
+ .isEqualTo(
+ """
+ |--$boundary
+ |Content-Disposition: form-data; name="file"; filename="hello.txt"
+ |Content-Type: text/plain
+ |
+ |hello
+ |--$boundary--
+ |
+ """
+ .trimMargin()
+ .replace("\n", "\r\n")
+ )
+ }
+
+ @Test
+ fun multipartFormData_serializesFieldWithoutFilename() {
+ val body =
+ multipartFormData(
+ jsonMapper(),
+ mapOf(
+ "field" to
+ MultipartField.builder()
+ .value("value")
+ .contentType("text/plain")
+ .build()
+ ),
+ )
+
+ val output = ByteArrayOutputStream()
+ body.writeTo(output)
+
+ assertThat(body.repeatable()).isTrue()
+ assertThat(output.size().toLong()).isEqualTo(body.contentLength())
+ val boundary = boundary(body)
+ assertThat(output.toString("UTF-8"))
+ .isEqualTo(
+ """
+ |--$boundary
+ |Content-Disposition: form-data; name="field"
+ |Content-Type: text/plain
+ |
+ |value
+ |--$boundary--
+ |
+ """
+ .trimMargin()
+ .replace("\n", "\r\n")
+ )
+ }
+
+ @Test
+ fun multipartFormData_serializesInputStream() {
+ // Use `.buffered()` to get a non-ByteArrayInputStream, which hits the non-repeatable code
+ // path.
+ val inputStream = "stream content".byteInputStream().buffered()
+ val body =
+ multipartFormData(
+ jsonMapper(),
+ mapOf(
+ "data" to
+ MultipartField.builder()
+ .value(inputStream)
+ .contentType("application/octet-stream")
+ .build()
+ ),
+ )
+
+ val output = ByteArrayOutputStream()
+ body.writeTo(output)
+
+ assertThat(body.repeatable()).isFalse()
+ assertThat(body.contentLength()).isEqualTo(-1L)
+ val boundary = boundary(body)
+ assertThat(output.toString("UTF-8"))
+ .isEqualTo(
+ """
+ |--$boundary
+ |Content-Disposition: form-data; name="data"
+ |Content-Type: application/octet-stream
+ |
+ |stream content
+ |--$boundary--
+ |
+ """
+ .trimMargin()
+ .replace("\n", "\r\n")
+ )
+ }
+
+ @Test
+ fun multipartFormData_serializesByteArray() {
+ val body =
+ multipartFormData(
+ jsonMapper(),
+ mapOf(
+ "binary" to
+ MultipartField.builder()
+ .value("abc".toByteArray())
+ .contentType("application/octet-stream")
+ .build()
+ ),
+ )
+
+ val output = ByteArrayOutputStream()
+ body.writeTo(output)
+
+ assertThat(body.repeatable()).isTrue()
+ assertThat(body.contentLength()).isEqualTo(output.size().toLong())
+ val boundary = boundary(body)
+ assertThat(output.toString("UTF-8"))
+ .isEqualTo(
+ """
+ |--$boundary
+ |Content-Disposition: form-data; name="binary"
+ |Content-Type: application/octet-stream
+ |
+ |abc
+ |--$boundary--
+ |
+ """
+ .trimMargin()
+ .replace("\n", "\r\n")
+ )
+ }
+
+ @Test
+ fun multipartFormData_serializesBooleanValue() {
+ val body =
+ multipartFormData(
+ jsonMapper(),
+ mapOf(
+ "flag" to
+ MultipartField.builder()
+ .value(true)
+ .contentType("text/plain")
+ .build()
+ ),
+ )
+
+ val output = ByteArrayOutputStream()
+ body.writeTo(output)
+
+ assertThat(body.repeatable()).isTrue()
+ assertThat(body.contentLength()).isEqualTo(output.size().toLong())
+ val boundary = boundary(body)
+ assertThat(output.toString("UTF-8"))
+ .isEqualTo(
+ """
+ |--$boundary
+ |Content-Disposition: form-data; name="flag"
+ |Content-Type: text/plain
+ |
+ |true
+ |--$boundary--
+ |
+ """
+ .trimMargin()
+ .replace("\n", "\r\n")
+ )
+ }
+
+ @Test
+ fun multipartFormData_serializesNumberValue() {
+ val body =
+ multipartFormData(
+ jsonMapper(),
+ mapOf(
+ "count" to
+ MultipartField.builder().value(42).contentType("text/plain").build()
+ ),
+ )
+
+ val output = ByteArrayOutputStream()
+ body.writeTo(output)
+
+ assertThat(body.repeatable()).isTrue()
+ assertThat(body.contentLength()).isEqualTo(output.size().toLong())
+ val boundary = boundary(body)
+ assertThat(output.toString("UTF-8"))
+ .isEqualTo(
+ """
+ |--$boundary
+ |Content-Disposition: form-data; name="count"
+ |Content-Type: text/plain
+ |
+ |42
+ |--$boundary--
+ |
+ """
+ .trimMargin()
+ .replace("\n", "\r\n")
+ )
+ }
+
+ @Test
+ fun multipartFormData_serializesNullValueAsNoParts() {
+ val body =
+ multipartFormData(
+ jsonMapper(),
+ mapOf(
+ "present" to
+ MultipartField.builder()
+ .value("yes")
+ .contentType("text/plain")
+ .build(),
+ "absent" to
+ MultipartField.builder()
+ .value(null as String?)
+ .contentType("text/plain")
+ .build(),
+ ),
+ )
+
+ val output = ByteArrayOutputStream()
+ body.writeTo(output)
+
+ assertThat(body.repeatable()).isTrue()
+ assertThat(body.contentLength()).isEqualTo(output.size().toLong())
+ val boundary = boundary(body)
+ assertThat(output.toString("UTF-8"))
+ .isEqualTo(
+ """
+ |--$boundary
+ |Content-Disposition: form-data; name="present"
+ |Content-Type: text/plain
+ |
+ |yes
+ |--$boundary--
+ |
+ """
+ .trimMargin()
+ .replace("\n", "\r\n")
+ )
+ }
+
+ @Test
+ fun multipartFormData_serializesArray() {
+ val body =
+ multipartFormData(
+ jsonMapper(),
+ mapOf(
+ "items" to
+ MultipartField.builder>()
+ .value(listOf("alpha", "beta", "gamma"))
+ .contentType("text/plain")
+ .build()
+ ),
+ )
+
+ val output = ByteArrayOutputStream()
+ body.writeTo(output)
+
+ assertThat(body.repeatable()).isTrue()
+ assertThat(body.contentLength()).isEqualTo(output.size().toLong())
+ val boundary = boundary(body)
+ assertThat(output.toString("UTF-8"))
+ .isEqualTo(
+ """
+ |--$boundary
+ |Content-Disposition: form-data; name="items"
+ |Content-Type: text/plain
+ |
+ |alpha,beta,gamma
+ |--$boundary--
+ |
+ """
+ .trimMargin()
+ .replace("\n", "\r\n")
+ )
+ }
+
+ @Test
+ fun multipartFormData_serializesObjectAsNestedParts() {
+ val body =
+ multipartFormData(
+ jsonMapper(),
+ mapOf(
+ "meta" to
+ MultipartField.builder