EFRIS API Integration Hub

Welcome to the developer portal for the EFRIS Multi-Tenant Integration Gateway. Our platform decouples your internal ERP or billing system from the complexities of the Uganda Revenue Authority (URA) EFRIS protocol.

💡 Why use the Gateway?

Integrating directly with URA requires custom XML envelopes, digital signatures, AES-128-ECB handshake encryption, and active key expiry tracking. Our middleware wraps these complex steps into standard REST/JSON endpoints, allowing you to achieve compliance in hours instead of months.

Key Features

  • Dynamic Key Handshakes: Automatically handles EFRIS handshake protocols (T101, T104, T103) in the background.
  • Simple Format Abstraction: Submit invoices with gross, tax-inclusive prices and the middleware calculates net amounts, VAT, and EFRIS discount lines.
  • Built-in Digital Signing: Securely signs request packages with your uploaded .pfx certificate.
  • Generic Passthrough Engine: Submit custom payloads directly to any EFRIS T-code interface while the middleware takes care of packaging, signing, and decryption.

📚 Resources & Downloads

Get started quickly with our comprehensive documentation and API collection.

📖
Integration Guide

Complete step-by-step guide with examples for all EFRIS endpoints including T109 invoicing, T130 products, T131 stock, and more.

⬇ Download Markdown
📬
Postman Collection

Ready-to-use Postman collection with all API endpoints pre-configured. Import and start testing immediately with your API key.

⬇ Download JSON

🎥 Integration Video Tutorials

Learn through step-by-step video guides from our development team.

Loading videos...

🔌 Authentication

All external API calls must include your company's API key. This key identifies the tenant account, whitelists the request IP, and loads the corresponding EFRIS certificate for signing.

Header Requirement:
X-API-Key: your_company_api_key_here

🧪 Sandbox & Testing

We provide a fully configured test environment so you can begin testing endpoints immediately without needing to set up a database company or configure certificates.

🚀 Pre-Configured Test Credentials

Use these credentials directly in Swagger UI or your requests:

  • API Key (`X-API-Key`): efris_mTGKiJaI8uCEi-NpT-_DuwM257cJeUDgA5YFPkhk6ew
  • Sandbox TIN: 1014409555 (Use this TIN for seller/buyer fields during testing)
  • Mock Device Number: 1014409555_02

To test inside the interactive console, click the Authorize button in Swagger UI (/docs), paste the key efris_mTGKiJaI8uCEi-NpT-_DuwM257cJeUDgA5YFPkhk6ew, and click authorize.

📝 Submit Invoice (Simple Format)

The recommended approach for ERPs is the Simple Format. Under this mode (triggered by sending "format": "simple" in the payload), you only send the gross, tax-inclusive price, and the gateway automatically calculates EFRIS tax, net amounts, and required separate discount lines.

Choose your programming language below to view the request sample:

import requests
import json

url = "https://efrisintegration.nafacademy.com/api/external/efris/submit-invoice"
headers = {
    "X-API-Key": "efris_mTGKiJaI8uCEi-NpT-_DuwM257cJeUDgA5YFPkhk6ew",
    "Content-Type": "application/json"
}
payload = {
    "format": "simple",
    "invoiceNumber": "INV-2026-001",
    "invoiceDate": "2026-05-29",
    "customerName": "Excise Buyer Ltd",
    "customerTin": "1000123456",
    "buyerType": "0",
    "paymentMethod": "102",
    "items": [
        {
            "item_name": "Premium Beer",
            "item_code": "BEER-01",
            "quantity": 10,
            "unit_price": 5000, # Gross price (inclusive of VAT & Excise)
            "tax_rate": 18,
            "excise_flag": "1",
            "excise_rule": "1",
            "excise_rate": "30%",
            "excise_tax": 9000
        }
    ]
}

response = requests.post(url, headers=headers, json=payload)
print(json.dumps(response.json(), indent=2))
use Illuminate\Support\Facades\Http;

