# EFRIS Gateway — ERP Integration Guide

How to connect an accounting system, POS or ERP to URA's EFRIS through the EFRIS
Gateway: what to send for each business operation, and what you get back.

---

## Contents

1. [Before you start](#1-before-you-start)
2. [Connecting](#2-connecting)
3. [The integration flow](#3-the-integration-flow)
4. [Products](#4-products)
5. [Stock](#5-stock)
6. [Invoices and receipts](#6-invoices-and-receipts)
7. [Credit notes](#7-credit-notes)
8. [Lookups](#8-lookups)
9. [Printing the fiscal document](#9-printing-the-fiscal-document)
10. [Reliability rules](#10-reliability-rules)
11. [Errors](#11-errors)
12. [Appendix — code tables](#appendix--code-tables)

---

## 1. Before you start

Your EFRIS provider sets up your account. Before your first request, confirm with
them that it has:

| Item | Why it matters |
| --- | --- |
| **TIN, legal name, address and phone** | Printed as the seller on every document. They come from your account, never from the request. |
| **EFRIS device number** | Issued by URA and required on every document. |
| **Business email address** | URA refuses documents without it. |
| **VAT registration status** | Decides whether you issue tax invoices or receipts — see [Tax invoice or receipt](#tax-invoice-or-receipt). |
| **Test or live** | Your provider sets whether your documents go to URA's test or live system. Moving to live needs no change in your code. |
| **API key** | Keep it secret. If it leaks, ask your provider to replace it; the old key stops working at once. |

Your provider can also limit your key to your server's IP address and set a
daily request limit.

---

## 2. Connecting

### Base URL

```
https://efrisgateway.com/api/efris
```

Every path in this guide is relative to it, so `POST /submit-invoice` means
`POST https://efrisgateway.com/api/efris/submit-invoice`.

Keep the base URL and the API key in your system's settings, not in its code:
both can change.

### Headers

```http
X-API-Key: efris_xxxxxxxxxxxxxxxx
Content-Type: application/json
```

Every response also carries your quota: `X-RateLimit-Limit`,
`X-RateLimit-Remaining` and `X-RateLimit-Reset`.

### Field names

Send fields in **`snake_case` or `camelCase`**, whichever your system produces.
The tables below list the accepted spellings. Responses are in camelCase.

### Responses

**Success** — each section below shows what comes back. Lookups return:

```json
{
  "success": true,
  "result": { "...": "the data" },
  "returnCode": "00"
}
```

**Failure** — every endpoint:

```json
{
  "success": false,
  "error": "Bad request",
  "detail": "EFRIS error 2122: goodsDetails-->itemCode:Item code and Name is not configured with URA. What to do: This item is not registered with URA. Register the product (item code and name) first, then send the invoice again.",
  "ura": {
    "code": "2122",
    "message": "goodsDetails-->itemCode:Item code and Name is not configured with URA.",
    "field": "Items → item code",
    "hint": "This item is not registered with URA. Register the product (item code and name) first, then send the invoice again."
  }
}
```

Show `detail` to your user — usually an accountant. It says what is wrong and
what to do, in words they can act on. When URA refused the document, `detail`
starts with URA's code and message and ends with `What to do:`, and `ura` carries
the same parts separately: `field` is the field in plain words, `hint` is what to
do.

### HTTP status codes

| Status | Meaning | What to do |
| --- | --- | --- |
| `200` | Accepted | Store the result. |
| `400` | The request is incomplete, or URA refused the document | Show `detail`; fix what it says. Do not resend unchanged. |
| `401` | Missing, wrong or disabled API key | Check the key; contact your provider. |
| `403` | Licence expired, or your address is not allowed | Contact your provider. |
| `404` | No such path | Check the path. |
| `422` | The body is not in the expected shape | See `details` for the fields. |
| `429` | Daily limit reached | Wait until `X-RateLimit-Reset`. |
| `502` | URA could not be reached or answered with a problem | Show `detail`. Queries can be retried; for invoices, read [Reliability rules](#10-reliability-rules) first. If `detail` says URA accepted it, do **not** resend. |
| `500` | Your account is not fully set up, or an unexpected fault | Show `detail` and contact your provider. If there is a `reference`, quote it. |

---

## 3. The integration flow

Do these in order.

| Step | Call | When |
| --- | --- | --- |
| 1. Check the connection | `GET /diagnostics` | At setup, and whenever something stops working |
| 2. Load reference data | `GET /units-of-measure`, `GET /payment-methods`, `GET /commodity-categories`, `GET /excise-duty`, `GET /branches` | At setup; refresh occasionally |
| 3. Register products | `POST /register-product` | Once per product, and again when it changes |
| 4. Record stock | `POST /stock-increase` | On every goods receipt, opening stock first |
| 5. Fiscalise sales | `POST /submit-invoice` or `POST /submit-receipt` | On every sale |
| 6. Reverse sales | `POST /submit-credit-note` | On returns and corrections |

### Step 1 — `GET /diagnostics`

Shows your account's details and whether the connection to URA works.

```json
{
  "success": true,
  "configuration": {
    "companyName": "Nakawa Traders Ltd",
    "tin": "1000000001",
    "deviceNumber": "1000000001_02",
    "environment": "sandbox",
    "apiCallsToday": 12,
    "dailyLimit": 5000,
    "licenceEnds": "2027-06-30"
  }
}
```

If `success` is `false`, the response says why — pass it to your provider. Do not
go further until this passes.

---

## 4. Products

A product **must be registered before it can be sold or stocked**. Register each
product once; update it with `operationType: "102"` when its price or description
changes.

### `POST /register-product`

```json
{
  "operationType": "101",
  "item_code": "WTR-500",
  "item_name": "Bottled water 500ml",
  "unit_price": "1500",
  "commodity_code": "50202301",
  "unit_of_measure": "101",
  "is_service": false,
  "stock_prewarning": "10"
}
```

**Response**

```json
{
  "success": true,
  "message": "The product was registered with EFRIS.",
  "productCode": "WTR-500",
  "efrisResponse": null
}
```

### Fields

| Field | Also accepted as | Required | Notes |
| --- | --- | --- | --- |
| `item_code` | `itemCode`, `goodsCode` | **Yes** | Your product code. Invoices refer to it. |
| `item_name` | `itemName`, `goodsName` | **Yes** | |
| `unit_price` | `unitPrice` | **Yes** | Tax-inclusive. |
| `commodity_code` | `commodityCode`, `commodityCategoryId`, `goodsCategoryId` | **Yes** | URA commodity category, from `GET /commodity-categories`. It decides the tax URA applies. |
| `operationType` | `operation_type` | No | `101` new (default), `102` update. |
| `unit_of_measure` | `unitOfMeasure`, `measureUnit` | Send it | From `GET /units-of-measure`. |
| `currency` | | No | `101` (UGX) by default. |
| `is_service` | `isService` | No | `true` for a service. |
| `goods_type_code` | `goodsTypeCode` | No | `101` goods (default), `102` fuel. |
| `stock_prewarning` | `stockPrewarning`, `low_stock_warning` | No | Goods only. |
| `description` | | No | |
| `have_excise_tax` | `haveExciseTax` | No | `101` yes, `102` no (default). See [Excise](#products-with-excise-duty). |
| `excise_duty_code` | `exciseDutyCode` | When excise applies | From `GET /excise-duty`. Leave it out otherwise. |
| `have_piece_unit` | `havePieceUnit` | No | `101` yes, `102` no. See [Packaging](#multi-unit-packaging). |
| `piece_measure_unit` | `pieceMeasureUnit` | With piece units | Unit of the inner piece. |
| `piece_unit_price` | `pieceUnitPrice` | With piece units | Price of one piece. |
| `package_scaled_value` | `packageScaledValue` | With piece units | Outer unit multiplier, usually `1`. |
| `piece_scaled_value` | `pieceScaledValue` | With piece units | Pieces per outer unit, e.g. `24`. |
| `customs_info` | `customsInfo` | No | Imported goods: `{ measure_unit, unit_price, package_scaled, customs_scaled }`. |
| `other_units` | `otherUnits` | No | Further selling units: `[{ unit_code, unit_price, scaled_value, package_scaled }]`. |

### Multi-unit packaging

For goods sold both in bulk and singly — a crate of 24 bottles priced either way:

| Field | Send |
| --- | --- |
| `have_piece_unit` | `"101"` |
| `package_scaled_value` | The outer unit multiplier, usually `"1"` |
| `piece_scaled_value` | Pieces in one outer unit, e.g. `"24"` |
| `piece_measure_unit` | Unit code of the piece |
| `piece_unit_price` | Price of one piece |

Both scaled values must be greater than zero. A product registered with piece
units must be invoiced with `package_scaled`, `piece_scaled` and `piece_qty` on
each line.

### Products with excise duty

Send `have_excise_tax: "101"` and the `excise_duty_code`. Look codes up with
`GET /excise-duty?name=beer`.

| Excise type | Example | `have_piece_unit` |
| --- | --- | --- |
| Percentage of value | Beer at 60% | `"102"` is fine, unless the product is naturally multi-unit |
| Fixed amount per unit | Mineral water at UGX 150 per litre | **Must be `"101"`**, or URA refuses it (error 676) |

For a fixed-amount code, also send `piece_measure_unit` as the excise unit (for
example the litre code), `piece_scaled_value` as the scale factor (`"1"` for a
single-unit product), and `piece_unit_price` as `unit_price ÷ piece_scaled_value`.

**Percentage example**

```json
{
  "operationType": "101",
  "item_code": "BELL-500",
  "item_name": "Bell Lager 500ml",
  "unit_price": "4720",
  "commodity_code": "2203001000",
  "unit_of_measure": "102",
  "have_piece_unit": "102",
  "have_excise_tax": "101",
  "excise_duty_code": "LED190100"
}
```

**Fixed-amount example**

```json
{
  "operationType": "101",
  "item_code": "WATER-1L",
  "item_name": "Mineral Water 1L",
  "unit_price": "2000",
  "commodity_code": "50202310",
  "unit_of_measure": "102",
  "have_piece_unit": "101",
  "piece_measure_unit": "102",
  "piece_scaled_value": "1",
  "package_scaled_value": "1",
  "piece_unit_price": "2000",
  "have_excise_tax": "101",
  "excise_duty_code": "LED190400"
}
```

### Updating a product

Send the same `item_code` with `"operationType": "102"`. Sending `101` for a code
that already exists is refused.

### Registering many products — `POST /register-products`

```json
{
  "products": [
    { "item_code": "WTR-500", "item_name": "Bottled water 500ml", "unit_price": "1500", "commodity_code": "50202301" },
    { "item_code": "WTR-1L",  "item_name": "Bottled water 1L",    "unit_price": "2500", "commodity_code": "50202301" }
  ]
}
```

`items` is accepted in place of `products`. Each entry takes the same fields as a
single registration. If any are refused, `detail` names each one and why.

---

## 5. Stock

URA tracks stock for each registered product. A sale of goods with no recorded
stock is refused, unless negative stock is allowed for your business (check with
`GET /negative-stock-configuration`).

### 5a. Stock received — `POST /stock-increase`

```json
{
  "goodsStockIn": {
    "supplierName": "Crown Beverages Ltd",
    "supplierTin": "1000001234",
    "stockInType": "102",
    "stockInDate": "2026-09-25",
    "goodsTypeCode": "101"
  },
  "goodsStockInItem": [
    { "goodsCode": "WTR-500", "quantity": "240", "unitPrice": "1000", "measureUnit": "101" }
  ]
}
```

**`stockInType`**

| Code | Meaning | Supplier fields |
| --- | --- | --- |
| `101` | Import | `supplierTin` and `supplierName` required |
| `102` | Local purchase | `supplierTin` and `supplierName` required |
| `103` | Manufacture / assembly | Leave them **empty**; send `productionBatchNo` and `productionDate` instead |
| `104` | Opening stock | Use when first going live |

Send `productionBatchNo` and `productionDate` (`YYYY-MM-DD`) only with type `103`.

### 5b. Stock written down — `POST /stock-decrease`

```json
{
  "goodsStockIn": {
    "adjustType": "102",
    "remarks": "Damaged in the warehouse"
  },
  "goodsStockInItem": [
    { "goodsCode": "WTR-500", "quantity": "12", "unitPrice": "1000", "measureUnit": "101" }
  ]
}
```

**`adjustType`** — required

| Code | Meaning |
| --- | --- |
| `101` | Expired goods |
| `102` | Damaged goods |
| `103` | Personal use |
| `104` | Others — `remarks` required |
| `105` | Raw materials |

`operationType` can be left out: the address already says which way the stock moves.
Stock written down has no supplier: leave out `supplierTin`, `supplierName` and `stockInType`.

### Stock fields

The header can be sent as `goodsStockIn` or `goods_stock_in`; the items as
`goodsStockInItem` or `goods_stock_in_item`.

| Header field | Also accepted as |
| --- | --- |
| `supplierTin` | `supplier_tin` |
| `supplierName` | `supplier_name` |
| `stockInType` | `stock_in_type` |
| `stockInDate` | `stock_in_date` |
| `adjustType` | `adjust_type` |
| `productionBatchNo` | `production_batch_no` |
| `productionDate` | `production_date` |
| `branchId` | `branch_id` |
| `remarks` | |

| Item field | Also accepted as | Notes |
| --- | --- | --- |
| `goodsCode` | `goods_code`, `item_code`, `itemCode` | A registered product code |
| `quantity` | `qty` | |
| `unitPrice` | `unit_price` | |
| `measureUnit` | `measure_unit`, `unit_of_measure` | Required. The unit this quantity is counted in; for an item held in two units, the one you mean |
| `remarks` | `remark` | |

**Rules**

| Rule | When |
| --- | --- |
| `stockInType` required | Stock received |
| `supplierName` required | Stock received, except type `103` |
| `supplierTin` empty | Stock written down, or type `103` |
| `supplierName` and `stockInType` empty | Stock written down |
| `adjustType` required | Stock written down |
| `measureUnit` required | Every item, on every stock movement and transfer |
| `remarks` required | Stock written down with `adjustType` `104` |
| `operationType` `101` | Only to `/stock-increase` |
| `operationType` `102` | Only to `/stock-decrease` |

### 5c. Transfer between branches — `POST /stock-transfer`

Moves stock between two branches of your business. Get branch ids from
`GET /branches`.

```json
{
  "goodsStockTransfer": {
    "sourceBranchId": "206637525568955296",
    "destinationBranchId": "206637528324276772",
    "transferTypeCode": "101",
    "remarks": "",
    "rollBackIfError": "0",
    "goodsTypeCode": "101"
  },
  "goodsStockTransferItem": [
    { "goodsCode": "WTR-500", "measureUnit": "101", "quantity": "48", "remarks": "" }
  ]
}
```

| Header field | Also accepted as | Notes |
| --- | --- | --- |
| `sourceBranchId` | `source_branch_id` | **Required** |
| `destinationBranchId` | `destination_branch_id` | **Required**; must differ from the source |
| `transferTypeCode` | `transfer_type_code` | **Required**: `101` out-of-stock adjust, `102` error adjust, `103` other — `remarks` required |
| `remarks` | `remark` | |
| `rollBackIfError` | `roll_back_if_error` | `"1"` cancels the whole transfer if any line fails; `"0"` (default) keeps the rest |
| `goodsTypeCode` | `goods_type_code` | `101` goods (default), `102` fuel |

| Item field | Also accepted as | Notes |
| --- | --- | --- |
| `goodsCode` | `goods_code`, `item_code` | Your product code |
| `measureUnit` | `measure_unit`, `unit_of_measure` | Required. The unit this quantity is counted in |
| `quantity` | `qty` | |

### Stock responses

```json
{
  "success": true,
  "message": "The stock increase was recorded.",
  "efrisResponse": []
}
```

If URA refuses any line, the response is an error whose `detail` names each line
and why.

---

## 6. Invoices and receipts

### Tax invoice or receipt

Which document you issue follows your VAT registration with URA:

| Your business is | You issue | Send sales to | Tax on the items |
| --- | --- | --- | --- |
| Registered for VAT | Tax invoice | `POST /submit-invoice` | **Every item** states its tax type: `18` standard, `0` zero-rated or `"-"` exempt |
| Not registered for VAT | Receipt | `POST /submit-receipt` | **None** — leave `tax_rate` out, or send `0` |

Nothing is changed for you: a sale is issued exactly as you send it, or refused
with a message saying what to fix. Keep your system's documents and tax settings
in line with your VAT registration, and change them if it changes.

**The wrong document is refused.** A business without VAT that sends a tax
invoice is told to issue receipts:

```json
{
  "success": false,
  "error": "Bad request",
  "detail": "Nakawa Traders Ltd (TIN 1000000001) is not registered for VAT, so URA accepts only receipts from it, with no VAT on the items. Issue this sale as a receipt, to /submit-receipt."
}
```

A VAT-registered business that sends a receipt is told, likewise, to issue a
tax invoice.

**Every item on a tax invoice needs a tax type.** Standard (`18`), zero-rated
(`0`) and exempt (`"-"`) are all tax types, and each is kept exactly as you send
it — goods zero-rated for local sale stay zero-rated. An item with no tax type at
all is refused:

```json
{
  "success": false,
  "error": "Bad request",
  "detail": "This business is registered for VAT, so each item on its tax invoice must state its VAT. No tax rate was given for: Bottled water 500ml. Set the VAT on the item in your system (18 standard, 0 zero-rated, \"-\" exempt) and send the sale again, so your books match the invoice URA issues."
}
```

**A receipt carries no VAT.** The price the customer pays is the whole amount, so
a business without VAT records its sales without VAT. A receipt sent with VAT on
any item is refused, naming the items:

```json
{
  "success": false,
  "error": "Bad request",
  "detail": "A receipt carries no VAT - it is what URA issues a business that is not registered for VAT. VAT was charged on: Bottled water 500ml (18%). Remove the VAT in your system (set the item to no VAT) and send the sale again, so your books match the receipt URA issues."
}
```


**A receipt:**

```json
{
  "format": "simple",
  "invoice_number": "RCPT-2026-000118",
  "invoice_date": "2026-09-25",
  "customer_name": "Walk-in Customer",
  "payment_method": "102",
  "operator": "Jane Kasule",
  "items": [
    {
      "item_name": "Bottled water 500ml",
      "item_code": "WTR-500",
      "quantity": 12,
      "unit_price": 1500,
      "unit_of_measure": "101",
      "goods_category_id": "50202301"
    }
  ]
}
```

The response has the same shape as an invoice's ([6h](#6h-the-response)), with
`fiscalData.invoiceKind` `"2"`. Print it as a receipt, with no VAT.

### 6a. Two rules for every sale

- **Always include `"format": "simple"`.**
- **Send tax-inclusive prices.** A product costing 10,000 before 18% VAT is sent
  as `unit_price: 11800`.

### 6b. A tax invoice — `POST /submit-invoice`

```json
{
  "format": "simple",
  "invoice_number": "INV-2026-000412",
  "invoice_date": "2026-09-25",
  "customer_name": "Nakawa Traders Ltd",
  "customer_tin": "1000999888",
  "customer_email": "accounts@nakawatraders.ug",
  "customer_phone": "0772000000",
  "buyer_type": "0",
  "payment_method": "102",
  "currency": "UGX",
  "operator": "Jane Kasule",
  "remarks": "Thank you for your business",
  "items": [
    {
      "item_name": "Bottled water 500ml",
      "item_code": "WTR-500",
      "quantity": 12,
      "unit_price": 1500,
      "tax_rate": 18,
      "unit_of_measure": "101",
      "goods_category_id": "50202301"
    }
  ]
}
```

### Invoice fields

| Field | Also accepted as | Required | Notes |
| --- | --- | --- | --- |
| `format` | | **Yes** | `"simple"` |
| `invoice_number` | `invoiceNumber` | **Yes** | Your document number. Each number can be used once. |
| `invoice_date` | `invoiceDate` | **Yes** | `YYYY-MM-DD` or `YYYY-MM-DD HH:mm:ss`. Sent to URA as given; print the issue date and time URA returns. A date in an earlier VAT period is refused. |
| `customer_name` | `customerName`, `buyerLegalName` | **Yes** | |
| `items` | `goods_details`, `goodsDetails` | **Yes** | At least one line |
| `buyer_type` | `buyerType` | No | `0` business, `1` individual (default), `2` foreigner, `3` government |
| `customer_tin` | `customerTin`, `buyerTin` | For business buyers | |
| `customer_address` | `customerAddress` | No | |
| `customer_email` | `customerEmail` | No | |
| `customer_phone` | `customerPhone` | No | |
| `payment_method` | `paymentMethod` | Send it | A code from `GET /payment-methods` (see the [table](#payment-methods)). Without it the sale prints as credit. |
| `currency` | | No | `UGX` by default |
| `operator` | | No | The person serving, printed as "served by". Without it, your account's default operator is used. |
| `remarks` | `remark`, `notes` | No | Up to 500 characters, printed on the document |
| `invoice_industry_code` | `invoiceIndustryCode` | No | `101` general (default), `102` export goods, `112` export service — see 6f |
| `delivery_terms_code` | `deliveryTermsCode` | For exports | Incoterm |
| `customs` | | For export goods | See 6f |

Do not send seller details; they come from your account.

### Item fields

| Field | Also accepted as | Required | Notes |
| --- | --- | --- | --- |
| `item_name` | `item`, `itemName`, `name` | **Yes** | |
| `item_code` | `code` | **Yes** | A registered product code |
| `quantity` | `qty` | **Yes** | |
| `unit_price` | `unitPrice`, `price` | **Yes** | Tax-inclusive |
| `tax_rate` | `taxRate` | **Yes** (tax invoices) | `18` standard · `0` zero-rated · `"-"` exempt. Receipts: leave out or `0`. |
| `unit_of_measure` | `unitOfMeasure`, `unit_code` | Send it | The product's registered unit |
| `goods_category_id` | `goodsCategoryId`, `commodity_code`, `commodityCategoryId` | No | The product's commodity category |
| `goods_category_name` | `goodsCategoryName` | No | |
| `discount` | | No | Tax-inclusive discount amount — see 6c |
| `package_scaled` | `packageScaledValue` | Piece-unit products | |
| `piece_scaled` | `pieceScaledValue` | Piece-unit products | |
| `piece_qty` | `pieceQty` | Piece-unit products and exports | |
| `piece_measure_unit` | `pieceMeasureUnit` | Exports | |
| `excise_*` | | Excise items | See 6d |
| `deemed_*`, `vat_project_*` | | Deemed items | See 6e |
| `total_weight`, `hs_code`, `hs_name` | camelCase forms | Exports | See 6f |

### 6c. Discounts

Add `discount` — the tax-inclusive amount — to the line:

```json
{
  "item_name": "Bottled water 500ml",
  "item_code": "WTR-500",
  "quantity": 5,
  "unit_price": 24000,
  "tax_rate": 18,
  "discount": 10000
}
```

The discount appears as its own line on the fiscalised document.

### 6d. Excise duty items

Add these to each line that carries excise:

```json
{
  "item_name": "Bell Lager 500ml",
  "item_code": "BELL-500",
  "quantity": 24,
  "unit_price": 4720,
  "tax_rate": 18,
  "unit_of_measure": "101",
  "excise_flag": "1",
  "category_id": "BEV001",
  "category_name": "Beer & Malt Beverages",
  "excise_rule": "2",
  "excise_rate": "650",
  "excise_tax": "7800",
  "excise_unit": "102",
  "excise_currency": "UGX",
  "package_scaled": "1",
  "piece_scaled": "500"
}
```

| Field | Also accepted as | Notes |
| --- | --- | --- |
| `excise_flag` | `exciseFlag` | `"1"` has excise, `"2"` none (default) |
| `excise_rule` | `exciseRule` | `"1"` percentage, `"2"` fixed amount per unit |
| `excise_rate` | `exciseRate` | Percentage: `10` means 10%. Fixed: the amount per unit, e.g. `650`. |
| `excise_tax` | `exciseTax` | The line's excise amount, as your system calculates it |
| `excise_unit` | `exciseUnit` | Fixed-amount only — see [appendix](#excise-units) |
| `excise_currency` | `exciseCurrency` | e.g. `UGX` |
| `category_id`, `category_name` | `categoryId`, `categoryName` | The excise category |

### 6e. Deemed VAT items

For businesses supplying under a deemed-VAT project (strategic investors,
aid-funded contractors, government MDAs and so on). Send `deemed_flag: "1"` on
each deemed line:

```json
{
  "format": "simple",
  "invoice_number": "INV-DEEMED-001",
  "invoice_date": "2026-09-25",
  "customer_name": "Ministry of Works",
  "customer_tin": "1000555444",
  "buyer_type": "3",
  "payment_method": "101",
  "items": [
    {
      "item_name": "Consulting services",
      "item_code": "SVC-CONSULT-01",
      "quantity": 1,
      "unit_price": 5000000,
      "tax_rate": 18,
      "goods_category_id": "73110000",
      "deemed_flag": "1",
      "deemed_exempt_code": "101",
      "vat_project_id": "893997229738400343",
      "vat_project_name": "Strategic Investor Project"
    }
  ]
}
```

| Field | Also accepted as | Required | Notes |
| --- | --- | --- | --- |
| `deemed_flag` | `deemedFlag` | Yes | `"1"` deemed, `"2"` not (default) |
| `deemed_exempt_code` | `deemedExemptCode` | When deemed | See [appendix](#deemed-exempt-codes) |
| `vat_project_id` | `vatProjectId` | **When deemed** | From URA's project registry |
| `vat_project_name` | `vatProjectName` | **When deemed** | |

### 6f. Export invoices

Send `invoice_industry_code` `102` (goods) or `112` (services):

```json
{
  "format": "simple",
  "invoice_number": "EXP-2026-001",
  "invoice_date": "2026-09-25",
  "customer_name": "Nairobi Traders Ltd",
  "buyer_type": "2",
  "payment_method": "107",
  "currency": "USD",
  "invoice_industry_code": "102",
  "delivery_terms_code": "FOB",
  "customs": {
    "sad_number": "SAD12345",
    "office": "Busia",
    "cif": "5000",
    "ware_house_number": "WH001",
    "ware_house_name": "Busia Warehouse",
    "destination_country": "Kenya",
    "origin_country": "Uganda",
    "import_export_flag": "2",
    "confirm_status": "0",
    "valuation_method": "CIF",
    "prn": "PRN123"
  },
  "items": [
    {
      "item_name": "Green coffee beans",
      "item_code": "COFFEE-001",
      "quantity": 500,
      "unit_price": 12000,
      "tax_rate": 0,
      "unit_of_measure": "103",
      "total_weight": 500,
      "piece_qty": 500,
      "piece_measure_unit": "103",
      "hs_code": "0901110000",
      "hs_name": "Coffee, not roasted, not decaffeinated"
    }
  ]
}
```

| Required | For |
| --- | --- |
| `delivery_terms_code` — `FOB`, `CIF`, `CFR`, `EXW`, `DAP`, `DDP`, `DPU`, `CIP`, `CPT`, `FAS`, `FCA` | `102` and `112` |
| `total_weight` (net kg), `piece_qty`, `piece_measure_unit` on **every line** | `102` and `112` |
| `customs` block | `102` |
| `hs_code`, `hs_name` | Optional |

**Customs block fields** — each also accepted in camelCase: `sad_number`,
`office`, `cif`, `ware_house_number`, `ware_house_name`, `destination_country`,
`origin_country`, `import_export_flag` (`1` import, `2` export — default),
`confirm_status` (default `0`), `valuation_method`, `prn`.

Exported goods are usually zero-rated: `tax_rate: 0`. Check an export's status
with `GET /export-invoice-status?invoiceNo={fdn}`.

### 6g. Sale types at a glance

| Sale | `invoice_industry_code` | Extra fields |
| --- | --- | --- |
| Normal sale | leave out | None |
| With discount | leave out | `discount` on the line |
| With excise | leave out | `excise_flag: "1"`, `excise_rule`, `excise_rate`, `excise_tax`, `category_id` |
| Deemed VAT | leave out | `deemed_flag: "1"`, `deemed_exempt_code`, `vat_project_id`, `vat_project_name` |
| Export goods | `102` | `delivery_terms_code`, `customs`, and per line `total_weight`, `piece_qty`, `piece_measure_unit` |
| Export service | `112` | `delivery_terms_code`, and per line `total_weight`, `piece_qty`, `piece_measure_unit` |

### 6h. The response

```json
{
  "success": true,
  "message": "The invoice was fiscalised.",
  "invoiceNumber": "INV-2026-000412",
  "fdn": "325043056477",
  "verificationCode": "234893273725405146366",
  "qrCode": "iVBORw0KGgo…",
  "fiscalisedAt": "2026-09-25T08:30:00.000Z",

  "seller": {
    "tin": "1000000001",
    "legalName": "Nakawa Traders Ltd",
    "tradeName": "Nakawa Traders Ltd",
    "address": "Plot 1, Kampala Road, Kampala",
    "referenceNumber": "INV-2026-000412",
    "servedBy": "Jane Kasule"
  },
  "fiscalData": {
    "documentType": "Original",
    "invoiceKind": "1",
    "fdn": "325043056477",
    "verificationCode": "234893273725405146366",
    "deviceNumber": "1000000001_02",
    "efrisInvoiceId": "8741236598741236",
    "issuedDate": "2026-09-25",
    "issuedTime": "11:30:00",
    "qrCode": "iVBORw0KGgo…"
  },
  "buyer": {
    "name": "Nakawa Traders Ltd",
    "tin": "1000999888",
    "buyerType": "0"
  },
  "items": [
    {
      "item": "Bottled water 500ml",
      "itemCode": "WTR-500",
      "qty": "12",
      "unitOfMeasure": "101",
      "unitPrice": "1500.00",
      "total": "18000.00",
      "taxRate": "0.18",
      "tax": "2745.76",
      "taxCategoryCode": "01",
      "discountFlag": "2",
      "orderNumber": "0"
    }
  ],
  "taxDetails": [
    {
      "taxCategoryCode": "01",
      "netAmount": "15254.24",
      "taxRate": "0.18",
      "taxAmount": "2745.76",
      "grossAmount": "18000.00",
      "taxRateName": "Standard Rate (18%)"
    }
  ],
  "summary": {
    "netAmount": 15254.24,
    "taxAmount": 2745.76,
    "grossAmount": 18000,
    "amountPayable": 18000,
    "amountInWords": "Eighteen thousand shillings only",
    "paymentMode": "Cash",
    "currency": "UGX",
    "itemCount": 1,
    "remarks": "Thank you for your business"
  }
}
```

**Store against the sale in your system:**

| Value | Why |
| --- | --- |
| `fiscalData.invoiceKind` | `"1"` tax invoice, `"2"` receipt — which to print |
| `invoiceNumber` | The number URA recorded. It can differ from yours if yours was already used — see [Reliability rules](#10-reliability-rules). |
| `fdn` | The fiscal document number. Lookups, credit notes and export queries use it. |
| `verificationCode` | Printed on the document |
| `qrCode` | Printed on the document |
| `fiscalData.efrisInvoiceId` | URA's id for the document; needed to cancel a credit note against it |
| The whole response | So the document can be reprinted |

---

## 7. Credit notes

A credit note reverses part or all of a fiscalised invoice or receipt. It is an
**application** to URA: a successful response means URA received it, not that it
is approved. Track it with the `referenceNo` in the response.

### `POST /submit-credit-note`

```json
{
  "credit_note_number": "CN-2026-001",
  "credit_note_date": "2026-09-25",
  "original_invoice_number": "325043056477",
  "original_fdn": "325043056477",
  "customer_name": "Nakawa Traders Ltd",
  "customer_tin": "1000999888",
  "buyer_type": "0",
  "reason": "Goods returned",
  "reason_code": "101",
  "items": [
    {
      "item_name": "Bottled water 500ml",
      "item_code": "WTR-500",
      "quantity": 2,
      "unit_price": 1500,
      "tax_rate": 18,
      "unit_of_measure": "101"
    }
  ]
}
```

- **Send positive quantities and amounts.**
- **Credit each item as it was sold:** the same item code, unit price and tax
  type as on the original invoice, and always send `original_fdn`. Against a tax
  invoice every item states its tax type; one without is refused.

**Response**

```json
{
  "success": true,
  "message": "The credit note application was submitted.",
  "referenceNo": "24092500001",
  "creditNoteNumber": "CN-2026-001",
  "amount": 3000,
  "itemCount": 1,
  "submittedAt": "2026-09-25T09:10:00.000Z",
  "efrisResponse": { "referenceNo": "24092500001" }
}
```

### Credit notes against receipts

A credit note against a receipt carries no VAT, as the receipt had none: leave
`tax_rate` out, or send `0`. One sent with VAT is refused:

```json
{
  "success": false,
  "error": "Bad request",
  "detail": "This credit note is against a receipt, and a receipt carries no VAT - so its credit note carries none either. VAT was charged on: Bottled water 500ml (18%). Remove the VAT from the credit note in your system (set the item to no VAT) and send it again, so your books match URA's."
}
```

### Credit note fields

| Field | Also accepted as | Required | Notes |
| --- | --- | --- | --- |
| `credit_note_number` | `creditNoteNumber` | **Yes** | Your credit note number |
| `original_invoice_number` | `originalInvoiceNumber` | **Yes** | |
| `original_fdn` | `originalFdn` | **Send it** | The original's fiscal document number |
| `customer_name` | `customerName` | **Yes** | |
| `reason` | | **Yes** | Free text |
| `reason_code` | `reasonCode` | Send it | See below |
| `items` | | **Yes** | |
| `credit_note_date` | `creditNoteDate` | No | `YYYY-MM-DD` |
| `customer_tin` | `customerTin` | For business buyers | |
| `buyer_type` | `buyerType` | No | `1` by default |
| `buyer_email` | `buyerEmail` | No | |
| `buyer_phone` | `buyerPhone` | No | |
| `currency` | | No | `UGX` by default |
| `remarks` | `remark` | No | Up to 500 characters. Sent as you give them; left empty if you give none |

**`reason_code`**

| Code | Meaning |
| --- | --- |
| `101` | Return of products (also expired or damaged) |
| `102` | Cancellation of purchase |
| `103` | Invoice amount wrongly stated |
| `104` | Partial or complete waive-off |
| `105` | Other — explain in `reason` |

### Credit note item fields

| Field | Also accepted as | Notes |
| --- | --- | --- |
| `item_name` | `itemName` | |
| `item_code` | `itemCode` | As on the original invoice |
| `quantity` | `qty` | Quantity credited |
| `unit_price` | `unitPrice` | As on the original invoice |
| `tax_rate` | `taxRate` | **Yes** against a tax invoice — as on the original. Against a receipt: leave out or `0`. |
| `unit_of_measure` | `unitOfMeasure` | |
| `package_scaled`, `piece_scaled` | camelCase forms | Piece-unit products |
| `excise_*`, `category_id`, `category_name` | camelCase forms | Only when the original line had excise; send the same values |

### After submitting

| To | Call |
| --- | --- |
| Find which invoices can be credited | `GET /creditable-invoices` |
| See what remains creditable on an invoice | `GET /invoice-remain-details?invoiceNo={fdn}` |
| Track the application | `GET /credit-notes?referenceNo={referenceNo}` |
| Cancel an issued credit note | `POST /cancel-credit-note` — below |

### Cancelling a credit note — `POST /cancel-credit-note`

```json
{
  "ori_invoice_id": "8741236598741236",
  "invoice_no": "325043056999",
  "reason_code": "103",
  "reason": "Credit note issued in error",
  "invoice_apply_category_code": "104"
}
```

| Field | Also accepted as | Required | Notes |
| --- | --- | --- | --- |
| `ori_invoice_id` | `oriInvoiceId` | **Yes** | `fiscalData.efrisInvoiceId` of the original invoice |
| `invoice_no` | `invoiceNo` | **Yes** | The credit note's fiscal document number |
| `reason_code` | `reasonCode` | **Yes** | `101` buyer refused it as incorrect, `102` not delivered as incorrect, `103` other |
| `reason` | | With code `103` | |
| `invoice_apply_category_code` | `invoiceApplyCategoryCode` | **Yes** | `104` to cancel a credit note |

---

## 8. Lookups

Each returns `{ "success": true, "result": …, "returnCode": "00" }`. `page` and
`pageNo` mean the same wherever there is paging.

| Purpose | Call | Parameters |
| --- | --- | --- |
| Check your connection | `GET /diagnostics` | — |
| Your registration details and branches | `GET /registration-details` | — |
| Branches (ids for stock transfers) | `GET /branches` | — |
| Units of measure, currencies, excise units | `GET /units-of-measure` | — |
| Payment methods — the codes for `payment_method` | `GET /payment-methods` | — |
| Commodity categories | `GET /commodity-categories` | `page`, `pageSize` |
| Excise duty codes and rates | `GET /excise-duty` | `name` or `code` |
| HS codes (exports) | `GET /hs-codes` | — |
| A buyer's details by TIN | `GET /taxpayer/{tin}` | — |
| Exchange rate | `GET /exchange-rate` | `currency` (required), `issueDate` |
| Your registered products | `GET /goods` | `goodsCode`, `goodsName`, `page`, `pageSize` |
| Negative stock setting | `GET /negative-stock-configuration` | — |
| One invoice | `GET /invoice/{fdn}` | — |
| Search invoices | `GET /invoices` | `page`, `pageSize`, `startDate`, `endDate`, `buyerLegalName`, `invoiceNo`, `invoiceKind` |

---

## 9. Printing the fiscal document

The `submit-invoice` and `submit-receipt` response has everything needed to
print, with no second call.

### Where each printed value comes from

| Printed as | Response field |
| --- | --- |
| Seller TIN | `seller.tin` |
| Legal name / trade name | `seller.legalName`, `seller.tradeName` (in capitals) |
| Seller address | `seller.address` |
| Seller reference | `seller.referenceNumber` |
| Served by | `seller.servedBy` |
| Document type | `fiscalData.documentType` |
| Tax invoice or receipt | `fiscalData.invoiceKind` — `"1"` tax invoice, `"2"` receipt (title it as a receipt, with no VAT) |
| **Fiscal document number** | `fdn` |
| Verification code | `verificationCode` |
| Device number | `fiscalData.deviceNumber` |
| Issued date and time | `fiscalData.issuedDate`, `fiscalData.issuedTime` |
| Buyer name / TIN | `buyer.name`, `buyer.tin` |
| Goods lines | `items[]` — `item`, `qty`, `unitPrice`, `total`, `taxCategoryCode` |
| Tax breakdown | `taxDetails[]` — `taxRateName`, `netAmount`, `taxAmount`, `grossAmount` |
| Net / tax / gross | `summary.netAmount`, `summary.taxAmount`, `summary.grossAmount` |
| Amount payable | `summary.amountPayable` |
| Amount in words | `summary.amountInWords` |
| Payment mode | `summary.paymentMode` |
| Remarks | `summary.remarks` |
| QR code | `qrCode` |

Discount lines appear in `items` with `discountFlag: "0"` and a negative `total`;
print them under the line they reduce.

### Tax categories

| `taxCategoryCode` | Letter | Printed as |
| --- | --- | --- |
| `01` | A | Standard (18%) |
| `02` | B | Zero rate (0%) |
| `03` | C | Exempt |
| `04` | D | Deemed (18%) |
| `05` | E | Excise duty |
| `11` | F | VAT not applicable (receipts) |

### Layout — the six URA sections

Print in this order, single column:

```
┌───────────────────────────────────────────┐
│  EFRIS     e-INVOICE / TAX INVOICE    1/1 │  header bar
├───────────────────────────────────────────┤
│ A  Seller details                         │
│    TIN · Legal name · Trade name          │
│    Address · Reference · Served by        │
├───────────────────────────────────────────┤
│ B  URA information                        │
│    Document type · FDN (prominent)        │
│    Verification code · Device number      │
│    Issued date · Issued time              │
├───────────────────────────────────────────┤
│ C  Buyer details                          │
│    Name · TIN (business / government)     │
├───────────────────────────────────────────┤
│ D  Goods and services                     │
│    No · Item · Qty · Unit price · Total · │
│    Tax category                           │
├───────────────────────────────────────────┤
│ E  Tax details                            │
│    Category · Net · Tax · Gross           │
├───────────────────────────────────────────┤
│ F  Summary                                │
│    Net · Tax · Gross (bold)               │
│    Amount in words · Payment mode         │
│    Remarks                                │
├───────────────────────────────────────────┤
│  *** END OF e-INVOICE / TAX INVOICE ***   │  footer bar
├───────────────────────────────────────────┤
│  [QR code]   Scan to verify with URA      │
└───────────────────────────────────────────┘
```

For a receipt, title the header and footer as a receipt.

### Suggested styling

| Element | Style |
| --- | --- |
| Header and footer bars | Blue (`#2563EB` → `#1D4ED8`), white bold text |
| Section headings | Light grey background (`#F3F4F6`), small bold text |
| FDN | Bold, blue (`#1D4ED8`), larger than surrounding text |
| Verification code | Monospace |
| Legal and trade name | Capitals |
| Gross amount | Bold |
| Amount in words | Italic, grey (`#6B7280`) |
| Dividers | 1px grey (`#D1D5DB`) |

### Example: reading the response in Dart / Flutter

```dart
Map<String, dynamic> _map(dynamic v) => (v as Map?)?.cast<String, dynamic>() ?? {};

final r         = response;                 // the submit-invoice JSON
final fiscal    = _map(r['fiscalData']);
final seller    = _map(r['seller']);
final summary   = _map(r['summary']);

final fdn              = r['fdn'] ?? fiscal['fdn'] ?? '';
final verificationCode = r['verificationCode'] ?? fiscal['verificationCode'] ?? '';
final qrCode           = r['qrCode'] ?? fiscal['qrCode'] ?? '';
final isReceipt        = fiscal['invoiceKind'] == '2';

final legalName = (seller['legalName'] ?? '').toString().toUpperCase();
final tradeName = (seller['tradeName'] ?? seller['legalName'] ?? '').toString().toUpperCase();
final reference = seller['referenceNumber'] ?? r['invoiceNumber'] ?? '';

final gross   = summary['grossAmount'] ?? 0;
final payable = summary['amountPayable'] ?? gross;
final words   = summary['amountInWords'] ?? '';

String categoryLabel(String code) => const {
      '01': 'A - Standard (18%)',
      '02': 'B - Zero rate (0%)',
      '03': 'C - Exempt',
      '04': 'D - Deemed (18%)',
      '05': 'E - Excise duty',
      '11': 'F - VAT not applicable',
    }[code] ??
    'Category $code';
```

### Reprinting

Store the full response against the sale in your own database, and reprint from
it. If the stored copy is lost, `GET /invoice/{fdn}` returns the document's
details.

---

## 10. Reliability rules

### Your invoice number is the sale — retrying is safe

Your `invoice_number` is sent to URA exactly as given and never changed, and a
number is fiscalised at most once. When a `submit-invoice` or `submit-receipt`
call times out, the connection drops or you get a `502`, **send it again with the
same number**:

- Already fiscalised: you get that same document back (`alreadyFiscalised: true`).
- Being fiscalised right now: `409`, send it again in a minute.
- Refused before, or never reached URA: it is fiscalised now.
- Fiscalised for a different amount: `409`, give this sale its own number.

**Never give a retry a new number** — URA would fiscalise the sale twice.

Queries (`GET`) are always safe to retry.

### Other rules

| Rule | Why |
| --- | --- |
| Register before selling | A sale of an unregistered item is refused. |
| Record stock before selling | Unless negative stock is allowed for your business. |
| Send piece fields for piece-unit products | Otherwise stock is counted wrongly. |
| Always send `payment_method` | Without it, the sale is recorded as credit. |
| Always send `unit_of_measure` | Use the product's registered unit. |
| Always send `"format": "simple"` | See 6a. |
| Match your documents to your VAT registration | VAT-registered: tax invoices, every item with a tax type. Not registered: receipts, with no VAT. |
| Watch `X-RateLimit-Remaining` | Slow down before your daily limit refuses you. |

---

## 11. Errors

Every error's `detail` says what is wrong and what to do; show it to your user.
The most common ones:

### Refused before reaching URA

These return `400`:

| `detail` starts with | Fix |
| --- | --- |
| `The request is missing required field…` | Add the named fields. |
| `A business-to-business invoice (buyer_type "0") requires customer_tin` | Send the buyer's TIN, or use another buyer type. |
| `This client (…) has no email address on record` | Your provider must add your business email. |
| `An export invoice requires delivery_terms_code` | Add the Incoterm. |
| `Export invoice line N (…) requires …` | Add the named field to that line. |
| `The product is missing required field…` | Add `item_code`, `item_name`, `unit_price` or `commodity_code`. |
| `excise_duty_code is required when have_excise_tax is "101"` | Add the code from `GET /excise-duty`. |
| `package_scaled_value and piece_scaled_value must both be greater than zero` | Fix the scale values. |
| `adjustType is required when reducing stock` | Add `adjustType`. |
| `Item N (…) has no unit of measure` | Send `measureUnit` for that item. |
| `remarks is required when adjustType is 104 (others)` | Say in `remarks` why the stock was written down. |
| `A stock decrease has no supplier and no stock-in type` | Remove the named fields. |
| `operationType … does not belong on the stock … route` | Send stock received to `/stock-increase` and stock written down to `/stock-decrease`. |
| `A transfer needs both sourceBranchId and destinationBranchId` | Add both, from `GET /branches`. |
| `transferTypeCode is required on a stock transfer` | Say why the stock is moving: `101`, `102` or `103`. |
| `The credit note is missing required field…` | Add the named fields. |
| `… is not registered for VAT, so URA accepts only receipts from it` | Issue the sale as a receipt, to `/submit-receipt`, with no VAT on the items. |
| `… is registered for VAT since …, so URA accepts only tax invoices from it` | Issue the sale as a tax invoice, to `/submit-invoice`. |
| `This business is registered for VAT, so each item on its tax invoice must state its VAT…` | Give each named item its tax type: `18`, `0` or `"-"`. |
| `A receipt carries no VAT … VAT was charged on: …` | Your business is not registered for VAT. Set the named items to no VAT and send the sale again. |
| `This credit note is against a tax invoice, so each item must state its VAT…` | Give each item the tax type it had on the invoice. |
| `This credit note is against a receipt … VAT was charged on: …` | Send the credit note without VAT, as the receipt was. |

### Refused by URA

These return `400` with `detail` starting `EFRIS error <code>:` and a `ura`
object. Common codes:

| Code | Meaning | Fix |
| --- | --- | --- |
| `45` | Some lines refused | Each line's own reason is listed in `detail`. |
| `602` | Item code already registered | Use `operationType: "102"` to update it, or a new code. |
| `676` | Fixed-amount excise product without piece units | Send `have_piece_unit: "101"` and the piece fields — see [Excise](#products-with-excise-duty). |
| `1115` | Seller email missing | Your provider must add your business email. |
| `1142` | Customer TIN missing | Send `customer_tin`, or sell to an individual. |
| `1462` | Credit note excise differs from the original | Send the same excise fields as the original line. |
| `1464` | Credit note line differs from the original | Credit each item as it was sold, and send `original_fdn`. |
| `2091` | Item out of stock | Record the stock received first. |
| `2122` | Item not registered | Register the product first. |
| `2240` | Receipt from a VAT-registered business | Send to `/submit-invoice`. |
| `2243` | Wrong unit of measure | Use the product's registered unit. |

### Other failures

| Symptom | What to do |
| --- | --- |
| `401` on every call | Check the API key; it may have been replaced or disabled. |
| `403` "licence has expired" | Contact your provider. |
| `403` "address is not permitted" | Your server's address is not allowed for this key; contact your provider. |
| `502` "Could not reach URA's EFRIS system…" | URA is down or busy. Retry queries later; for sales, see [Reliability rules](#10-reliability-rules). |
| `502` "URA accepted this, but its reply could not be read…" | The document or stock movement **is** recorded. Do not resend; look it up by its number. |
| `502` "URA refused to log this client in…" | A problem with your account at URA; `ura.code` says which. Contact your provider. |
| `500` with a setup message | Your account is not fully set up. Contact your provider. |
| `500` with a reference | Quote the reference to your provider. |

---

## Appendix — code tables

### Buyer types

| Code | Meaning | TIN |
| --- | --- | --- |
| `0` | Business (B2B) | Required |
| `1` | Individual (B2C) — default | — |
| `2` | Foreigner | — |
| `3` | Government (B2G) | Send it |

### Payment methods

| Code | Meaning |
| --- | --- |
| `101` | Credit — default |
| `102` | Cash |
| `103` | Cheque |
| `104` | Demand draft |
| `105` | Mobile money |
| `106` | Visa / Mastercard |
| `107` | EFT |
| `108` | POS |
| `109` | RTGS |
| `110` | SWIFT transfer |

The table is a guide. `GET /payment-methods` returns URA's current list, for your
system's payment dropdown:

```json
{
  "success": true,
  "result": [
    { "code": "101", "name": "Credit" },
    { "code": "102", "name": "Cash" }
  ],
  "returnCode": "00"
}
```

### Tax rates

| Send | Meaning | Category |
| --- | --- | --- |
| `18` | Standard VAT | `01` |
| `0` | Zero-rated | `02` |
| `"-"` | Exempt | `03` |
| `18` with `deemed_flag: "1"` | Deemed VAT | `04` |
| leave out, or `0` — receipts | No VAT (business not registered for VAT) | `11` |

On a tax invoice every item needs one of the first four; an item with none is
refused.

### Invoice industry codes

| Code | Meaning |
| --- | --- |
| `101` | General — default |
| `102` | Export goods |
| `112` | Export service |

### Excise units

| Code | Meaning |
| --- | --- |
| `101` | Per stick |
| `102` | Per litre |
| `103` | Per kg |
| `104` | Per user per day |
| `105` | Per minute |
| `106` | Per 1,000 sticks |
| `107` | Per 50 kg |

The full list is in `GET /units-of-measure`.

### Deemed exempt codes

| Code | Meaning |
| --- | --- |
| `101` | Strategic investor |
| `102` | Petroleum licensee |
| `103` | Aid-funded project contractor |
| `104` | Government MDA |
| `105` | VAT and excise duty exempt |
| `106` | Excise duty exempt |
| `107` | Mining licensee |
| `108` | EACOP licensee |
| `109` | EACOP level 1 contractor |

### Yes / no flags

| Where | Yes | No |
| --- | --- | --- |
| Product registration (`have_excise_tax`, `have_piece_unit`) | `101` | `102` |
| Invoice lines (`excise_flag`, `deemed_flag`) | `1` | `2` |
| Invoice line `discountFlag` (in responses) | `1` has a discount · `0` is the discount line · `2` none | |
