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

kra-etims-oscu-sdk

v0.1.6

Published

Unofficial TypeScript SDK for the Kenya Revenue Authority (KRA) eTIMS OSCU system-to-system integration API. Not affiliated with or endorsed by KRA.

Readme

kra-etims-oscu-sdk

A community TypeScript SDK for the Kenya Revenue Authority (KRA) eTIMS OSCU system-to-system JSON API, with a compatible VSCU client for taxpayer-hosted deployments. Not affiliated with, endorsed by, or officially supported by KRA.

Read this first: docs/kra-contract-verification.md. It lists, for every endpoint this SDK implements, exactly which KRA document and section it came from, and it lists what is not implemented and why. This README summarizes; that document is the source of truth.

What this SDK is

  • A typed client for KRA's real, published OSCU JSON REST API (OSCU Specification Document v2.0, v2.0, April 2023): device initialization, code/customer/notice lookup, item classification & save, branch management, import item reconciliation, sales, purchases, and stock.
  • A client that can also talk to a VSCU instance you already run (same JSON contract, taxpayer-hosted), once KRA has approved your VSCU service request and you have the runtime running — see docs/vscu.md.
  • Strict about fiscal safety: write operations are never silently retried, and the two highest-risk writes (items.save, sales.save) are validated locally with Zod before anything is sent over the network.

What this SDK is NOT

  • Not an implementation of the VSCU Java runtime itself. KRA distributes that as a separate downloadable package after service approval; this SDK talks to it, it does not replace it. See docs/vscu.md.
  • Not an implementation of the local TIS↔SCU device IPC/XML protocol described in KRA's "Technical Specification of TIS for OSCU/VSCU" document — that's a different protocol for device firmware, not for typical backend integration. See docs/kra-contract-verification.md.
  • Not certified, vetted, or endorsed by KRA. Passing your own integration through KRA's testing/certification process remains your responsibility.
  • Not exercised against a live KRA sandbox as part of building this repo (no sandbox credentials were available). Every type and endpoint is sourced from KRA's own specification PDFs and unit-tested against fixtures derived from the spec's own JSON samples — see the status legend in docs/kra-contract-verification.md for exactly what "verified" means here versus what still needs sandbox testing by you.

Supported integration modes and environments

These are two independent settings — see docs/architecture.md:

| | OSCU | VSCU | |---|---|---| | Hosted by | KRA | You (taxpayer), KRA-provided Java runtime | | environment: "sandbox" \| "production" | Yes, resolves KRA's base URL | No — you supply baseUrl directly | | Implemented by this SDK | Full JSON API client | Full JSON API client, pointed at your runtime |

Installation

npm i kra-etims-oscu-sdk

Quick start

import { EtimsClient } from "kra-etims-oscu-sdk";

const client = new EtimsClient({
  mode: "OSCU",
  environment: "sandbox",
  pin: "A123456789Z",
  branchId: "00",
});

// One-time: activate the device and obtain your communication key.
const { info } = await client.oscu!.init.initialize({
  tin: "A123456789Z",
  bhfId: "00",
  dvcSrlNo: "dev-serial-0001",
});

console.log("Store this securely, do not log it again:", info.cmcKey);

See docs/sandbox.md for the full onboarding walkthrough and examples/ for runnable code.

Configuration

interface OscuClientConfig {
  mode: "OSCU";
  environment: "sandbox" | "production";
  pin: string;         // KRA "tin", 11 characters
  branchId: string;     // "00" = head office
  communicationKey?: string; // cmcKey, once you have one
  baseUrl?: string;     // override, e.g. for a test proxy
  timeoutMs?: number;   // default 15000
  maxRetries?: number;  // default 2, read-only calls only — see "Fiscal safety"
  fetchImpl?: typeof fetch;
}

interface VscuClientConfig {
  mode: "VSCU";
  pin: string;
  branchId: string;
  communicationKey?: string;
  baseUrl: string;      // required — your VSCU runtime's host, e.g. http://localhost:8088
  timeoutMs?: number;
  maxRetries?: number;
  fetchImpl?: typeof fetch;
}

Sales

import { EtimsClient, roundEtimsAmount, toEtimsDate, toEtimsDateTime } from "kra-etims-oscu-sdk";

