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-sdk

v1.1.3

Published

Kenya Revenue Authority (KRA) Electronic Tax Invoice Management System (eTims) API Integration SDK

Readme

KRA eTims SDK

A comprehensive Node.js SDK for integrating with the Kenya Revenue Authority (KRA) Electronic Tax Invoice Management System (eTims).

npm version License: ISC

Features

  • Complete KRA eTims API integration
  • Express.js server mode for easy API endpoint exposure
  • CORS support with domain whitelisting
  • Comprehensive validation for all API requests
  • Detailed logging and error handling
  • Environment-based configuration
  • TypeScript support

Author

Shadrack Matata
Email: [email protected]
Tel: +254722854082 / +254733854082

Installation

npm install kra-etims-sdk

Quick Start

Basic Usage

const KRAeTimsSDK = require('kra-etims-sdk');

// Initialize the SDK
const sdk = new KRAeTimsSDK();

// Authenticate with KRA eTims API
async function authenticate() {
  try {
    const token = await sdk.authenticate('your_username', 'your_password');
    console.log('Authentication successful:', token);
    return token;
  } catch (error) {
    console.error('Authentication failed:', error);
  }
}

// Send a sales transaction
async function sendSalesTransaction() {
  try {
    // Authenticate first
    await authenticate();
    
    // Prepare sales transaction data
    const salesData = {
      tin: 'P000000045R',
      bhfId: '00',
      invcNo: 'INV001',
      salesTrnsItems: [
        {
          itemCd: 'ITEM001',
          itemNm: 'Test Item',
          qty: 1,
          prc: 100,
          splyAmt: 100,
          dcRt: 0,
          dcAmt: 0,
          taxTyCd: 'V',
          taxAmt: 16
        }
      ]
    };
    
    // Send sales transaction
    const result = await sdk.sendSalesTransaction(salesData);
    console.log('Sales transaction sent:', result);
  } catch (error) {
    console.error('Failed to send sales transaction:', error);
  }
}

Express Server Integration

Complete Express Integration Example

Below is a comprehensive example of integrating the KRA eTims SDK with an Express application as middleware:

const express = require('express');
const bodyParser = require('body-parser');
const morgan = require('morgan'); // Optional for logging
const KRAeTimsSDK = require('kra-etims-sdk');

// Initialize Express app
const app = express();
const port = process.env.PORT || 3000;

// Middleware
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
app.use(morgan('dev')); // Optional for logging

// Initialize the KRA eTims SDK
const etimsSDK = new KRAeTimsSDK({
  // Optional configuration
  logLevel: 'info', // 'debug', 'info', 'warn', 'error'
  timeout: 30000, // API request timeout in ms
});

// Mount the SDK as middleware
app.use('/etims-api', etimsSDK.middleware());

// Custom routes that use the SDK directly

// Authentication example
app.post('/api/login', async (req, res) => {
  try {
    const { username, password } = req.body;
    const token = await etimsSDK.authenticate(username, password);
    res.json({ success: true, token });
  } catch (error) {
    console.error('Authentication error:', error);
    res.status(401).json({ success: false, error: error.message });
  }
});

// Sales transaction example
app.post('/api/sales', async (req, res) => {
  try {
    // Authenticate first (or use middleware to handle this)
    await etimsSDK.authenticate(process.env.API_USERNAME, process.env.API_PASSWORD);
    
    // Send sales transaction
    const result = await etimsSDK.sendSalesTransaction(req.body);
    res.json({ success: true, data: result });
  } catch (error) {
    console.error('Sales transaction error:', error);
    res.status(400).json({ success: false, error: error.message });
  }
});

// Get stock information example
app.post('/api/stock/info', async (req, res) => {
  try {
    await etimsSDK.authenticate(process.env.API_USERNAME, process.env.API_PASSWORD);
    const result = await etimsSDK.getStockMoveList(req.body);
    res.json({ success: true, data: result });
  } catch (error) {
    console.error('Stock info error:', error);
    res.status(400).json({ success: false, error: error.message });
  }
});

// Error handling middleware
app.use((err, req, res, next) => {
  console.error('Application error:', err);
  res.status(500).json({ success: false, error: 'Internal server error' });
});

// Start the server
app.listen(port, () => {
  console.log(`Server running on port ${port}`);
});

Authentication Middleware Example

Create a reusable authentication middleware for your Express routes:

