Bonaventure OgetoBy Bonaventure Ogeto|

KRA eTIMS and Payment Receipt Integration Considerations

KRA's eTIMS (electronic Tax Invoice Management System) requires businesses in Kenya to generate electronic tax invoices for sales. When a customer pays through Paystack, you need to generate an eTIMS-compliant tax invoice for that transaction. Paystack does not generate eTIMS invoices for you. Your system must call the eTIMS API (directly or through a third-party integration) after each successful payment to generate and transmit the invoice to KRA.

Important Disclaimer

This article is not tax or legal advice. eTIMS requirements are set by KRA and have been updated multiple times since the system was introduced. The information here is based on publicly available guidelines and may not reflect the most current rules.

Before implementing eTIMS integration:

  • Check the KRA website for the latest eTIMS guidelines and deadlines. [TODO: verify current eTIMS requirements].
  • Consult a qualified tax professional or accountant familiar with eTIMS.
  • If you use a third-party eTIMS integration tool, verify that it is KRA-approved.

What eTIMS Is and Why It Exists

eTIMS stands for electronic Tax Invoice Management System. It is KRA's system for tracking every business-to-customer and business-to-business sale in Kenya through electronic tax invoices.

The idea is straightforward: instead of businesses issuing paper receipts that may or may not be reported to KRA, every sale generates an electronic invoice that is transmitted to KRA in real time or near-real time. This gives KRA visibility into actual sales volumes, which helps them collect the correct amount of VAT and income tax.

For businesses, eTIMS replaces the old Electronic Tax Register (ETR) system. Instead of a physical device printing receipts, the invoice generation happens through software (a mobile app, a web portal, or an API).

For developers building products that process payments, eTIMS means one more system to integrate with. Every time a customer pays through your Paystack-powered product, you may need to generate an eTIMS invoice for that transaction and transmit it to KRA.

What eTIMS Requires for Each Transaction

When a sale occurs, eTIMS requires a tax invoice that includes:

  • Seller information. Your business name, KRA PIN, and eTIMS registration details.
  • Buyer information. For B2B transactions, the buyer's KRA PIN. For B2C transactions, the buyer's name or a generic identifier.
  • Invoice details. Invoice number, date, and time.
  • Item details. Description of the goods or services, quantity, unit price, and total price.
  • Tax breakdown. The VAT amount (if applicable), the VAT rate, and the total inclusive of tax.
  • eTIMS control number. A unique identifier assigned by the eTIMS system when the invoice is transmitted. This confirms KRA received and recorded the invoice.

The invoice must be transmitted to KRA through one of the approved channels: the eTIMS mobile app, the eTIMS web portal (Taxpayer Portal on iTax), or the eTIMS API. For automated systems that process payments through Paystack, the API is the relevant option.

The key point for developers: the Paystack payment confirmation and the eTIMS invoice are two different things. The Paystack webhook tells you money moved. The eTIMS invoice tells KRA that a taxable sale occurred. Your system connects these two events.

How Payment Receipts Relate to Tax Invoices

This is where many developers get confused. A Paystack payment confirmation is not a tax invoice. They serve different purposes.

Paystack payment receipt: Confirms that the customer paid a specific amount through a specific payment method. Contains the transaction reference, amount, currency, payment channel, and timestamp. This is a record of money moving. Paystack may send the customer an email receipt. This receipt is not an eTIMS-compliant tax invoice.

eTIMS tax invoice: A legally compliant document that records the sale for tax purposes. Contains seller and buyer details, item descriptions, tax breakdown, and the eTIMS control number from KRA. This is a record of a taxable event.

Both are generated for the same underlying event (a customer bought something), but they go to different places and serve different legal purposes.

Your system needs to:

  1. Receive the Paystack charge.success webhook confirming payment.
  2. Generate the eTIMS tax invoice with the correct details for that sale.
  3. Transmit the invoice to KRA through the eTIMS API.
  4. Store the eTIMS control number alongside the Paystack reference in your database.
  5. Optionally, provide the customer with the eTIMS-compliant invoice (as a PDF, in-app, or via email).

Each Paystack transaction should map to one eTIMS invoice. The Paystack reference becomes the bridge between your payment records and your tax records.

Integration Architecture: Connecting Paystack and eTIMS

The cleanest integration puts the eTIMS invoice generation in your Paystack webhook handler. When payment succeeds, generate the invoice. Here is the architectural flow:

