npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@cdx-forge/di-typescript-sdk

v1.2.0

Published

TypeScript server SDK for the Candescent Digital Insight API

Readme

Candescent Digital Insight — TypeScript SDK

npm version OpenAPI spec downloads node

Server-side TypeScript SDK for the Candescent Digital Insight API. Covers all API operations with full type safety, automatic OAuth token management, and built-in retry logic.

| Package | Version | |---------|---------| | This package (@cdx-forge/di-typescript-sdk) | 1.2.0 | | OpenAPI spec (candescent-dev/openapi) | 1.8.0 |

The npm package version and the OpenAPI specification version are tracked separately. See Versioning and CHANGELOG.md.

Overview

This SDK is the recommended way for server-side TypeScript and Node.js applications to integrate with the Candescent Digital Insight platform. It wraps the full REST API surface — accounts, transactions, customer management, business banking, alerts, money movement, notifications, disclosures, e-statements, and MX services — behind a single typed client that handles the protocol complexity so your application does not have to.

Key things the SDK takes care of on your behalf:

  • OAuth token management — acquires, caches, and refreshes tokens automatically across two token endpoint families (V1 and V2), and injects the correct Authorization header on every request.
  • Typed models — every request parameter and response shape is generated from the OpenAPI specification, giving you autocomplete and compile-time safety.
  • Error hierarchy — HTTP error codes are mapped to typed exceptions (NotFoundError, RateLimitError, etc.) so you can catch exactly what you need.
  • Pagination — an async-iterable PageIterator handles all four pagination patterns used across the API.
  • Retry with backoff — transient failures (408, 429, 5xx) are retried automatically with exponential backoff.

The SDK is ESM-only and requires Node.js 20+. For browser or edge runtimes, call the REST API directly using the Candescent API reference.

Documentation

See the Candescent API reference for full endpoint documentation, and the examples folder for runnable code across every service area.

Requirements

| Requirement | Details | |-------------|---------| | Node.js | 20+ | | Module system | ESM only — use import syntax. require() will throw ERR_REQUIRE_ESM (see Troubleshooting) |

Installation

npm install @cdx-forge/di-typescript-sdk
# or
yarn add @cdx-forge/di-typescript-sdk
# or
pnpm add @cdx-forge/di-typescript-sdk

Usage

The client is initialized once and reused across your application. It accepts credentials directly or reads them from environment variables. All service areas are available as properties on the client instance, and every method is fully typed — parameters, responses, and errors.

Configure the client with your credentials from the Candescent Developer Console:

import { CandescentClient, Environment } from "@cdx-forge/di-typescript-sdk";

const client = new CandescentClient({
  clientId: process.env.CANDESCENT_CLIENT_ID!,
  clientSecret: process.env.CANDESCENT_CLIENT_SECRET!,
  institutionId: process.env.CANDESCENT_INSTITUTION_ID!,
  environment: Environment.Stage, // or Environment.Production
});

const accounts = await client.accounts.list({ hostUserId: "user-12345" });
console.log(`Found ${accounts.accounts?.length ?? 0} accounts`);

await client.close();

Or load all credentials from environment variables:

const client = CandescentClient.fromEnv();

The client is organized by service area. Each property maps to a group of fully typed API operations:

| Property | Service area | |----------|-------------| | client.accounts | Account listing, account details, transactions | | client.customerManagement | Customer registration, lookup, profile management | | client.authentication | Token creation and revocation | | client.businessBanking | Business entitlements, business details | | client.alerts | Alert configuration, delivery, preferences | | client.moneyMovement | Payments and transfers | | client.notifications | Notification channels and subscriptions | | client.disclosures | Disclosure management | | client.estatements | E-statement access | | client.mxPlatform | MX Platform proxy APIs | | client.realTime | MX Real Time proxy APIs | | client.reporting | MX Reporting proxy APIs | | client.sso | MX SSO proxy APIs |

Standalone functions

For serverless environments (e.g. AWS Lambda) where managing a persistent client is impractical, import individual operation functions directly:

import { listAccounts, getAccount } from "@cdx-forge/di-typescript-sdk/operations";

// Reads credentials from process.env.CANDESCENT_*
const accounts = await listAccounts({ hostUserId: "user-12345" });
const account  = await getAccount({ accountId: "acc-abc123" });

Authentication

The package needs to be configured with your Client ID, Client Secret, and Institution ID, available in the Candescent Developer Console.

The SDK obtains and caches OAuth tokens automatically, refreshes them before expiry, and injects the correct Authorization header on every request — you never manage tokens directly.

OAuth V1 and V2

The Candescent API uses two distinct OAuth token endpoint families that exist for historical reasons. Newer endpoints use the V2 standard JSON flow; a subset of legacy endpoints require the older V1 flow (which may return XML). The SDK handles both transparently:

| | V1 (Legacy) | V2 (Current) | |---|---|---| | Token endpoint | POST /v1/oauth/token | POST /oauth2/v1/token | | Token lifetime | ~30 minutes | ~1 hour | | Response format | XML or JSON | JSON | | Typical operations | Customer registration, account listing via /bankingservices/, notification channels | Accounts, alerts, MX, money movement, disclosures, e-statements |

The SDK inspects the request URL path at call time and selects the correct token provider automatically — V1 for paths containing /bankingservices/, /registration/, /destinations/, /subscriptions/, or /send-event/, and V2 for everything else. Tokens are cached and reused for their lifetime, then refreshed proactively before expiry. XML token responses from V1 endpoints are parsed internally; you always receive typed TypeScript objects regardless of what the wire format was.

No configuration is needed beyond your client credentials. If you already have a pre-obtained JWT, you can pass it directly and skip the OAuth flow entirely (see Using a static bearer token below).

Using environment variables

export CANDESCENT_CLIENT_ID=your-client-id
export CANDESCENT_CLIENT_SECRET=your-client-secret
export CANDESCENT_INSTITUTION_ID=your-institution-id
export CANDESCENT_ENVIRONMENT=production  # or stage (default)
const client = CandescentClient.fromEnv();

Scripts do not load .env files automatically. Run source .env first, or use dotenv.

Using a static bearer token

If you have a pre-obtained JWT, pass it directly — the OAuth flow is skipped:

import { CandescentClient } from "@cdx-forge/di-typescript-sdk";

const client = new CandescentClient({
  bearerToken: "your-pre-obtained-jwt",
  institutionId: "your-institution-id",
});

Environment variables reference

| Variable | Required | Description | |----------|----------|-------------| | CANDESCENT_INSTITUTION_ID | Always | Your institution identifier | | CANDESCENT_CLIENT_ID | Yes* | OAuth 2.0 client ID | | CANDESCENT_CLIENT_SECRET | Yes* | OAuth 2.0 client secret | | CANDESCENT_BEARER_TOKEN | Yes* | Pre-obtained JWT (skips OAuth flow) | | CANDESCENT_ENVIRONMENT | No | stage (default) or production | | CANDESCENT_USERNAME | No | Password grant username | | CANDESCENT_PASSWORD | No | Password grant password |

*Provide either CANDESCENT_BEARER_TOKEN or CANDESCENT_CLIENT_ID + CANDESCENT_CLIENT_SECRET.

Error handling

Every non-2xx response is surfaced as a typed exception rather than a generic Error, so you can handle specific failure modes (not found, rate limited, unauthenticated) without parsing status codes yourself. Catch the base ApiError for a catch-all, or catch specific subclasses for targeted handling:

import {
  ApiError,
  AuthenticationError,
  NotFoundError,
  RateLimitError,
} from "@cdx-forge/di-typescript-sdk";

try {
  const account = await client.accounts.get({ accountId: "acc-not-found" });
} catch (error) {
  if (error instanceof NotFoundError) {
    console.error("Account not found:", error.message);
  } else if (error instanceof RateLimitError) {
    console.warn(`Rate limited. Retry after ${error.retryAfter}s`);
  } else if (error instanceof AuthenticationError) {
    console.error("Check your credentials");
  } else if (error instanceof ApiError) {
    console.error(`API error ${error.statusCode}:`, error.message);
  } else {
    throw error;
  }
}

| HTTP status | Exception | |-------------|-----------| | Any non-2xx | ApiError (base) | | 400 | BadRequestError | | 401 | AuthenticationError | | 403 | PermissionDeniedError | | 404 | NotFoundError | | 409 | ConflictError | | 422 | UnprocessableEntityError | | 429 | RateLimitError | | 5xx | InternalServerError |

Pagination

The Candescent API uses several different pagination styles across its endpoints (offset-based, cursor-based). PageIterator abstracts over all of them with a single async iterable interface — you iterate over items and the SDK fetches the next page when needed, without you tracking page numbers or tokens:

import { PageIterator } from "@cdx-forge/di-typescript-sdk";

for await (const account of new PageIterator(
  (req) => client.accounts.list({ hostUserId: "user-12345", ...req }),
  { page: 0, size: 50 },
)) {
  console.log(account.accountId);
}

To collect all results into an array:

const allAccounts = [];
for await (const account of new PageIterator(
  (req) => client.accounts.list({ hostUserId: "user-12345", ...req }),
)) {
  allAccounts.push(account);
}

Retry behavior

The SDK automatically retries transient failures with exponential backoff:

| Retried on | Max retries | Initial delay | Max delay | |------------|-------------|---------------|-----------| | 408, 429, 500, 502, 503, 504 | 2 (3 total attempts) | 500 ms | 30 s |

RateLimitError is thrown only after all retries are exhausted. Check error.retryAfter for the value from the Retry-After response header.

Framework integration