// auth.middleware.js
const KRAeTimsSDK = require('kra-etims-sdk');
const sdk = new KRAeTimsSDK();

const authMiddleware = async (req, res, next) => {
  try {
    // You could also check for an existing token and validate it
    await sdk.authenticate(process.env.API_USERNAME, process.env.API_PASSWORD);
    req.etimsSDK = sdk; // Attach the authenticated SDK to the request
    next();
  } catch (error) {
    console.error('KRA eTims authentication error:', error);
    res.status(401).json({ success: false, error: 'Authentication failed' });
  }
};

module.exports = authMiddleware;

Then use it in your routes:

const express = require('express');
const authMiddleware = require('./auth.middleware');
const router = express.Router();

// Apply authentication middleware to all routes
router.use(authMiddleware);

// Now all routes have access to the authenticated SDK via req.etimsSDK
router.post('/sales/send', async (req, res) => {
  try {
    const result = await req.etimsSDK.sendSalesTransaction(req.body);
    res.json({ success: true, data: result });
  } catch (error) {
    res.status(400).json({ success: false, error: error.message });
  }
});

module.exports = router;

Setting Up as a Standalone Server

For comparison, here's how to set up the SDK as a standalone server:

const KRAeTimsSDK = require('kra-etims-sdk');

// Initialize the SDK with server option
const sdk = new KRAeTimsSDK({ server: true });

// Start the server
const port = process.env.PORT || 5000;
sdk.start(port).then(() => {
  console.log(`KRA eTims server started on port ${port}`);
}).catch(err => {
  console.error('Failed to start server:', err);
});

Environment Configuration

Create a .env file in your project root:

# API Base URLs
DEV_API_BASE_URL=https://etims-api-sbx.kra.go.ke
PROD_API_BASE_URL=https://etims-api.kra.go.ke/etims-api

# Authentication
API_USERNAME=your_username
API_PASSWORD=your_password

# Environment
NODE_ENV=development
PORT=5000

# CORS Configuration
CORS_ALLOWED_ORIGINS=http://localhost:3000,https://yourdomain.com

Switching Between Production and Development Endpoints

The SDK automatically selects the appropriate API base URL based on the NODE_ENV environment variable:

  • When NODE_ENV=development (or not set), the SDK uses DEV_API_BASE_URL (sandbox environment)
  • When NODE_ENV=production, the SDK uses PROD_API_BASE_URL (production environment)

Method 1: Using Environment Variables

# For development (sandbox)
NODE_ENV=development npm start

# For production
NODE_ENV=production npm start

Method 2: Setting NODE_ENV in Your Code

// Before initializing the SDK
process.env.NODE_ENV = 'production'; // or 'development'

const KRAeTimsSDK = require('kra-etims-sdk');
const sdk = new KRAeTimsSDK();

Method 3: Explicitly Setting the API Base URL

You can also override the automatic selection by explicitly setting the API base URL when initializing the SDK:

const KRAeTimsSDK = require('kra-etims-sdk');

// For development (sandbox)
const devSdk = new KRAeTimsSDK({
  apiBaseUrl: 'https://etims-api-sbx.kra.go.ke'
});

// For production
const prodSdk = new KRAeTimsSDK({
  apiBaseUrl: 'https://etims-api.kra.go.ke/etims-api'
});

CORS and Security

The SDK includes built-in CORS support with domain whitelisting to secure your API endpoints.

Configuring CORS

You can configure allowed origins in your .env file:

CORS_ALLOWED_ORIGINS=https://yourdomain.com,https://app.yourdomain.com

By default, the SDK allows requests from http://localhost:3000 and http://localhost:5000 in development.

API Endpoints and Integration Examples

This section provides comprehensive examples of how to use each endpoint in your Express application.

Authentication

POST /api/auth/token - Get authentication token

Request Body:

{
  "username": "your_username",
  "password": "your_password"
}

Express Integration Example:

const express = require('express');
const KRAeTimsSDK = require('kra-etims-sdk');
const router = express.Router();

// Initialize the SDK
const etimsSDK = new KRAeTimsSDK();