$response = Http::withHeaders([
    'X-API-Key' => 'efris_mTGKiJaI8uCEi-NpT-_DuwM257cJeUDgA5YFPkhk6ew',
    'Content-Type' => 'application/json'
])->post('https://efrisintegration.nafacademy.com/api/external/efris/submit-invoice', [
    'format' => 'simple',
    'invoiceNumber' => 'INV-2026-001',
    'invoiceDate' => '2026-05-29',
    'customerName' => 'Excise Buyer Ltd',
    'customerTin' => '1000123456',
    'buyerType' => '0',
    'paymentMethod' => '102',
    'items' => [
        [
            'item_name' => 'Premium Beer',
            'item_code' => 'BEER-01',
            'quantity' => 10,
            'unit_price' => 5000,
            'tax_rate' => 18,
            'excise_flag' => '1',
            'excise_rule' => '1',
            'excise_rate' => '30%',
            'excise_tax' => 9000
        ]
    ]
]);

print_r($response->json());
const axios = require('axios');

const url = 'https://efrisintegration.nafacademy.com/api/external/efris/submit-invoice';
const headers = {
    'X-API-Key': 'efris_mTGKiJaI8uCEi-NpT-_DuwM257cJeUDgA5YFPkhk6ew',
    'Content-Type': 'application/json'
};
const payload = {
    format: 'simple',
    invoiceNumber: 'INV-2026-001',
    invoiceDate: '2026-05-29',
    customerName: 'Excise Buyer Ltd',
    customerTin: '1000123456',
    buyerType: '0',
    paymentMethod: '102',
    items: [
        {
            item_name: 'Premium Beer',
            item_code: 'BEER-01',
            quantity: 10,
            unit_price: 5000,
            tax_rate: 18,
            excise_flag: '1',
            excise_rule: '1',
            excise_rate: '30%',
            excise_tax: 9000
        }
    ]
};

axios.post(url, payload, { headers })
    .then(response => console.log(response.data))
    .catch(error => console.error(error));
using System;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;

class Program
{
    static async Task Main()
    {
        var client = new HttpClient();
        client.DefaultRequestHeaders.Add("X-API-Key", "efris_mTGKiJaI8uCEi-NpT-_DuwM257cJeUDgA5YFPkhk6ew");

        var json = @"{
            ""format"": ""simple"",
            ""invoiceNumber"": ""INV-2026-001"",
            ""invoiceDate"": ""2026-05-29"",
            ""customerName"": ""Excise Buyer Ltd"",
            ""customerTin"": ""1000123456"",
            ""buyerType"": ""0"",
            ""paymentMethod"": ""102"",
            ""items"": [
                {
                    ""item_name"": ""Premium Beer"",
                    ""item_code"": ""BEER-01"",
                    ""quantity"": 10,
                    ""unit_price"": 5000,
                    ""tax_rate"": 18,
                    ""excise_flag"": ""1"",
                    ""excise_rule"": ""1"",
                    ""excise_rate"": ""30%"",
                    ""excise_tax"": 9000
                }
            ]
        }";

        var content = new StringContent(json, Encoding.UTF8, "application/json");
        var response = await client.PostAsync("https://efrisintegration.nafacademy.com/api/external/efris/submit-invoice", content);
        Console.WriteLine(await response.Content.ReadAsStringAsync());
    }
}
package main

import (
    "bytes"
    "fmt"
    "io"
    "net/http"
)

func main() {
    url := "https://efrisintegration.nafacademy.com/api/external/efris/submit-invoice"
    jsonStr := []byte(`{
        "format": "simple",
        "invoiceNumber": "INV-2026-001",
        "invoiceDate": "2026-05-29",
        "customerName": "Excise Buyer Ltd",
        "customerTin": "1000123456",
        "buyerType": "0",
        "paymentMethod": "102",
        "items": [
            {
                "item_name": "Premium Beer",
                "item_code": "BEER-01",
                "quantity": 10,
                "unit_price": 5000,
                "tax_rate": 18,
                "excise_flag": "1",
                "excise_rule": "1",
                "excise_rate": "30%",
                "excise_tax": 9000
            }
        ]
    }`)

    req, _ := http.NewRequest("POST", url, bytes.NewBuffer(jsonStr))
    req.Header.Set("X-API-Key", "efris_mTGKiJaI8uCEi-NpT-_DuwM257cJeUDgA5YFPkhk6ew")
    req.Header.Set("Content-Type", "application/json")

    client := &http.Client{}
    resp, _ := client.Do(req)
    defer resp.Body.Close()

    body, _ := io.ReadAll(resp.Body)
    fmt.Println(string(body))
}
import okhttp3.*;
import java.io.IOException;