// Paystack webhook handler with eTIMS integration
app.post('/webhooks/paystack', express.json(), async (req, res) => {
  // Return 200 immediately
  res.status(200).send('OK');

  // Verify Paystack signature
  const hash = crypto
    .createHmac('sha512', process.env.PAYSTACK_SECRET_KEY)
    .update(JSON.stringify(req.body))
    .digest('hex');

  if (hash !== req.headers['x-paystack-signature']) {
    console.error('Invalid Paystack signature');
    return;
  }

  const event = req.body;

  if (event.event === 'charge.success') {
    const { reference, amount, currency, metadata } = event.data;

    // Step 1: Update order status
    const order = await getOrderByReference(reference);
    if (!order || order.status === 'paid') return;

    await updateOrderStatus(order.id, 'paid');

    // Step 2: Generate eTIMS invoice
    try {
      const etimsResult = await generateEtimsInvoice({
        orderId: order.id,
        items: order.items,
        totalAmount: amount / 100, // Convert from cents to KES
        customerName: order.customerName,
        customerPin: order.customerKraPin || null, // Optional for B2C
        paymentReference: reference,
        paymentDate: new Date(),
      });

      // Step 3: Store the eTIMS control number
      await storeEtimsRecord(order.id, {
        controlNumber: etimsResult.controlNumber,
        invoiceNumber: etimsResult.invoiceNumber,
        paystackReference: reference,
        generatedAt: new Date(),
      });

    } catch (etimsError) {
      // eTIMS failed but payment succeeded
      // Log the error and queue for retry
      console.error('eTIMS generation failed:', etimsError);
      await queueEtimsRetry(order.id, reference);
    }
  }
});

A critical design decision: what happens when the eTIMS API call fails but the payment succeeded? You have two options:

  • Option A: Process the order anyway and retry eTIMS later. The customer gets their product or service. You queue the failed eTIMS call for retry. This is the better customer experience but means you might have a window where a sale is not reflected in eTIMS.
  • Option B: Hold the order until eTIMS succeeds. The customer waits. This ensures compliance but creates a poor experience when the eTIMS API is slow or down.

Most businesses choose Option A with a reliable retry mechanism. The eTIMS invoice can be generated minutes or hours after the sale and still be compliant, as long as it reflects the correct date and time of the original transaction. Check the current rules on acceptable delay.

Working with the eTIMS API

KRA provides API access for eTIMS integration. The API allows your system to generate and transmit tax invoices programmatically without manual entry in the eTIMS portal.

The general API flow is:

  1. Authenticate. Obtain access credentials from KRA through the eTIMS registration process.
  2. Build the invoice payload. Structure the invoice data according to KRA's API specification, including seller details, buyer details, items, quantities, prices, and tax calculations.
  3. Submit the invoice. Send the payload to the eTIMS API endpoint.
  4. Receive the response. KRA returns a control number and status. Store both.
// Simplified eTIMS invoice generation
// The actual API endpoint, authentication, and payload format
// must be verified against KRA's current eTIMS API documentation
async function generateEtimsInvoice(saleData) {
  const invoicePayload = {
    // Seller details (your business)
    sellerPin: process.env.KRA_PIN,
    sellerName: process.env.BUSINESS_NAME,

    // Buyer details
    buyerPin: saleData.customerPin || '', // Empty for B2C
    buyerName: saleData.customerName || 'Walk-in Customer',

    // Invoice metadata
    invoiceDate: saleData.paymentDate.toISOString().split('T')[0],
    invoiceTime: saleData.paymentDate.toISOString().split('T')[1].split('.')[0],

    // Line items
    items: saleData.items.map(item => ({
      description: item.name,
      quantity: item.quantity,
      unitPrice: item.unitPrice,
      totalPrice: item.quantity * item.unitPrice,
      taxRate: item.vatRate || 16, // Standard VAT rate, verify current rate
      taxAmount: (item.quantity * item.unitPrice * (item.vatRate || 16)) / 100,
    })),

    // Totals
    totalExclTax: saleData.totalAmount / 1.16, // If prices include VAT
    totalTax: saleData.totalAmount - (saleData.totalAmount / 1.16),
    totalInclTax: saleData.totalAmount,

    // Payment reference for your records
    externalReference: saleData.paymentReference,
  };

  // Call the eTIMS API
  // [TODO: verify current eTIMS API endpoint and authentication method]
  const response = await fetch(process.env.ETIMS_API_URL, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      Authorization: `Bearer ${process.env.ETIMS_API_TOKEN}`,
    },
    body: JSON.stringify(invoicePayload),
  });

  const result = await response.json();

  if (!result.success) {
    throw new Error(`eTIMS error: ${result.message}`);
  }

  return {
    controlNumber: result.controlNumber,
    invoiceNumber: result.invoiceNumber,
  };
}