router.post('/auth', async (req, res) => {
  try {
    const { username, password } = req.body;
    
    // Call the SDK's authentication method
    const token = await etimsSDK.authenticate(username, password);
    
    // Store token in session or return to client
    req.session.etimsToken = token;
    
    res.json({
      success: true,
      message: 'Authentication successful',
      expiresAt: etimsSDK.getTokenExpiry()
    });
  } catch (error) {
    console.error('Authentication error:', error);
    res.status(401).json({
      success: false,
      message: 'Authentication failed',
      error: error.message
    });
  }
});

module.exports = router;

Initialization

POST /api/initialization/osdc-info - Initialize OSDC Info

Request Body:

{
  "tin": "P000000045R",
  "bhfId": "00",
  "dvcSrlNo": "MOVA22"
}

Express Integration Example:

router.post('/initialize', async (req, res) => {
  try {
    // Ensure authentication
    await etimsSDK.authenticate(process.env.API_USERNAME, process.env.API_PASSWORD);
    
    const initData = req.body;
    const result = await etimsSDK.initializeOsdcInfo(initData);
    
    res.json({
      success: true,
      message: 'OSDC initialization successful',
      data: result
    });
  } catch (error) {
    console.error('Initialization error:', error);
    res.status(400).json({
      success: false,
      message: 'Initialization failed',
      error: error.message
    });
  }
});

Basic Data Management

POST /api/basic-data/code-list - Get code list

Request Body:

{
  "tin": "P000000045R",
  "bhfId": "00",
  "lastReqDt": "20220101010101"
}

Express Integration Example:

router.post('/code-list', async (req, res) => {
  try {
    await etimsSDK.authenticate(process.env.API_USERNAME, process.env.API_PASSWORD);
    
    const result = await etimsSDK.getCodeList(req.body);
    
    res.json({
      success: true,
      data: result
    });
  } catch (error) {
    console.error('Code list error:', error);
    res.status(400).json({
      success: false,
      error: error.message
    });
  }
});

POST /api/basic-data/item-cls-list - Get item classification list

Request Body:

{
  "tin": "P000000045R",
  "bhfId": "00",
  "lastReqDt": "20220101010101"
}

Express Integration Example:

router.post('/item-classifications', async (req, res) => {
  try {
    await etimsSDK.authenticate(process.env.API_USERNAME, process.env.API_PASSWORD);
    
    const result = await etimsSDK.getItemClassificationList(req.body);
    
    // Cache the results for future use
    req.app.locals.itemClassifications = result;
    
    res.json({
      success: true,
      count: result.length,
      data: result
    });
  } catch (error) {
    console.error('Item classification error:', error);
    res.status(400).json({
      success: false,
      error: error.message
    });
  }
});

POST /api/basic-data/bhf-list - Get branch list

Request Body:

{
  "lastReqDt": "20220101010101"
}

Express Integration Example:

router.post('/branches', async (req, res) => {
  try {
    await etimsSDK.authenticate(process.env.API_USERNAME, process.env.API_PASSWORD);
    
    const result = await etimsSDK.getBranchList(req.body);
    
    res.json({
      success: true,
      count: result.length,
      data: result
    });
  } catch (error) {
    console.error('Branch list error:', error);
    res.status(400).json({
      success: false,
      error: error.message
    });
  }
});

POST /api/basic-data/notice-list - Get notice list

Request Body:

{
  "tin": "P000000045R",
  "bhfId": "00",
  "lastReqDt": "20220101010101"
}

Express Integration Example:

router.post('/notices', async (req, res) => {
  try {
    await etimsSDK.authenticate(process.env.API_USERNAME, process.env.API_PASSWORD);
    
    const result = await etimsSDK.getNoticeList(req.body);
    
    res.json({
      success: true,
      data: result
    });
  } catch (error) {
    console.error('Notice list error:', error);
    res.status(400).json({
      success: false,
      error: error.message
    });
  }
});

POST /api/basic-data/taxpayer-info - Get taxpayer info

Request Body:

{
  "tin": "P000000045R",
  "bhfId": "00",
  "lastReqDt": "20220101010101"
}

Express Integration Example:

router.post('/taxpayer', async (req, res) => {
  try {
    await etimsSDK.authenticate(process.env.API_USERNAME, process.env.API_PASSWORD);
    
    const result = await etimsSDK.getTaxpayerInfo(req.body);
    
    res.json({
      success: true,
      data: result
    });
  } catch (error) {
    console.error('Taxpayer info error:', error);
    res.status(400).json({
      success: false,
      error: error.message
    });
  }
});