The client is stateful (it holds a token cache) and safe to instantiate once at application startup and share across requests. For serverless functions where a persistent instance is impractical, use the standalone functions export instead (see Standalone functions above, and the AWS Lambda example below).

Express / Fastify

import express from "express";
import { CandescentClient } from "@cdx-forge/di-typescript-sdk";

const app = express();
const client = CandescentClient.fromEnv();

app.get("/accounts/:userId", async (req, res) => {
  const accounts = await client.accounts.list({ hostUserId: req.params.userId });
  res.json(accounts);
});

AWS Lambda

Use the standalone functions export to avoid managing client lifecycle across invocations:

import { listAccounts } from "@cdx-forge/di-typescript-sdk/operations";

export const handler = async (event: { userId: string }) => {
  const accounts = await listAccounts({ hostUserId: event.userId });
  return { statusCode: 200, body: JSON.stringify(accounts) };
};

Lifecycle management

Call client.close() on shutdown to revoke cached tokens:

await client.close();

Hand-written wrapper layer

The SDK is built on top of auto-generated OpenAPI client stubs, but the code your application actually interacts with is entirely hand-written and maintained separately from the generated output. The durable layer lives in src/ and consists of:

| File | What it does | |------|-------------| | client.ts | CandescentClient facade — mounts all service areas and runs the middleware pipeline | | auth.ts | V1 and V2 token providers — token acquisition, caching, pre-expiry refresh, and per-request routing | | transport.ts | Middleware — injects auth and tracing headers; converts XML responses to JSON before deserialization | | errors.ts | Maps HTTP status codes to typed ApiError subclasses | | pagination.ts | PageIterator — async iterable supporting offset and cursor pagination patterns | | retry.ts | Exponential backoff with jitter for transient failures | | operations.ts | Standalone functions for serverless environments |

This layer is what survives when the SDK is regenerated from a new OpenAPI spec. If you want to understand exactly how a request flows — from your call site through auth, headers, retries, and back — the source files above are the place to look. You can browse them directly on GitHub.

Configuration

import { CandescentClient, Environment } from "@cdx-forge/di-typescript-sdk";

const client = new CandescentClient({
  // --- Required (one of the two auth methods) ---
  clientId: "...",            // OAuth client ID
  clientSecret: "...",        // OAuth client secret
  institutionId: "...",       // Your institution identifier

  // OR use a static token instead of client credentials:
  bearerToken: "...",

  environment: Environment.Production,  // Environment.Stage (default)
  baseUrl: "https://custom.api.host",   // Override base URL (advanced)
});

Examples

The examples/typescript/ folder on GitHub contains runnable scripts that show how to consume the SDK end-to-end across every service area. These are the best starting point for understanding real usage patterns — each script is a self-contained illustration of how to initialise the client, make calls, handle responses, and clean up.

Coverage includes: accounts, transactions, authentication and token lifecycle, customer registration and lookup, business banking (registration, entitlements, payments), alerts (configuration, preferences, delivery), money movement, MX integration, notifications, disclosures, e-statements, pagination with PageIterator, and the full error handling hierarchy.

Key characteristics of the example scripts:

  • Each script is independent. You can run a single example to explore one service area without needing to run them all.
  • Error handling is a separate script (error-handling.ts) that exercises intentional failures and is excluded from the batch runner by design.
  • The batch runner (node scripts/run-all-typescript-examples.mjs) runs all examples in sequence and writes a log, which is useful for verifying your credentials work across the full API surface.

See the Examples README for environment setup, how to run individual scripts, and the full list of example files with what each one covers.

Troubleshooting

CommonJS require() not working

This package is ESM-only. Your project must use "type": "module" in package.json or use .mjs file extensions. require('@cdx-forge/di-typescript-sdk') will throw ERR_REQUIRE_ESM.

Cannot find module '@cdx-forge/di-typescript-sdk'

Run npm install @cdx-forge/di-typescript-sdk and confirm the package is listed in your package.json dependencies.

Authentication errors (401)

  • Verify CANDESCENT_CLIENT_ID and CANDESCENT_CLIENT_SECRET are correct.
  • Confirm CANDESCENT_INSTITUTION_ID matches the credentials issued to you.
  • For staging environments, ensure CANDESCENT_ENVIRONMENT=stage.

"Parameters hostUserId and loginId are mutually exclusive"

Pass only one user identifier per request — hostUserId or loginId, not both.

Versioning

This SDK follows semantic versioning independently from the Candescent DI OpenAPI specification. The SDK version and spec version are tracked separately.

| Package | Version | |---------|---------| | SDK (@cdx-forge/di-typescript-sdk) | 1.2.0 | | OpenAPI spec (candescent-dev/openapi) | 1.8.0 |

Each SDK release is pinned to a specific spec version. See CHANGELOG.md for the spec version used in each release.


License

Copyright © Candescent. All rights reserved. The Digital Insight TypeScript SDK is proprietary software.

See LICENSE for the proprietary terms.


Support