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

@thredfi/accounting

v0.10.0

Published

Embeddable accounting widget for integrating Thredfi accounting features into your application

Downloads

607

Readme

Embeddable accounting widget by Thredfi


📦 Installation

npm install @thredfi/accounting

🚀 Quick Start

ES Module (npm/bundler)

For Registered Businesses

import {
  mountThredfi,
  unmountThredfi,
  setThredfiLanguage,
  type TokenFetcher,
  type TokenResponse,
} from "@thredfi/accounting";

mountThredfi({
  // CSS selector for the container element
  thredfiSelector: "#thredfi-widget",
  // Function to fetch authentication token from your backend
  getToken: async () => {
    const response = await fetch("https://your-api.com/thredfi-token", {
      headers: {
        Authorization: "Bearer your-auth-token",
      },
    });
    return await response.json();
  },
  // Your business UUID in the Thredfi system
  businessId: "964f4325-3efb-400d-a1bd-8b1f29e828cf",
  lang: "en",
  environment: "production", // "development" | "staging" | "sandbox" | "production" | "local"
  // Theme customization (optional)
  theme: {
    navigationBackgroundColor: "#f9f9f9",
    cardsBackgroundColor: "#ffffff",
    textColor: "#111827",
    textSecondaryColor: "#6b7280",
    activeColor: "#871f91",
    borderColor: "#dcdcdc",
    borderSecondaryColor: "#e5e7eb",
  },
  // Base path for routing within the widget
  basePath: "/accounting",
  // Hide the header navigation menu (optional)
  hideMenu: false,
});

For Unregistered Businesses

If the business is not yet registered in Thredfi, you can mount the widget with registration details. The user will see a sales/onboarding page instead of the full accounting interface:

import { mountThredfi, type RegistrationDetails } from "@thredfi/accounting";

mountThredfi({
  thredfiSelector: "#thredfi-widget",
  registrationDetails: {
    partnerId: "partner-uuid",
    businessName: "Acme Corporation",
    personName: "John Doe",
    emailId: "[email protected]",
    phone: "+1234567890", // Optional
    countryCode: "US",
  },
  lang: "en",
  environment: "production",
  basePath: "/my-app/sales", // Optional: Base path for routing
});

CDN Usage (UMD)

Use directly in the browser without a bundler:

For Registered Businesses

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>My App with Thredfi</title>
  </head>
  <body>
    <!-- Container for the widget -->
    <div id="thredfi-widget"></div>

    <!-- Load from CDN -->
    <script src="https://unpkg.com/@thredfi/accounting@latest/dist/index.umd.js"></script>
    <script>
      // Access via global ThredfiAccounting object
      ThredfiAccounting.mountThredfi({
        thredfiSelector: "#thredfi-widget",
        getToken: async () => {
          const response = await fetch("https://your-api.com/thredfi-token", {
            headers: {
              Authorization: "Bearer your-auth-token",
            },
          });
          return await response.json();
        },
        businessId: "964f4325-3efb-400d-a1bd-8b1f29e828cf",
        environment: "production",
      });
    </script>
  </body>
</html>

For Unregistered Businesses

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>Thredfi Sales</title>
  </head>
  <body>
    <div id="thredfi-widget"></div>

    <script src="https://unpkg.com/@thredfi/accounting@latest/dist/index.umd.js"></script>
    <script>
      ThredfiAccounting.mountThredfi({
        thredfiSelector: "#thredfi-widget",
        registrationDetails: {
          partnerId: "partner-uuid",
          businessName: "Acme Corporation",
          personName: "John Doe",
          emailId: "[email protected]",
          countryCode: "US",
        },
        environment: "production",
        basePath: "/sales", // Optional: Base path for routing
      });
    </script>
  </body>
</html>

📖 API Reference

mountThredfi(options)

Mounts the Thredfi accounting widget in the specified container.

The widget supports two modes:

  • Registered businesses: Full accounting interface with authentication
  • Unregistered businesses: Sales/onboarding page with registration details

Options

// Shared configuration for both modes
interface BaseMountOptions {
  thredfiSelector: string; // Required: CSS selector for container
  lang?: string; // Optional: Language code (default: "en")
  environment?: "development" | "staging" | "sandbox" | "production" | "local"; // Optional: Backend environment (default: "sandbox")
  basePath?: string; // Optional: Base path for routing (default: "")
  theme?: ThemeConfig; // Optional: Color overrides for theming
  hideMenu?: boolean; // Optional: Hide header navigation (default: false)
}

