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

@zweiundeins/cashctrl-ts-sdk

v0.5.0

Published

Typed TypeScript client for the CashCtrl accounting API, covering all 376 endpoints. Generated from the published reference, with an OpenAPI 3.1 spec.

Readme

cashctrl-ts-sdk

JSR npm CI License: MIT

A typed TypeScript client for the CashCtrl accounting API, covering all 376 endpoints.

CashCtrl publishes no OpenAPI spec and no official SDK, so this repo generates both from the published HTML reference plus live probing. Zero dependencies, fetch-only, verified on Deno 2.7, Node 22 and Bun 1.3.

Unofficial and not affiliated with CashCtrl.

Install

deno add jsr:@zweiundeins/cashctrl-ts-sdk    # Deno
npm  install @zweiundeins/cashctrl-ts-sdk    # Node
bun  add     @zweiundeins/cashctrl-ts-sdk    # Bun
pnpm add     @zweiundeins/cashctrl-ts-sdk
import { CashCtrl } from "@zweiundeins/cashctrl-ts-sdk";

The npm build ships ESM and CommonJS with declarations and needs Node 18+; only the build tooling is Deno, never the published package.

You need an API key: Settings > Users & Roles > Add > Add API user. It is scoped to one organisation and inherits the role you give it.

Usage

Resources mirror the API's own path structure, so POST /api/v1/account/costcenter/category/create.json is cc.account.costcenter.category.create({...}). Every method takes an optional trailing AbortSignal.

import { CashCtrl } from "@zweiundeins/cashctrl-ts-sdk";

const cc = new CashCtrl({
  organisation: "myorg", // the subdomain of myorg.cashctrl.com
  apiKey: process.env.CASHCTRL_APIKEY!,
  lang: "de", // language for error messages and generated PDFs
});

const accounts = await cc.account.list({ onlyActive: true }); // unwrapped
const { insertId } = await cc.person.create({ company: "ACME AG" });

// Nested JSON params are typed all the way down.
await cc.order.create({
  associateId: insertId!,
  categoryId: 4,
  date: new Date(),
  items: [{ accountId: 42, name: "Consulting", unitPrice: 180, quantity: 8 }],
});

Updates are full replacements. CashCtrl treats an omitted parameter as an empty value, so a partial update silently clears every field left out. Use cc.person.updatePreserving(existing, { id, lastName: "Neu" }), which does the read-modify-write for you.

Errors

CashCtrl returns HTTP 200 for validation failures, with success: false in the body. The SDK promotes those to a thrown error so they cannot be missed:

try {
  await cc.journal.create({ amount: 0, debitId: 0, creditId: 0 });
} catch (err) {
  if (err instanceof CashCtrlValidationError) {
    console.log(err.byField()); // { debitId: ["This field cannot be empty."] }
  }
}

CashCtrlValidationError (HTTP 200, success: false), CashCtrlAuthError (401/403), CashCtrlRateLimitError (429, carries retryAfter) and CashCtrlHttpError (any other non-2xx). 429 and 5xx are retried with exponential backoff; configure with retry: { attempts, baseDelayMs }.

Parameter encoding

The API is form-encoded even though it returns JSON, so the SDK flattens structured values for you:

| You pass | Sent as | | ------------------------- | ------------------------ | | true | "true" | | new Date(2026, 6, 27) | "2026-07-27" | | [1, 2, 3] (a CSV param) | "1,2,3" | | [{...}] (a JSON param) | '[{"...":...}]' | | null | "", clearing the field | | undefined | omitted entirely |

The null versus undefined distinction is what makes updatePreserving work: undefined leaves a field alone, null clears it.

Localized text, documents, escape hatch

Translatable fields are an XML blob (<values><de>Kasse</de>...</values>), not JSON. localize, parseLocalized, toLocalized and isLocalized convert both ways, falling back to another language when one is missing and passing plain strings through untouched.

The 59 endpoints that return a file hand back the raw Response:

const pdf = await cc.order.document.readPdf({ ids: orderId });
await Deno.writeFile("invoice.pdf", new Uint8Array(await pdf.arrayBuffer()));

For anything the generated surface does not cover, cc.http.get and cc.http.post take a path and params directly.

Resources

account, currency, customfield, domain, file, fiscalperiod, history, inventory, journal, location, order, person, report, rounding, salary, sequencenumber, setting, tax, text — 62 classes once nested ones are counted.

What is verified

| Layer | Coverage | How | | -------------------- | ------------------ | ---------------------------------------- | | Request construction | 376/376 (100%) | tests/contract_test.ts, mock transport | | Write endpoints | 192/192 (100%) | scripts/write-test.ts, live writes | | Response shapes | 108/376 (29%) | live probing, one organisation |

Request parameters come from the docs and are authoritative. Response types are best-effort: inferred from one organisation's live data, so a field it never populated may be typed more loosely than reality, and an array empty in every sample infers as unknown[]. POST responses are never probed, so writes return the generic WriteEnvelope. Re-probing against your own organisation (deno task probe && deno task generate) tightens them for your data.

Documentation

  • docs/testing.md — what each layer covers, including the suite that drives all 192 write endpoints against a disposable organisation.
  • docs/api-notes.md — undocumented CashCtrl behaviour that suite turned up. Useful to any caller, not only users of this SDK.
  • docs/generation.md — the generation pipeline, the spec/ artefacts (OpenAPI 3.1 and a search index), side-effecting GETs, and the weekly upstream check.
  • CONTRIBUTING.md — working on the generator.

License

MIT