POST /api/basic-data/customer-list - Get customer list

Request Body:

{
  "tin": "P000000045R",
  "bhfId": "00",
  "lastReqDt": "20220101010101"
}

Express Integration Example:

router.post('/customers', async (req, res) => {
  try {
    await etimsSDK.authenticate(process.env.API_USERNAME, process.env.API_PASSWORD);
    
    const result = await etimsSDK.getCustomerList(req.body);
    
    res.json({
      success: true,
      count: result.length,
      data: result
    });
  } catch (error) {
    console.error('Customer list error:', error);
    res.status(400).json({
      success: false,
      error: error.message
    });
  }
});

Sales Management

POST /api/sales/send - Send sales transaction

Request Body:

{
  "tin": "P000000045R",
  "bhfId": "00",
  "invcNo": "INV001",
  "salesTrnsItems": [
    {
      "itemCd": "ITEM001",
      "itemNm": "Test Item",
      "qty": 1,
      "prc": 100,
      "splyAmt": 100,
      "dcRt": 0,
      "dcAmt": 0,
      "taxTyCd": "V",
      "taxAmt": 16
    }
  ]
}

Express Integration Example:

router.post('/sales/create', async (req, res) => {
  try {
    await etimsSDK.authenticate(process.env.API_USERNAME, process.env.API_PASSWORD);
    
    // You might want to validate the sales data before sending
    const salesData = req.body;
    
    // Calculate totals if not provided
    if (!salesData.totals) {
      const items = salesData.salesTrnsItems;
      const totalAmount = items.reduce((sum, item) => sum + item.splyAmt, 0);
      const totalTax = items.reduce((sum, item) => sum + item.taxAmt, 0);
      
      salesData.totals = {
        totalAmount,
        totalTax,
        grandTotal: totalAmount + totalTax
      };
    }
    
    const result = await etimsSDK.sendSalesTransaction(salesData);
    
    // You might want to store the result in your database
    
    res.json({
      success: true,
      message: 'Sales transaction recorded successfully',
      data: result,
      receiptNo: result.receiptNo,
      timestamp: result.timestamp
    });
  } catch (error) {
    console.error('Sales transaction error:', error);
    res.status(400).json({
      success: false,
      message: 'Failed to record sales transaction',
      error: error.message
    });
  }
});

POST /api/sales/select - Get sales transaction

Request Body:

{
  "tin": "P000000045R",
  "bhfId": "00",
  "lastReqDt": "20220101010101",
  "invcNo": "INV001"  // Optional
}

Express Integration Example:

router.post('/sales/history', async (req, res) => {
  try {
    await etimsSDK.authenticate(process.env.API_USERNAME, process.env.API_PASSWORD);
    
    const result = await etimsSDK.getSalesTransactions(req.body);
    
    res.json({
      success: true,
      count: result.length,
      data: result
    });
  } catch (error) {
    console.error('Sales history error:', error);
    res.status(400).json({
      success: false,
      error: error.message
    });
  }
});

// Get a specific invoice
router.get('/sales/invoice/:invoiceNo', async (req, res) => {
  try {
    await etimsSDK.authenticate(process.env.API_USERNAME, process.env.API_PASSWORD);
    
    const params = {
      tin: process.env.KRA_TIN,
      bhfId: process.env.KRA_BHF_ID,
      lastReqDt: '20220101010101',
      invcNo: req.params.invoiceNo
    };
    
    const result = await etimsSDK.getSalesTransactions(params);
    
    if (result.length === 0) {
      return res.status(404).json({
        success: false,
        message: 'Invoice not found'
      });
    }
    
    res.json({
      success: true,
      data: result[0]
    });
  } catch (error) {
    console.error('Invoice retrieval error:', error);
    res.status(400).json({
      success: false,
      error: error.message
    });
  }
});

Stock Management

POST /api/stock/move-list - Get move list

Request Body:

{
  "tin": "P000000045R",
  "bhfId": "00",
  "lastReqDt": "20220101010101"
}

Express Integration Example:

router.post('/stock/movements', async (req, res) => {
  try {
    await etimsSDK.authenticate(process.env.API_USERNAME, process.env.API_PASSWORD);
    
    const result = await etimsSDK.getStockMoveList(req.body);
    
    res.json({
      success: true,
      count: result.length,
      data: result
    });
  } catch (error) {
    console.error('Stock movement error:', error);
    res.status(400).json({
      success: false,
      error: error.message
    });
  }
});