// Case A: Registered businesses
interface RegisteredOptions extends BaseMountOptions {
  businessId: string; // Required: Business UUID in Thredfi system
  getToken: () => Promise<{
    // Required: Token fetcher function
    access_token: string;
    expires_in: number;
  }>;
  registrationDetails?: never; // Cannot be used with businessId
}

// Case B: Unregistered businesses
interface UnregisteredOptions extends BaseMountOptions {
  registrationDetails: RegistrationDetails; // Required: Registration info
  businessId?: never; // Cannot be used with registrationDetails
  getToken?: never; // Not needed for unregistered businesses
}

// Registration details for businesses not yet in Thredfi
interface RegistrationDetails {
  partnerId: string; // Required: Partner UUID (must be valid UUID format)
  businessName: string; // Required: Business/company name (non-empty string)
  personName: string; // Required: Contact person name (non-empty string)
  emailId: string; // Required: Contact email (must be valid email format)
  phone?: string; // Optional: Contact phone number (non-empty string if provided)
  countryCode: string; // Required: ISO 3166-1 alpha-2 country code (e.g., "US", "GB")
}

// Final exported type (discriminated union)
type ThredfiMountOptions = RegisteredOptions | UnregisteredOptions;

Validation Rules for RegistrationDetails

When using registrationDetails, the following validations are automatically performed:

| Field | Validation Rules | Example | | -------------- | ----------------------------------- | ---------------------------------------- | | partnerId | Must be a valid UUID format | "550e8400-e29b-41d4-a716-446655440000" | | businessName | Non-empty string | "Acme Corporation" | | personName | Non-empty string | "John Doe" | | emailId | Valid email format | "[email protected]" | | phone | Non-empty string (if provided) | "+1234567890" | | countryCode | ISO 3166-1 alpha-2 code (2 letters) | "US", "GB", "DE" |

Validation Errors:

If validation fails, mountThredfi() will throw a descriptive error:

// Invalid email
throw new Error(
  "[Thredfi] registrationDetails.emailId has invalid email format",
);

// Invalid UUID
throw new Error("[Thredfi] registrationDetails.partnerId must be a valid UUID");

// Invalid country code
throw new Error(
  "[Thredfi] registrationDetails.countryCode must be a valid ISO 3166-1 alpha-2 country code (e.g., 'US', 'GB')",
);

// Missing required field
throw new Error(
  "[Thredfi] registrationDetails.businessName is required and must be a non-empty string",
);

Theme Configuration

interface ThemeConfig {
  navigationBackgroundColor?: string; // Background color for header/navigation
  cardsBackgroundColor?: string; // Background color for cards and panels
  textColor?: string; // Main text color
  textSecondaryColor?: string; // Secondary/muted text color
  activeColor?: string; // Active/highlight/profit color
  borderColor?: string; // Border color for elements
  borderSecondaryColor?: string; // Light/secondary border color
}

Usage Examples

Registered Business:

mountThredfi({
  thredfiSelector: "#app",
  businessId: "uuid-here",
  getToken: async () => ({ access_token: "token", expires_in: 3600 }),
});

Unregistered Business:

mountThredfi({
  thredfiSelector: "#app",
  registrationDetails: {
    partnerId: "partner-id",
    businessName: "Acme Corp",
    personName: "John Doe",
    emailId: "[email protected]",
    countryCode: "US",
  },
});

unmountThredfi()

Unmounts the currently mounted widget and cleans up resources.

unmountThredfi();

When to use:

  • Cleaning up before remounting with different options
  • Component unmount in React/Vue/Angular
  • Page navigation cleanup

setThredfiLanguage(lang)

Changes the language of the mounted widget at runtime.

setThredfiLanguage("de"); // Switch to German
setThredfiLanguage("es"); // Switch to Spanish

Note: The widget must be mounted before calling this function.

🌍 Supported Languages

| Code | Language | | ---- | ---------------- | | en | English | | de | German | | es | Spanish | | fr | French | | it | Italian | | nl | Dutch | | no | Norwegian |


🌐 Environment Configuration

The environment option controls which backend API the widget connects to:

| Environment | Backend URL | Use Case | | ------------ | ----------------------------------- | --------------- | | sandbox | https://sandbox-backend.thredfi.com | Testing/Sandbox | | production | https://backend.thredfi.com | Production |

mountThredfi({
  // ... other options
  environment: "production", // Default is "sandbox"
});

🎛️ Hide Header Menu

You can hide the header navigation menu to show only the content:

mountThredfi({
  // ... other options
  hideMenu: true, // Hide the header menu
});

This is useful when:

  • Embedding specific views directly
  • Implementing your own custom navigation (see Custom Navigation Menu)
  • Integrating with an existing app layout