public class EfrisClient {
    public static void main(String[] args) throws IOException {
        OkHttpClient client = new OkHttpClient();

        MediaType mediaType = MediaType.parse("application/json");
        RequestBody body = RequestBody.create(mediaType, "{\n  \"format\": \"simple\",\n  \"invoiceNumber\": \"INV-2026-001\",\n  \"invoiceDate\": \"2026-05-29\",\n  \"customerName\": \"Excise Buyer Ltd\",\n  \"customerTin\": \"1000123456\",\n  \"buyerType\": \"0\",\n  \"paymentMethod\": \"102\",\n  \"items\": [\n    {\n      \"item_name\": \"Premium Beer\",\n      \"item_code\": \"BEER-01\",\n      \"quantity\": 10,\n      \"unit_price\": 5000,\n      \"tax_rate\": 18,\n      \"excise_flag\": \"1\",\n      \"excise_rule\": \"1\",\n      \"excise_rate\": \"30%\",\n      \"excise_tax\": 9000\n    }\n  ]\n}");
        Request request = new Request.Builder()
            .url("https://efrisintegration.nafacademy.com/api/external/efris/submit-invoice")
            .post(body)
            .addHeader("X-API-Key", "efris_mTGKiJaI8uCEi-NpT-_DuwM257cJeUDgA5YFPkhk6ew")
            .addHeader("Content-Type", "application/json")
            .build();

        try (Response response = client.newCall(request).execute()) {
            System.out.println(response.body().string());
        }
    }
}
require 'net/http'
require 'uri'
require 'json'

uri = URI.parse("https://efrisintegration.nafacademy.com/api/external/efris/submit-invoice")
request = Net::HTTP::Post.new(uri)
request["X-API-Key"] = "efris_mTGKiJaI8uCEi-NpT-_DuwM257cJeUDgA5YFPkhk6ew"
request["Content-Type"] = "application/json"
request.body = JSON.dump({
  "format" => "simple",
  "invoiceNumber" => "INV-2026-001",
  "invoiceDate" => "2026-05-29",
  "customerName" => "Excise Buyer Ltd",
  "customerTin" => "1000123456",
  "buyerType" => "0",
  "paymentMethod" => "102",
  "items" => [
    {
      "item_name" => "Premium Beer",
      "item_code" => "BEER-01",
      "quantity" => 10,
      "unit_price" => 5000,
      "tax_rate" => 18,
      "excise_flag" => "1",
      "excise_rule" => "1",
      "excise_rate" => "30%",
      "excise_tax" => 9000
    }
  ]
})

response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: uri.scheme == "https") do |http|
  http.request(request)
end

puts response.body
import 'package:dio/dio.dart';

void main() async {
  var dio = Dio();
  dio.options.headers["X-API-Key"] = "efris_mTGKiJaI8uCEi-NpT-_DuwM257cJeUDgA5YFPkhk6ew";
  dio.options.headers["Content-Type"] = "application/json";

  var payload = {
    "format": "simple",
    "invoiceNumber": "INV-2026-001",
    "invoiceDate": "2026-05-29",
    "customerName": "Excise Buyer Ltd",
    "customerTin": "1000123456",
    "buyerType": "0",
    "paymentMethod": "102",
    "items": [
      {
        "item_name": "Premium Beer",
        "item_code": "BEER-01",
        "quantity": 10,
        "unit_price": 5000,
        "tax_rate": 18,
        "excise_flag": "1",
        "excise_rule": "1",
        "excise_rate": "30%",
        "excise_tax": 9000
      }
    ]
  };

  try {
    var response = await dio.post(
      "https://efrisintegration.nafacademy.com/api/external/efris/submit-invoice",
      data: payload,
    );
    print(response.data);
  } catch (e) {
    print(e);
  }
}

Expected Successful Response Schema

The endpoint returns both the nested fullEfrisResponse (as per our integration guide) and flat convenience fields at the top level for backward compatibility:

{
  "success": true,
  "message": "Invoice successfully submitted to EFRIS",
  "fdn": "FDN-123456",
  "verificationCode": "VER-1234",
  "qrCode": "https://ura.go.ug/qr/123",
  "fiscalized_at": "2026-05-29T21:00:00Z",
  "invoiceNumber": "INV-2026-001",
  "fullEfrisResponse": {
    "fdn": "FDN-123456",
    "verification_code": "VER-1234",
    "qr_code": "https://ura.go.ug/qr/123",
    ...
  }
}

🧮 Excise Duty Calculation Math

Uganda EFRIS regulations dictate specific mathematical structures for tax details when Excise Duty (Category 05) is applied to items alongside Standard VAT (Category 01).

📊 Standard VAT (Category 01)
  • VAT Base (VAT netAmount) = Total Gross / 1.18
  • VAT Tax = Total Gross - VAT Base
  • VAT Gross = Total Gross
📈 Excise Duty (Category 05)
  • Excise netAmount = VAT Base - Total Excise Tax
  • Excise taxAmount = Total Excise Tax
  • Excise grossAmount = VAT Base

The gateway automatically handles this breakdown. If your ERP passes an item priced at 50,000 gross with 9,000 excise tax, the gateway computes the tax details as follows:

Category Code Net Amount Tax Amount Gross Amount
Standard VAT 01 42,372.88 7,627.12 50,000.00
Excise Duty 05 33,372.88 9,000.00 42,372.88

⚡ Generic Passthrough Engine

If you need to call specialized EFRIS T-code interfaces (e.g. daily reports, branch listings, negative stock settings) that don't have dedicated REST endpoints, you can use the **Generic Passthrough Engine**.

Endpoint: POST /api/external/efris/passthrough/{interface_code}

The gateway automatically loads keys, handles handshake keys (T104), encrypts/decrypts the body via AES-128, and wraps/signs the request using your digital certificate. You only pass the raw content JSON.

Example Passthrough Request (T125 Query Excise Duty)

POST /api/external/efris/passthrough/T125?encrypt_code=0
Header: X-API-Key: efris_mTGKiJaI8uCEi-NpT-_DuwM257cJeUDgA5YFPkhk6ew

{
  "exciseDutyCode": "",
  "exciseDutyName": "beer"
}

📦 Products & Inventory

Before an item can be fiscalized, it must be registered with EFRIS. We provide endpoints to register products, adjust stock quantities (stock increases/decreases), and query commodity categories.

Multi-Unit / Packaging Magic

If you sell bulk packages (e.g. Boxes, Crates) but manage inventory in sub-units (e.g. Sticks, Bottles), you can specify packaging scales during registration and invoice submissions. Use package_scaled, piece_scaled, and piece_qty fields in the item lists as described in the API schemas.

🗂️ Endpoint Directory

Below is a directory of the EFRIS integration endpoints. Click any route to explore it interactively in the Swagger Console.

Method Path EFRIS Code Description
POST /api/external/efris/submit-invoice T109 Submit & fiscalize invoice (Simple/EFRIS formats)
POST /api/external/efris/submit-credit-note T110 Apply for credit notes / debit notes reversals
POST /api/external/efris/passthrough/{interface_code} T101-T187 Run any arbitrary EFRIS request dynamically
POST /api/external/efris/register-product T130 Register goods/services in URA EFRIS catalog
POST /api/external/efris/stock-increase T131 Increment EFRIS stock balance (maintain records)
POST /api/external/efris/stock-decrease T131 Decrement EFRIS stock balance (maintain records)
GET /api/external/efris/server-time T101 Check URA clock sync & connection health
GET /api/external/efris/registration-details T102 Get company's profile registration details
GET /api/external/efris/taxpayer/{tin} T119 Look up taxpayer legal info by TIN from URA
GET /api/external/efris/commodity-categories T124 List valid EFRIS category codes (paginated)
GET /api/external/efris/credit-notes T112 Query applied credit notes list and approval status
GET /api/external/efris/excise-duty T125 List valid excise duty codes and rate types
GET /api/external/efris/goods T127 List company's registered goods catalog
GET /api/external/efris/invoice/{invoice_number} T108 Retrieve fiscal FDN / QR code details for invoice

🚀 Explore the Interactive Documentation Console

Authorize with your key and try requesting EFRIS servers live using our Swagger console explorer.

Open Swagger Console (/docs)