Important: The code above is illustrative. The actual eTIMS API endpoint, authentication mechanism, payload structure, and field names must be obtained from KRA's current eTIMS API documentation. KRA may use different formats, require specific certificates, or mandate a different authentication flow. Do not ship this code without verifying against the actual API documentation.

Third-Party eTIMS Integration Tools

You do not have to build a direct eTIMS integration from scratch. Several Kenyan companies offer eTIMS middleware that simplifies the integration:

  • eTIMS-compatible POS systems. If your business uses a point-of-sale system, many POS providers in Kenya have built eTIMS integration into their software. The POS generates the eTIMS invoice automatically when a sale is recorded.
  • Accounting software with eTIMS support. Some accounting platforms used in Kenya (including Kenyan-built ones) have eTIMS integration built in. If you already use one, check whether it supports automated invoice submission.
  • eTIMS middleware APIs. Third-party services provide a simpler API that wraps the KRA eTIMS API. You send them the sale data, and they handle the formatting, authentication, and submission to KRA. This can save weeks of integration work.
  • KRA eTIMS mobile app. For businesses with low transaction volumes, KRA provides a mobile app for manual invoice entry. This is not suitable for automated payment systems, but it works as a fallback during development.

If you choose a third-party tool, verify:

  1. Is it approved or recognized by KRA?
  2. Does it have an API you can call from your webhook handler?
  3. What is its uptime and reliability?
  4. What happens to your data? eTIMS invoices contain customer information.
  5. What is the pricing model? Per-invoice fees can add up at high volumes.

For most developers, a third-party eTIMS middleware is the fastest path to compliance. Building a direct integration with the KRA API is feasible but requires more effort to get the authentication, certificate handling, and payload formatting right.

Handling Refunds and Credit Notes in eTIMS

When a customer gets a refund through Paystack, you need to handle the eTIMS side as well. A refund does not cancel the original invoice. Instead, you generate a credit note.

The flow:

  1. Customer requests a refund.
  2. You initiate the refund through the Paystack Refunds API.
  3. Paystack sends a refund.processed webhook.
  4. Your system generates an eTIMS credit note referencing the original invoice.
  5. The credit note is transmitted to KRA, adjusting the tax records.
// Handle refund and generate eTIMS credit note
async function handleRefund(originalReference, refundAmount) {
  // Get the original eTIMS record
  const etimsRecord = await getEtimsRecordByPaystackRef(originalReference);

  if (!etimsRecord) {
    console.error('No eTIMS record found for refunded transaction:', originalReference);
    // Queue for manual review
    await flagForManualReview(originalReference, 'Missing eTIMS record for refund');
    return;
  }

  // Generate credit note through eTIMS
  const creditNote = await generateEtimsCreditNote({
    originalInvoiceNumber: etimsRecord.invoiceNumber,
    originalControlNumber: etimsRecord.controlNumber,
    refundAmount: refundAmount / 100, // Convert from cents
    reason: 'Customer refund',
    date: new Date(),
  });

  // Store the credit note reference
  await storeCreditNoteRecord(originalReference, {
    creditNoteNumber: creditNote.creditNoteNumber,
    controlNumber: creditNote.controlNumber,
    refundAmount: refundAmount / 100,
  });
}

Credit notes are where eTIMS integration gets more complex. Not all refund scenarios are simple. Partial refunds, refunds months after the original sale, and refunds across different tax periods each have their own considerations. Consult your accountant for the correct handling of each scenario.

Compliance Timeline

KRA has rolled out eTIMS in phases, with different deadlines for different categories of businesses. The rollout has been adjusted multiple times, so the current timeline may differ from what was originally announced.

General categories that have been subject to eTIMS requirements include:

  • VAT-registered businesses. These were among the first to be required to use eTIMS.
  • Businesses above certain turnover thresholds. Even if not VAT-registered, businesses with turnover above specified thresholds have been brought into the eTIMS requirement.
  • All businesses. KRA has signaled the intention to eventually require all businesses to use eTIMS, but the timeline for smaller businesses has been adjusted.

[TODO: verify current eTIMS requirements]. Check the KRA website for your specific category and deadline. If you are unsure whether eTIMS applies to your business, ask your accountant.

