Invoice Upload (T109)
Upload fiscal documents (Invoices, Receipts, Debit Notes, Credit Memos) to the EFRIS server. This is the core endpoint for issuing tax-compliant documents. Requires encrypted request and response.
Endpoint Overview
| Property | Value |
|---|---|
| Interface Code | T109 |
| Request Encrypted | ✅ Yes |
| Response Encrypted | ✅ Yes |
| Request Body | Full invoice structure |
| Response Format | JSON (with server-generated fields) |
Flow Description
- Client constructs invoice payload (Seller, Buyer, Goods, Tax, Summary).
- Client calculates totals (Net, Tax, Gross) ensuring mathematical consistency.
- Client sends encrypted request via T109.
- Server validates TINs, commodity codes, tax calculations, and stock availability.
- Server returns signed invoice with
invoiceNo,antifakeCode, andqrCode. - Client stores response for records and prints QR code on receipt.
⚠️ Critical: Ensure
summarytotals match the sum oftaxDetails, andtaxDetailsmatch the sum ofgoodsDetails. Mismatches will result in rejection.
- PHP
- JavaScript / TypeScript
- Python
try {
// Construct invoice payload
$invoiceData = [
'sellerDetails' => [
'tin' => $config['tin'],
'legalName' => 'Your Company Ltd',
'businessName' => 'Your Brand',
'address' => 'Plot 123, Kampala',
'mobilePhone' => '0772140000',
'emailAddress' => 'accounts@yourcompany.com',
'placeOfBusiness' => 'Kampala',
'referenceNo' => 'REF-' . time(),
'isCheckReferenceNo' => '0'
],
'basicInformation' => [
'deviceNo' => $config['device_no'],
'issuedDate' => date('Y-m-d H:i:s'),
'operator' => 'Cashier 01',
'currency' => 'UGX',
'invoiceType' => '1', // 1=Invoice, 2=Credit Note, 4=Debit Note
'invoiceKind' => '1', // 1=Invoice, 2=Receipt
'dataSource' => '103', // 103=WebService API
'invoiceIndustryCode' => '101' // 101=General Industry
],
'buyerDetails' => [
'buyerTin' => '1000029771',
'buyerLegalName' => 'Customer Name',
'buyerBusinessName' => 'Customer Biz',
'buyerAddress' => 'Customer Address',
'buyerEmail' => 'customer@example.com',
'buyerMobilePhone' => '0772999999',
'buyerType' => '0', // 0=B2B, 1=B2C, 2=Foreigner
'buyerCitizenship' => 'UG-Uganda',
'buyerSector' => 'Private'
],
'goodsDetails' => [
[
'item' => 'Test Product',
'itemCode' => 'ITEM001',
'qty' => '1',
'unitOfMeasure' => '101', // From T115
'unitPrice' => '1000.00',
'total' => '1000.00',
'taxRate' => '0.18',
'tax' => '180.00',
'goodsCategoryId' => '100000000',
'vatApplicableFlag' => '1',
'discountFlag' => '2', // 2=Non-discount
'deemedFlag' => '2',
'exciseFlag' => '2'
]
],
'taxDetails' => [
[
'taxCategoryCode' => '01', // 01=Standard 18%
'netAmount' => '1000.00',
'taxRate' => '0.18',
'taxAmount' => '180.00',
'grossAmount' => '1180.00',
'taxRateName' => 'Standard'
]
],
'summary' => [
'netAmount' => '1000.00',
'taxAmount' => '180.00',
'grossAmount' => '1180.00',
'itemCount' => 1,
'modeCode' => '1', // 1=Online, 0=Offline
'remarks' => 'Test invoice'
],
'payWay' => [
[
'paymentMode' => '102', // 102=Cash
'paymentAmount' => '1180.00',
'orderNumber' => 'a'
]
]
];
// Call T109: Upload Invoice
$response = $client->fiscaliseInvoice($invoiceData);
$content = $response['data']['content'] ?? $response;
$invoiceNo = $content['basicInformation']['invoiceNo'] ?? null;
if ($invoiceNo) {
echo "✅ Invoice Uploaded: {$invoiceNo}\n";
echo " QR Code: {$content['summary']['qrCode']}\n";
echo " AntiFake: {$content['basicInformation']['antifakeCode']}\n";
} else {
echo "⚠️ Upload failed or missing invoice number\n";
}
} catch (\UraEfrisSdk\Exceptions\APIException $e) {
echo "❌ Upload failed: " . $e->getMessage() . "\n";
echo " Return Code: " . $e->getReturnCode() . "\n";
}
try {
// Construct invoice payload
const invoiceData = {
sellerDetails: {
tin: config.tin,
legalName: 'Your Company Ltd',
businessName: 'Your Brand',
address: 'Plot 123, Kampala',
mobilePhone: '0772140000',
emailAddress: 'accounts@yourcompany.com',
placeOfBusiness: 'Kampala',
referenceNo: `REF-${Date.now()}`,
isCheckReferenceNo: '0'
},
basicInformation: {
deviceNo: config.device_no,
issuedDate: new Date().toISOString().slice(0, 19).replace('T', ' '),
operator: 'Cashier 01',
currency: 'UGX',
invoiceType: '1', // 1=Invoice, 2=Credit Note, 4=Debit Note
invoiceKind: '1', // 1=Invoice, 2=Receipt
dataSource: '103',
invoiceIndustryCode: '101'
},
buyerDetails: {
buyerTin: '1000029771',
buyerLegalName: 'Customer Name',
buyerBusinessName: 'Customer Biz',
buyerAddress: 'Customer Address',
buyerEmail: 'customer@example.com',
buyerMobilePhone: '0772999999',
buyerType: '0', // 0=B2B, 1=B2C, 2=Foreigner
buyerCitizenship: 'UG-Uganda',
buyerSector: 'Private'
},
goodsDetails: [
{
item: 'Test Product',
itemCode: 'ITEM001',
qty: '1',
unitOfMeasure: '101',
unitPrice: '1000.00',
total: '1000.00',
taxRate: '0.18',
tax: '180.00',
goodsCategoryId: '100000000',
vatApplicableFlag: '1',
discountFlag: '2',
deemedFlag: '2',
exciseFlag: '2'
}
],
taxDetails: [
{
taxCategoryCode: '01',
netAmount: '1000.00',
taxRate: '0.18',
taxAmount: '180.00',
grossAmount: '1180.00',
taxRateName: 'Standard'
}
],
summary: {
netAmount: '1000.00',
taxAmount: '180.00',
grossAmount: '1180.00',
itemCount: 1,
modeCode: '1', // 1=Online, 0=Offline
remarks: 'Test invoice'
},
payWay: [
{
paymentMode: '102',
paymentAmount: '1180.00',
orderNumber: 'a'
}
]
};
// Call T109: Upload Invoice
const response = await client.fiscaliseInvoice(invoiceData);
const content = response?.data?.content ?? response;
const invoiceNo = content?.basicInformation?.invoiceNo;
if (invoiceNo) {
console.log('✅ Invoice Uploaded:', invoiceNo);
console.log(' QR Code:', content.summary.qrCode);
console.log(' AntiFake:', content.basicInformation.antifakeCode);
return content;
} else {
console.warn('⚠️ Upload failed or missing invoice number');
return null;
}
} catch (error: any) {
console.error(`❌ Upload failed: ${error.message}`);
if (error.returnCode) {
console.error(` Return Code: ${error.returnCode}`);
}
throw error;
}
try:
# Construct invoice payload
invoice_data = {
"sellerDetails": {
"tin": config["tin"],
"legalName": "Your Company Ltd",
"businessName": "Your Brand",
"address": "Plot 123, Kampala",
"mobilePhone": "0772140000",
"emailAddress": "accounts@yourcompany.com",
"placeOfBusiness": "Kampala",
"referenceNo": f"REF-{int(time.time())}",
"isCheckReferenceNo": "0"
},
"basicInformation": {
"deviceNo": config["device_no"],
"issuedDate": datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
"operator": "Cashier 01",
"currency": "UGX",
"invoiceType": "1", # 1=Invoice, 2=Credit Note, 4=Debit Note
"invoiceKind": "1", # 1=Invoice, 2=Receipt
"dataSource": "103",
"invoiceIndustryCode": "101"
},
"buyerDetails": {
"buyerTin": "1000029771",
"buyerLegalName": "Customer Name",
"buyerBusinessName": "Customer Biz",
"buyerAddress": "Customer Address",
"buyerEmail": "customer@example.com",
"buyerMobilePhone": "0772999999",
"buyerType": "0", # 0=B2B, 1=B2C, 2=Foreigner
"buyerCitizenship": "UG-Uganda",
"buyerSector": "Private"
},
"goodsDetails": [
{
"item": "Test Product",
"itemCode": "ITEM001",
"qty": "1",
"unitOfMeasure": "101",
"unitPrice": "1000.00",
"total": "1000.00",
"taxRate": "0.18",
"tax": "180.00",
"goodsCategoryId": "100000000",
"vatApplicableFlag": "1",
"discountFlag": "2",
"deemedFlag": "2",
"exciseFlag": "2"
}
],
"taxDetails": [
{
"taxCategoryCode": "01",
"netAmount": "1000.00",
"taxRate": "0.18",
"taxAmount": "180.00",
"grossAmount": "1180.00",
"taxRateName": "Standard"
}
],
"summary": {
"netAmount": "1000.00",
"taxAmount": "180.00",
"grossAmount": "1180.00",
"itemCount": 1,
"modeCode": "1", # 1=Online, 0=Offline
"remarks": "Test invoice"
},
"payWay": [
{
"paymentMode": "102",
"paymentAmount": "1180.00",
"orderNumber": "a"
}
]
}
# Call T109: Upload Invoice
response = client.fiscalise_invoice(invoice_data)
content = response.get("data", {}).get("content", response)
invoice_no = content.get("basicInformation", {}).get("invoiceNo")
if invoice_no:
print("✅ Invoice Uploaded:", invoice_no)
print(" QR Code:", content.get("summary", {}).get("qrCode"))
print(" AntiFake:", content.get("basicInformation", {}).get("antifakeCode"))
else:
print("⚠️ Upload failed or missing invoice number")
except Exception as e:
print(f"❌ Upload failed: {e}")
if hasattr(e, "return_code"):
print(f" Return Code: {e.return_code}")
raise
Request Structure
{
"sellerDetails": {
"tin": "1000029771",
"legalName": "Your Company Ltd",
"businessName": "Your Brand",
"address": "Plot 123",
"mobilePhone": "0772140000",
"emailAddress": "accounts@yourcompany.com",
"placeOfBusiness": "Kampala",
"referenceNo": "REF-123456",
"isCheckReferenceNo": "0"
},
"basicInformation": {
"deviceNo": "TCS9e0df01728335239",
"issuedDate": "2025-02-19 10:00:00",
"operator": "Cashier 01",
"currency": "UGX",
"invoiceType": "1",
"invoiceKind": "1",
"dataSource": "103",
"invoiceIndustryCode": "101"
},
"buyerDetails": {
"buyerTin": "1000029771",
"buyerLegalName": "Customer Name",
"buyerBusinessName": "Customer Biz",
"buyerAddress": "Customer Address",
"buyerEmail": "customer@example.com",
"buyerMobilePhone": "0772999999",
"buyerType": "0",
"buyerCitizenship": "UG-Uganda",
"buyerSector": "Private"
},
"goodsDetails": [
{
"item": "Test Product",
"itemCode": "ITEM001",
"qty": "1",
"unitOfMeasure": "101",
"unitPrice": "1000.00",
"total": "1000.00",
"taxRate": "0.18",
"tax": "180.00",
"goodsCategoryId": "100000000",
"vatApplicableFlag": "1",
"discountFlag": "2",
"deemedFlag": "2",
"exciseFlag": "2"
}
],
"taxDetails": [
{
"taxCategoryCode": "01",
"netAmount": "1000.00",
"taxRate": "0.18",
"taxAmount": "180.00",
"grossAmount": "1180.00",
"taxRateName": "Standard"
}
],
"summary": {
"netAmount": "1000.00",
"taxAmount": "180.00",
"grossAmount": "1180.00",
"itemCount": 1,
"modeCode": "1",
"remarks": "Test invoice"
},
"payWay": [
{
"paymentMode": "102",
"paymentAmount": "1180.00",
"orderNumber": "a"
}
]
}
Response Structure
{
"basicInformation": {
"invoiceId": "1000002",
"invoiceNo": "00000000001",
"antifakeCode": "201905081711",
"deviceNo": "TCS9e0df01728335239",
"issuedDate": "19/02/2025 10:00:00",
"operator": "Cashier 01",
"currency": "UGX",
"invoiceType": "1",
"invoiceKind": "1",
"dataSource": "103"
},
"summary": {
"netAmount": "1000.00",
"taxAmount": "180.00",
"grossAmount": "1180.00",
"itemCount": 1,
"modeCode": "1",
"qrCode": "02000000416B9C322000150744000016E3600000037DCD..."
},
"sellerDetails": {
"branchName": "Head Office",
"branchCode": "01"
}
}
Field Descriptions
Seller Details
| Field | Required | Type | Description |
|---|---|---|---|
tin | ✅ Yes | String (10-20) | Seller's TIN (must match globalInfo) |
legalName | ✅ Yes | String (256) | Registered legal name |
businessName | ❌ No | String (256) | Trading name |
mobilePhone | ❌ No | String (30) | Contact phone |
emailAddress | ✅ Yes | String (50) | Valid email format |
referenceNo | ✅ Yes | String (50) | Unique transaction reference |
isCheckReferenceNo | ❌ No | String (1) | 0=No (default), 1=Yes (enforce uniqueness) |
Basic Information
| Field | Required | Type | Description |
|---|---|---|---|
deviceNo | ✅ Yes | String (20) | Registered device serial number |
issuedDate | ✅ Yes | Date | yyyy-MM-dd HH:mm:ss |
operator | ✅ Yes | String (150) | Cashier/operator name |
currency | ✅ Yes | String (10) | UGX or from T115 dictionary |
invoiceType | ✅ Yes | String (1) | 1=Invoice/Receipt, 2=Credit Note, 4=Debit Note, 5=Credit Memo |
invoiceKind | ✅ Yes | String (1) | 1=Invoice, 2=Receipt |
dataSource | ✅ Yes | String (3) | 103=WebService API |
modeCode | ✅ Yes | String (1) | 1=Online, 0=Offline (requires QR code generation client-side) |
Buyer Details
| Field | Required | Type | Description |
|---|---|---|---|
buyerTin | ❌ No | String (10-20) | Mandatory for B2B (buyerType=0) or B2G (buyerType=3) |
buyerLegalName | ❌ No | String (256) | Mandatory for B2B |
buyerType | ✅ Yes | String (1) | 0=B2B, 1=B2C, 2=Foreigner, 3=B2G |
buyerCitizenship | ❌ No | String (128) | Required for Foreigner (buyerType=2) |
buyerMobilePhone | ❌ No | String (30) | Contact number |
Goods Details (Array)
| Field | Required | Type | Description |
|---|---|---|---|
item | ✅ Yes | String (200) | Product name |
itemCode | ✅ Yes | String (50) | Must match registered goods code |
qty | ✅ Yes | Number | Quantity (must be positive for invoices) |
unitPrice | ✅ Yes | Number | Unit price (excl. tax) |
total | ✅ Yes | Number | qty * unitPrice (max 2 decimals) |
taxRate | ✅ Yes | Number | e.g., 0.18 for 18% |
tax | ✅ Yes | Number | Calculated tax amount |
goodsCategoryId | ✅ Yes | String (18) | From T115/T123 dictionary |
vatApplicableFlag | ❌ No | String (1) | 1=Applicable, 0=Not Applicable |
discountFlag | ✅ Yes | String (1) | 0=Discount Amount, 1=Discount Item, 2=Non-discount |
exciseFlag | ✅ Yes | String (1) | 1=Excise, 2=Not Excise |
Tax Details (Array)
| Field | Required | Type | Description |
|---|---|---|---|
taxCategoryCode | ✅ Yes | String (2) | 01=Standard, 02=Zero, 03=Exempt, 05=Excise |
netAmount | ✅ Yes | Number | Sum of net amounts for this tax category |
taxAmount | ✅ Yes | Number | Sum of tax amounts for this category |
grossAmount | ✅ Yes | Number | netAmount + taxAmount |
taxRate | ✅ Yes | Number | e.g., 0.18 |
Summary
| Field | Required | Type | Description |
|---|---|---|---|
netAmount | ✅ Yes | Number | Total net amount (must match taxDetails sum) |
taxAmount | ✅ Yes | Number | Total tax amount (must match taxDetails sum) |
grossAmount | ✅ Yes | Number | Total gross amount (must match taxDetails sum) |
itemCount | ✅ Yes | Number | Count of goods lines (excluding discount lines) |
modeCode | ✅ Yes | String (1) | 1=Online, 0=Offline |
qrCode | ❌ No | String (500) | Required if modeCode=0 (Offline) |
Payment Way (Array)
| Field | Required | Type | Description |
|---|---|---|---|
paymentMode | ✅ Yes | String | 101=Credit, 102=Cash, 105=Mobile Money, etc. |
paymentAmount | ✅ Yes | Number | Amount paid via this mode |
orderNumber | ✅ Yes | String | Sort order (e.g., 'a', 'b', 'c') |
Return Codes (T109 Specific)
| Code | Message | Description |
|---|---|---|
00 | SUCCESS | Invoice uploaded successfully |
1040 | Invoice number already exists | Duplicate invoice number detected |
1100 | sellerDetails-->tin:cannot be empty! | Missing seller TIN |
1102 | sellerDetails-->tin:The inner tin must be the same as the tin in the outer packet! | TIN mismatch |
1316 | goodsDetails-->total:Multiply the quantity by the product of the unit price... | Calculation error (total != qty * price) |
1342 | taxDetails-->:'netAmount' plus 'taxAmount' must equal 'grossAmount'! | Tax calculation mismatch |
1344 | summary-->taxAmount:Must be equal to the sum of all taxAmount's in taxDetails! | Summary mismatch |
1600 | Inventory shortage | Insufficient stock for goods (if stock management enabled) |
2075 | Invoice amount exceeds the maximum limit! | Exceeds maxGrossAmount from T103 |
2786 | You cannot issue offline invoices! | Offline mode disabled for this taxpayer |
2821 | buyerDetails-->buyerTin: you don't have permission to issue deemed invoices! | Missing deemed invoice permission |
3088 | Device has been blocked | Contact URA |
💡 Tip: Always validate calculations client-side before sending. The server strictly enforces
net + tax = grossconsistency acrossgoodsDetails,taxDetails, andsummary.
Common Use Cases
-
Standard B2B Invoice
SetbuyerType=0, providebuyerTin,buyerLegalName. UseinvoiceType=1,invoiceKind=1. -
Simplified Receipt (B2C)
SetbuyerType=1,buyerTinoptional. UseinvoiceKind=2for receipt. -
Debit Note
SetinvoiceType=4, provideoriInvoiceId(original invoice ID). Adjust amounts positively. -
Offline Mode
SetmodeCode=0. Generate QR code client-side using URA algorithm. Upload later when online. -
Export Invoice
SetinvoiceIndustryCode=102. ProvidebuyerPassportNum,buyerCitizenship. Zero-rated tax.