const receipt = await client.oscu!.sales.save({
  tin: "A123456789Z",
  bhfId: "00",
  cmcKey: process.env.ETIMS_CMC_KEY!,
  trdInvcNo: "INV-0001",
  invcNo: 1,
  orgInvcNo: 0,
  custTin: null,
  custNm: "Walk-in customer",
  rcptTyCd: "S",
  pmtTyCd: "01",
  salesSttsCd: "02",
  cfmDt: toEtimsDateTime(new Date()),
  salesDt: toEtimsDate(new Date()),
  stockRlsDt: toEtimsDateTime(new Date()),
  cnclReqDt: null, cnclDt: null, rfdDt: null, rfdRsnCd: null,
  totItemCnt: 1,
  taxblAmtA: 0, taxblAmtB: 100, taxblAmtC: 0, taxblAmtD: 0, taxblAmtE: 0,
  taxRtA: 0, taxRtB: 16, taxRtC: 0, taxRtD: 0, taxRtE: 0,
  taxAmtA: 0, taxAmtB: roundEtimsAmount(16), taxAmtC: 0, taxAmtD: 0, taxAmtE: 0,
  totTaxblAmt: 100, totTaxAmt: 16, totAmt: 116,
  prchrAcptcYn: "N",
  remark: null,
  regrId: "admin", regrNm: "Admin", modrId: "admin", modrNm: "Admin",
  receipt: {
    custTin: null, custMblNo: null,
    rcptPbctDt: toEtimsDateTime(new Date()),
    trdeNm: null, adrs: null, topMsg: null, btmMsg: null,
    prchrAcptcYn: "N",
  },
  itemList: [{
    itemSeq: 1, itemClsCd: "5059690800", itemCd: "KE1NTXU0000001", itemNm: "Sample item",
    bcd: null, pkgUnitCd: "NT", pkg: 1, qtyUnitCd: "U", qty: 1,
    prc: 116, splyAmt: 100, dcRt: 0, dcAmt: 0,
    isrccCd: null, isrccNm: null, isrcRt: null, isrcAmt: null,
    taxTyCd: "B", taxblAmt: 100, totTaxAmt: 16, totAmt: 116,
  }],
});

// Print receipt.intrlData and receipt.rcptSign on the customer receipt —
// see TIS-for-OSCU--VSCU-Technical-Specifications-v2.0.pdf §6.23.

Customers, Items, Purchases, Stock

await client.oscu!.customers.search({ tin, bhfId, cmcKey, custmTin: "P987654321X" });
await client.oscu!.items.search({ tin, bhfId, cmcKey, lastReqDt: "20200101000000" });
await client.oscu!.purchases.searchPurchaseSales({ tin, bhfId, cmcKey, lastReqDt: "20200101000000" });
await client.oscu!.stock.searchMoves({ tin, bhfId, cmcKey, lastReqDt: "20200101000000" });

Full field-level types for every request/response are exported from the package root — see src/index.ts or your editor's autocomplete.

Errors

import { EtimsNetworkError, EtimsApiError, EtimsAuthenticationError, EtimsValidationError } from "kra-etims-oscu-sdk";

try {
  await client.oscu!.sales.save(request);
} catch (err) {
  if (err instanceof EtimsValidationError) {
    // Never left your machine — fix err.issues and resubmit safely.
  } else if (err instanceof EtimsAuthenticationError) {
    // Device/cmcKey problem — see docs/troubleshooting.md.
  } else if (err instanceof EtimsApiError) {
    // KRA responded with a business-rule failure — err.resultCd/resultMsg.
  } else if (err instanceof EtimsNetworkError) {
    // Request may or may not have reached KRA — see "Fiscal safety" below
    // before retrying a write call.
  }
}

Fiscal safety

Fiscal writes (sales.save, purchases.save, stock.saveIo) are never automatically retried by this SDK, even on a network timeout — see docs/architecture.md#retry-policy-and-fiscal-safety. A dropped connection does not tell whether the KRA backend processed the request. Before resubmitting a write after a network error, use the corresponding search endpoint (purchases.searchPurchaseSales, stock.searchMoves) to check whether the original submission already landed, and give it a new invcNo/sarNo only if it didn't.

Testing

npm test                    # unit tests, no network calls, safe in CI
npm run test:integration    # opt-in, requires real sandbox credentials in .env

Sandbox

See docs/sandbox.md for the full KRA onboarding walkthrough (service request, device activation, communication key).

Security

See SECURITY.md.

Architecture

See docs/architecture.md.

Contributing

See CONTRIBUTING.md. The short version: every KRA-facing detail must cite an official KRA document in docs/kra-contract-verification.md.

Versioning

This package follows semver relative to its own public TypeScript API. docs/kra-contract-verification.md separately tracks which version of KRA's own specification (OSCU_Specification_Document_v2.0.pdf, v2.0) this SDK was built against — a future KRA contract revision may require a new SDK major version if field shapes change.

License

MIT — see LICENSE. This project is independent and not affiliated with KRA; see the notice at the bottom of LICENSE.