For developers, the practical implication is this: even if eTIMS is not required for your business today, it likely will be in the near future. Building the integration now (or at least designing your system to accommodate it) is easier than retrofitting it later under deadline pressure.

When you design your payment flow, leave a hook in your webhook handler for eTIMS. Even if the hook does nothing today, having the architecture in place means adding eTIMS later is a matter of implementing one function, not restructuring your entire payment pipeline.

Practical Architecture Tips

Here are patterns that make eTIMS integration less painful:

  • Separate the eTIMS call from the main payment flow. Do not let a slow or failing eTIMS API block your customer from receiving their purchase. Process the payment, deliver value, and generate the invoice asynchronously.
  • Use a queue for eTIMS submissions. Put every successful payment into a queue (BullMQ, RabbitMQ, or a database-backed queue). A worker processes the queue, generating eTIMS invoices. If one fails, it stays in the queue for retry.
  • Store the mapping. Every record in your transactions table should have fields for both the Paystack reference and the eTIMS control number. When both are populated, the transaction is fully reconciled.
  • Build a reconciliation report. A simple query that finds transactions with a Paystack reference but no eTIMS control number tells you which invoices are missing. Run this daily.
  • Handle VAT correctly. If your product prices include VAT, you need to calculate the exclusive amount and the VAT amount separately for the eTIMS invoice. If your prices exclude VAT, add it. Get the math right at the beginning, because fixing it later means regenerating invoices.
  • Test with real scenarios. Test the full flow: payment through Paystack, webhook, eTIMS generation, and storage. Test refunds and credit notes. Test what happens when the eTIMS API is down. Test with amounts that have decimal places (KES 99.50).

Further Reading

For related regulatory and compliance topics:

For building production payment systems with proper compliance, the McTaba 26-week bootcamp covers payment integration, API design, and the business context of building for the Kenyan market.

Key Takeaways

  • eTIMS is KRA's system for electronic tax invoices. Businesses in Kenya must generate eTIMS-compliant invoices for sales. This applies to transactions processed through Paystack and every other payment method.
  • Paystack is a payment processor, not a tax system. Paystack confirms that money moved. eTIMS confirms that the tax invoice was generated and transmitted to KRA. These are separate systems that you connect in your backend.
  • The integration point is your webhook handler. When Paystack sends a charge.success event, your system should generate the eTIMS invoice for that transaction.
  • Third-party eTIMS integration tools and middleware exist. You do not necessarily need to build a direct integration with the KRA eTIMS API from scratch.
  • The compliance timeline has been phased. Different categories of businesses have different deadlines for eTIMS adoption. Check your category and deadline. [TODO: verify current eTIMS requirements].
  • This article provides general information. eTIMS requirements change. Verify the current rules with KRA and consult a tax professional.

Frequently Asked Questions

Does Paystack generate eTIMS invoices for me?
No. Paystack is a payment processor. It confirms that money moved from the customer to your account. eTIMS invoices are your responsibility as the seller. You need to generate the invoice through the eTIMS system (API, app, or web portal) after each successful payment.
What happens if I accept payment but do not generate an eTIMS invoice?
Non-compliance with eTIMS requirements can result in penalties from KRA. Additionally, your business expenses may not be deductible for tax purposes if they are not backed by eTIMS invoices from your suppliers. The consequences of non-compliance are financial and legal. Consult your accountant for the specific penalties in effect.
Can I use the eTIMS mobile app instead of building an API integration?
Yes, if your transaction volume is low enough to manually enter each invoice. For businesses processing more than a handful of transactions per day, manual entry is not practical. If you process payments through Paystack with an automated system, you need an automated eTIMS integration to match.
Does eTIMS apply to B2C transactions?
Yes. eTIMS applies to both B2B and B2C sales. For B2C transactions, the buyer's KRA PIN is not always required (the customer may not have one or may not want to provide it). The invoice is still generated and transmitted to KRA, but the buyer information may be less detailed.
How do I handle eTIMS for subscription payments?
Each recurring payment should generate a separate eTIMS invoice. When Paystack processes a subscription charge and sends a charge.success webhook, your system generates an eTIMS invoice for that specific billing cycle. Monthly subscriptions mean monthly invoices. The process is the same as a one-time payment, just repeated on the billing schedule.

Ready to build real-world apps?

Join the McTaba Labs full-stack marathon (4 months full-time · 6 months part-time). Learn M-Pesa, USSD, and WhatsApp engineering while shipping 8 production apps.

Apply to the McTaba Marathon