POST /api/stock/save-master - Save stock master

Request Body:

{
  "tin": "P000000045R",
  "bhfId": "00",
  "itemCd": "ITEM001",
  "itemClsCd": "FOOD",
  "itemNm": "Test Item",
  "pkgUnitCd": "EA",
  "qtyUnitCd": "EA",
  "splyAmt": 100,
  "vatTyCd": "V"
}

Express Integration Example:

router.post('/stock/create', async (req, res) => {
  try {
    await etimsSDK.authenticate(process.env.API_USERNAME, process.env.API_PASSWORD);
    
    const stockData = req.body;
    const result = await etimsSDK.saveStockMaster(stockData);
    
    res.json({
      success: true,
      message: 'Stock item created successfully',
      data: result
    });
  } catch (error) {
    console.error('Stock creation error:', error);
    res.status(400).json({
      success: false,
      message: 'Failed to create stock item',
      error: error.message
    });
  }
});

Purchase Management

POST /api/purchase/select - Get purchase transaction

Request Body:

{
  "tin": "P000000045R",
  "bhfId": "00",
  "lastReqDt": "20220101010101"
}

Express Integration Example:

router.post('/purchases', async (req, res) => {
  try {
    await etimsSDK.authenticate(process.env.API_USERNAME, process.env.API_PASSWORD);
    
    const result = await etimsSDK.getPurchaseTransactions(req.body);
    
    res.json({
      success: true,
      count: result.length,
      data: result
    });
  } catch (error) {
    console.error('Purchase history error:', error);
    res.status(400).json({
      success: false,
      error: error.message
    });
  }
});

Complete Router Example

Here's how to organize all these endpoints into a complete Express router:

// routes/etims.routes.js
const express = require('express');
const KRAeTimsSDK = require('kra-etims-sdk');
const router = express.Router();

// Initialize the SDK
const etimsSDK = new KRAeTimsSDK();

// Authentication middleware
const authMiddleware = async (req, res, next) => {
  try {
    await etimsSDK.authenticate(process.env.API_USERNAME, process.env.API_PASSWORD);
    req.etimsSDK = etimsSDK;
    next();
  } catch (error) {
    res.status(401).json({
      success: false,
      message: 'KRA eTims authentication failed',
      error: error.message
    });
  }
};

// Apply authentication middleware to all routes except login
router.use((req, res, next) => {
  if (req.path === '/auth') {
    return next();
  }
  authMiddleware(req, res, next);
});

// Authentication route
router.post('/auth', async (req, res) => {
  try {
    const { username, password } = req.body;
    const token = await etimsSDK.authenticate(username, password);
    res.json({ success: true, token });
  } catch (error) {
    res.status(401).json({ success: false, error: error.message });
  }
});

// Basic data routes
router.post('/code-list', async (req, res) => {
  try {
    const result = await req.etimsSDK.getCodeList(req.body);
    res.json({ success: true, data: result });
  } catch (error) {
    res.status(400).json({ success: false, error: error.message });
  }
});

// Add other routes here...

module.exports = router;

// In your main app.js:
// const etimsRoutes = require('./routes/etims.routes');
// app.use('/api/etims', etimsRoutes);

Advanced Usage

Custom Error Handling

const { AuthenticationError, ValidationError } = require('kra-etims-sdk/errors');

try {
  // SDK operations
} catch (error) {
  if (error instanceof AuthenticationError) {
    // Handle authentication errors
    console.error('Authentication failed:', error.message);
  } else if (error instanceof ValidationError) {
    // Handle validation errors
    console.error('Validation failed:', error.details);
  } else {
    // Handle other errors
    console.error('Operation failed:', error);
  }
}

Using with TypeScript

import KRAeTimsSDK from 'kra-etims-sdk';
import { SalesTransaction, ApiResponse } from 'kra-etims-sdk/types';

const sdk = new KRAeTimsSDK();

async function processSale(sale: SalesTransaction): Promise<ApiResponse> {
  try {
    await sdk.authenticate('username', 'password');
    return await sdk.sendSalesTransaction(sale);
  } catch (error) {
    console.error('Failed to process sale:', error);
    throw error;
  }
}

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

License

This project is licensed under the ISC License - see the LICENSE file for details.