The SDK listens to route changes and renders the matching view automatically.


🎨 Theming

Customize the widget appearance with simple color overrides:

mountThredfi({
  // ... other options
  theme: {
    navigationBackgroundColor: "#f9f9f9", // Background color for header/navigation
    cardsBackgroundColor: "#ffffff", // Background color for cards and panels
    textColor: "#111827", // Main text color
    textSecondaryColor: "#6b7280", // Secondary/muted text color
    activeColor: "#871f91", // Active/highlight/profit color
    borderColor: "#dcdcdc", // Border color for elements
    borderSecondaryColor: "#e5e7eb", // Light/secondary border color
  },
});

Available theme properties:

| Property | Description | Default | | --------------------------- | -------------------------------------- | --------- | | navigationBackgroundColor | Background color for header/navigation | #f9f9f9 | | cardsBackgroundColor | Background color for cards and panels | #ffffff | | textColor | Main text color throughout the widget | #111827 | | textSecondaryColor | Secondary/muted text color | #6b7280 | | activeColor | Active/highlight/profit color | #871f91 | | borderColor | Border color for elements | #dcdcdc | | borderSecondaryColor | Light/secondary border color | #e5e7eb |


🔐 Authentication

The widget requires a getToken function to fetch authentication tokens. This gives you full control over how tokens are fetched, including adding custom headers and authentication. Use Thredfi's API key to create short-lived tokens that can be used in the frontend.

Example of a Token Fetcher Function:

import { mountThredfi, type TokenFetcher } from "@thredfi/accounting";

const getToken: TokenFetcher = async () => {
  const response = await fetch("https://your-api.com/thredfi-token", {
    headers: {
      Authorization: "Bearer your-auth-token", // Add your own auth
      "X-Custom-Header": "custom-value", // any custom headers
    },
  });

  const data = await response.json();

  return {
    access_token: data.access_token,
    expires_in: data.expires_in,
  };
};

mountThredfi({
  // ... other options
  getToken, // Your custom token fetcher
});

Token Response Format

Your getToken function must return an object with:

  • access_token (string) - The JWT access token
  • expires_in (number) - Token expiration time in seconds

Example response:

{
  access_token: "eyJhbGciOiJIUzI1NiIs...",
  expires_in: 3600
}

Automatic Token Management

The widget automatically:

  • Calls getToken before making API requests
  • Refreshes expired tokens (60 seconds before expiration)
  • Calls getToken when a 401 error is received
  • Retries failed requests with new tokens

🧭 Custom Navigation Menu

When embedding the Thredfi widget, you can hide the built-in header menu and implement your own custom navigation (e.g., a vertical sidebar). The SDK automatically renders the correct view based on the current URL path.

Available Routes

| Route | Description | | ---------------------------------- | --------------------------------- | | /insights | Dashboard with financial overview | | /tasks | Tasks and action items | | /bank-transactions | Bank transactions management | | /reports | Financial reports | | /general-ledger | General ledger (redirects to journal-entries) | | /general-ledger/journal-entries | Journal entries list | | /general-ledger/chart-of-accounts| Chart of accounts | | /accounts-receivable | AR section (redirects to invoices)| | /accounts-receivable/invoices | Customer invoices | | /accounts-receivable/credit-notes| Customer credit notes | | /accounts-payable | AP section (redirects to bills) | | /accounts-payable/bills | Vendor bills | | /accounts-payable/credits | Vendor credits |

How It Works

  1. Hide the SDK menu: Set hideMenu: true to remove the built-in header navigation
  2. Use your router: Navigate using your app's router (React Router, Next.js, etc.)
  3. SDK responds to URL: The SDK listens to route changes and renders the matching view
  4. Set basePath: If your app mounts at a sub-path (e.g., /accounting), set basePath accordingly

With Sub-Path Example

// If your app is mounted at /accounting
mountThredfi({
  thredfiSelector: "#thredfi-widget",
  businessId: "your-business-id",
  getToken: yourTokenFetcher,
  basePath: "/accounting", // SDK routes become /accounting/insights, etc.
  hideMenu: true,
});

// Your navigation would use full paths
const navItems = [
  { label: "Insights", path: "/accounting/insights" },
  { label: "Tasks", path: "/accounting/tasks" },
  // ...
];

📘 TypeScript Support

The package includes full TypeScript definitions. Import types for better development experience:

import {
  type ThredfiMountOptions,
  type RegistrationDetails,
  type TokenFetcher,
  type TokenResponse,
  type ThredfiLanguage,
} from "@thredfi/accounting";

🔗 Links