Transmit e-invoices to NRS with a single API call.
Doftwerks is a midpoint Access Point gateway. Integrate once, send your invoices in our schema, and we sign, report and transmit them to Nigeria's National Revenue Service and your customers.
One integration
Send our schema. We map and route to the recipient Access Point Provider.
Secure by default
Per-environment client credentials, hashed and scoped to your account.
Clear results
A unified response with a code, message and receipt status every time.
Overview
How it works
You authenticate with a client id and secret, POST an invoice in the NRS schema below, and receive a unified response telling you exactly what happened — including the IRN, qr code and receipt status.
- 1
Get credentials
Register, get approved, then generate sandbox & production keys from your dashboard.
- 2
Send the invoice
POST your invoice JSON to the transmit endpoint with your headers.
- 3
Read the result
Use the code and receipt_status to confirm reporting & transmission.
Before you start
NRS resources
Before you can transmit an e-invoice, several fields in the payload must carry official codes issued by NRS/FIRS — not free text. NRS publishes these as reference (lookup) resources that you fetch from their API, persist in your own system, and use to map your invoice data onto our payload.
| Resource | Used to fill |
|---|---|
| Invoice type | invoice_type_code (e.g. 381, 380, 384) |
| Payment means | payment method / settlement codes |
| Tax categories | tax_total[].tax_subtotal[].tax_category.id and invoice_line[].tax_category.id (e.g. STANDARD_VAT) |
| Currencies | document_currency_code, tax_currency_code (e.g. NGN) |
| Invoice Quantity Codes | invoice_line[].price.price_unit (e.g. H87) |
| Products Codes | invoice_line[].hsn_code + product_category (goods lines) |
| Service Codes | invoice_line[].isic_code + service_category (service lines) |
| Local governments | postal_address.lga (e.g. NG-AB-ANO) |
| State codes | postal_address.state (e.g. NG-LA) |
| Countries | postal_address.country (e.g. NG) |
Retrieve once, persist, and map. Each integrating system should pull these resources from the NRS API and store them (refreshing periodically). You then use them to translate your own data into valid codes — the correct state, lga and country for a postal address; the price_unit for a line quantity; the hsn_code / product_category for a goods line, or the isic_code / service_category for a service line; and the right tax_category. A value that isn't in these lists fails validation.
Browse the NRS resource endpointsSecurity
Authentication
Every request is authenticated with a client id / secret pair issued per environment. Your account must be approved before credentials can be generated, and the secret is shown only once — store it securely on your server.
Sandbox
cli_test_…
Safe to experiment — nothing is reported to FIRS.
Production
cli_live_…
Live transmission to the National Revenue Service.
Request
Request headers
Send the following headers with every request. Missing or malformed headers return a specific error code (see below).
| Header | Example | Description |
|---|---|---|
| Accept required | application/json | Tells the gateway to return a JSON response. |
| Content-Type required | application/json | The request body is JSON. A different value returns code 20. |
| x-client-id required | cli_live_xxxxxxxxxxxx | Your public client identifier. Generate it from your dashboard (sandbox or production). |
| x-client-secret required | •••••••••••••••• | Your secret key. Keep it server-side; never expose it in browsers or mobile apps. |
Reference
Base URL
Every e-invoice endpoint shares one base URL. Append the endpoint path — for example /transmit — to build the full URL.
Reference
Transmit endpoint
Send a POST request to the transmit endpoint (base URL + /transmit). Pick your language below to see a ready-to-use example.
curl -X POST https://einvoice.doftwerks.com/api/v1/einvoice/transmit \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-H "x-client-id: cli_live_xxxxxxxxxxxx" \
-H "x-client-secret: ••••••••••••••••" \
-H "x-invoice-format: nrs" \
-d @invoice.jsonRequest
Transmit payload
The complete NRS invoice schema. Send this object as the raw JSON body. Every field is explained in the field reference below.
{
"business_id": "2f6e9e21-5441-4f05-8889-aacd116f3fc1",
"invoice_number": "INV202600001",
"irn": "INV202600001-68415441-20260801",
"payment_status": "PENDING",
"invoice_kind": "B2B",
"issue_date": "2026-08-01",
"issue_time": "01:00:00",
"due_date": "2026-08-31",
"invoice_type_code": "381",
"tax_point_date": "2026-08-31",
"document_currency_code": "NGN",
"tax_currency_code": "NGN",
"accounting_cost": "Office Supplies",
"note": "Kindly take out the WHT and pay to the revenue service. Banking info: 1234567890 (Account Number), Test Bank (Bank Name).",
"buyer_reference": "SUP_08_0001-109288E5-20260801",
"payment_terms_note": "Net 30 days (30 days after the issue date)",
"order_reference": "ORD_12345-68415441-20260801",
"actual_delivery_date": "2026-08-16",
"invoice_delivery_period": {
"start_date": "2026-08-14",
"end_date": "2026-08-16"
},
"dispatch_document_reference": {
"irn": "DOC321-68415441-20260801",
"issue_date": "2026-08-01"
},
"receipt_document_reference": {
"irn": "DOC322-68415441-20260801",
"issue_date": "2026-08-01"
},
"originator_document_reference": {
"irn": "DOC323-68415441-20260801",
"issue_date": "2026-08-01"
},
"contract_document_reference": {
"irn": "DOC324-68415441-20260801",
"issue_date": "2026-08-01"
},
"additional_document_reference": [
{
"irn": "DOC325-68415441-20260801",
"issue_date": "2026-08-01"
},
{
"irn": "DOC326-68415441-20260801",
"issue_date": "2026-08-01"
}
],
"accounting_supplier_party": {
"party_name": "Ut mollitia porro",
"tin": "57161140-8935",
"email": "[email protected]",
"telephone": "+2348100000000",
"business_description": "Technology Firm",
"postal_address": {
"street_name": "8000 Marina Blvd, Suite 300",
"city_name": "Victoria Island",
"postal_zone": "100323",
"lga": "NG-AB-EOS",
"state": "NG-LA",
"country": "NG"
}
},
"accounting_customer_party": {
"party_name": "Colleen Diaz Plc",
"tin": "22701248-8449",
"email": "[email protected]",
"telephone": "+2349122944976",
"business_description": "Professional Services",
"postal_address": {
"street_name": "4557 De Silva St",
"city_name": "Fremont",
"postal_zone": "100538",
"lga": "NG-AB-ANO",
"state": "NG-AB",
"country": "NG"
}
},
"payment_means": [
{
"payment_means_code": "10",
"payment_due_date": "2026-08-10"
},
{
"payment_means_code": "30",
"payment_due_date": "2026-08-11"
}
],
"allowance_charge": [
{
"charge_indicator": true,
"amount": 22500
},
{
"charge_indicator": false,
"amount": 54500
}
],
"billing_reference": [
{
"irn": "INV202600031-68415441-20260131",
"issue_date": "2026-01-31"
},
{
"irn": "INV202600030-68415441-20260130",
"issue_date": "2026-01-30"
}
],
"tax_total": [
{
"tax_amount": 68850,
"tax_subtotal": [
{
"taxable_amount": 918000,
"tax_amount": 68850,
"tax_category": {
"id": "STANDARD_VAT",
"percent": 7.5
}
}
]
}
],
"legal_monetary_total": {
"line_extension_amount": 918000,
"tax_exclusive_amount": 918000,
"tax_inclusive_amount": 986850,
"payable_amount": 986850
},
"invoice_line": [
{
"invoiced_quantity": 10,
"line_extension_amount": 472500,
"item": {
"name": "Office Cabinet with Doors",
"description": "Cabinet with Doors",
"sellers_item_identification": "E-COM11"
},
"price": {
"price_amount": 50000,
"base_quantity": 1,
"price_unit": "H87"
},
"tax_category": [
{
"id": "STANDARD_VAT",
"percent": 7.5
}
],
"hsn_code": "7203.10",
"product_category": "Ferrous products; obtained by direct reduction of iron ore, in lumps, pellets or similar forms",
"isic_code": "",
"service_category": "",
"discount_rate": 10,
"discount_amount": 50000,
"fee_rate": 5,
"fee_amount": 22500
},
{
"invoiced_quantity": 100,
"line_extension_amount": 445500,
"item": {
"name": "Steel Pipes",
"description": "Hot water steel pipes for homes",
"sellers_item_identification": "687194817958-2"
},
"price": {
"price_amount": 4500,
"base_quantity": 1,
"price_unit": "H87"
},
"tax_category": [
{
"id": "STANDARD_VAT",
"percent": 7.5
}
],
"hsn_code": "7203.10",
"product_category": "Ferrous products; obtained by direct reduction of iron ore, in lumps, pellets or similar forms",
"isic_code": "",
"service_category": "",
"discount_rate": 1,
"discount_amount": 4500,
"fee_rate": 0,
"fee_amount": 0
}
]
}Reference
Transmit field reference
What every line in the payload means. The whole payload — including the nested party, line, tax and total objects — is validated up front, so a request with problems is rejected with a single 422 that lists every field to fix, before anything reaches the Access Point. Required fields are marked below.
Invoice header
rootTop-level fields that describe the invoice document.
| Field | Type | Description |
|---|---|---|
| business_idrequired | uuid | The unique identifier of the business issuing the invoice — a UUID assigned when the entity is enabled for e-invoicing on the NRS platform. It must match the business id on your account. |
| invoice_numberrequired | string | Your invoice ID — the human-readable invoice number from your own system (e.g. INV202600001). Required on every request; an omitted invoice_number is rejected with code 90. |
| irnconditional | string | Invoice Reference Number — the unique tracking number for this invoice, e.g. INV00XX-94ND90NR-20240611 (max 80 characters). Format is {{Invoice ID}}-{{Supplier Service ID}}-{{Date in YYYYMMDD}}. Required for Access Point clients; System Integrator clients have it generated server-side. A missing IRN returns code 99. |
| invoice_kindrequired | string | The kind of transaction being invoiced — B2B, B2C or B2G. B2C invoices may omit accounting_customer_party. |
| invoice_type_coderequired | string | NRS Invoice Type code describing the type of document, e.g. "381" (3 characters). Use "380" for a credit note, which also requires billing_reference. Fetch valid codes from the NRS resources. |
| issue_daterequired | date | The date the document was issued by the sender (YYYY-MM-DD). |
| issue_time | time | The time the document was issued, in 24-hour HH:MM:SS format, e.g. "17:59:04". |
| due_date | date | The date the invoice becomes due for payment (YYYY-MM-DD). |
| tax_point_date | date | The date the tax actually becomes applicable, when it differs from the issue date (YYYY-MM-DD). |
| payment_status | string | Whether the invoice is already settled. Only PENDING (the default) or PAID may be sent on a new invoice — use the update-status endpoint for PARTIAL or REJECTED. |
| document_currency_coderequired | string | The default currency the invoice is issued in, e.g. "NGN" (3 characters). Must be a valid NRS currency code in ISO 4217 format. |
| tax_currency_coderequired | string | The currency tax amounts are calculated and reported in, e.g. "NGN". Must be a valid NRS currency code in ISO 4217 format. |
| accounting_cost | string | The accounting category or cost centre the transaction is booked against, e.g. "Office Supplies" or "5000 NGN". |
| note | string | Free-form text about the invoice that is not carried by any other field, e.g. withholding-tax or bank instructions. Minimum 5 characters. |
| buyer_reference | string | A reference or code supplied by the buyer so they can track the invoice on their side. This should be in IRN format, e.g. "{{Buyer Reference}}-{{Buyer Service ID}}-{{Date in YYYYMMDD format}}". |
| order_reference | string | The purchase-order number this invoice relates to. This should be in IRN format, e.g. "{{Order Reference}}-{{Supplier Service ID}}-{{Date in YYYYMMDD format}}" |
| payment_terms_note | string | The terms and conditions of payment in plain words, e.g. "Net 30 days (30 days after the issue date)". |
| actual_delivery_date | date | The date the goods or services were actually delivered (YYYY-MM-DD). |
| invoice_delivery_period | object | The window over which goods or services were delivered — see Invoice delivery period below. |
| payment_means | object[] | How the invoice will be paid and when each amount is due — see Payment means below. |
| allowance_charge | object[] | Invoice-level discounts (allowances) and extra charges — see Allowance and charge below. |
| billing_reference | object[] | Links this document to earlier billing documents, such as the invoice a credit note corrects — see Billing reference below. |
| dispatch_document_reference | object | A reference to the despatch advice used to track the dispatch of the goods — see Document references below. |
| receipt_document_reference | object | A reference to the receipt advice associated with this invoice — see Document references below. |
| originator_document_reference | object | A reference to the originating document that initiated this invoice — see Document references below. |
| contract_document_reference | object | A reference to the contract governing the transaction — see Document references below. |
| additional_document_reference | object[] | A general-purpose list of any other related documents — see Document references below. |
| accounting_supplier_partyrequired | object | The seller or supplier issuing the invoice — see Supplier party below. |
| accounting_customer_partyconditional | object | The buyer receiving the goods or services. Required for B2B and B2G; may be omitted for B2C — see Customer party below. |
| payee_party | object | The party that actually receives the payment, when it is not the supplier — see Other parties below. |
| ship_party | object | The party the goods are shipped to, when it differs from the buyer — see Other parties below. |
| tax_representative_party | object | The tax agent handling invoicing and compliance for the supplier — see Other parties below. |
| tax_totalrequired | object[] | The total tax charged on the invoice, broken down by category — see Tax total below. |
| legal_monetary_totalrequired | object | The document totals the buyer has to pay, before and after tax — see Legal monetary total below. |
| invoice_linerequired | object[] | One entry per item or service invoiced — see Invoice lines below. |
Invoice delivery period
invoice_delivery_period (Optional)Optional. The start and end dates over which the goods or services were delivered. Both dates are required once the object is sent.
| Field | Type | Description |
|---|---|---|
| start_daterequired | date | First day of the delivery window (YYYY-MM-DD). |
| end_daterequired | date | Last day of the delivery window (YYYY-MM-DD). Must be the same as or later than start_date. |
Document references
dispatch · receipt · originator · contract · additional_document_reference (Optional)Optional. Every document reference shares the same two fields. dispatch, receipt, originator and contract each take a single object; additional_document_reference takes an array of them. Both fields are required once a reference is sent.
| Field | Type | Description |
|---|---|---|
| irnrequired | string | The IRN or document identifier of the referenced document. This should be in IRN format, e.g. "{{Document Reference}}-{{Supplier Service ID}}-{{Date in YYYYMMDD format}}" |
| issue_daterequired | date | The issue date of the referenced document (YYYY-MM-DD). |
Payment means
payment_means[] (Optional)Optional. How the invoice will be paid and when payment is due. Send one entry per payment method; both fields are required in each entry.
| Field | Type | Description |
|---|---|---|
| payment_means_coderequired | string | NRS Payment Means code describing the settlement method, e.g. "10" or "30". Fetch valid codes from the NRS resources. |
| payment_due_daterequired | date | The date this payment is due (YYYY-MM-DD). |
Allowance and charge
allowance_charge[] (Optional)Optional. Summarises the discounts and extra charges on the invoice. When sent, the amounts must reconcile with the lines: charge_indicator true must equal the total invoice_line fee_amount, and false must equal the total invoice_line discount_amount.
| Field | Type | Description |
|---|---|---|
| charge_indicatorrequired | boolean | true for a charge — the total of every line fee_amount. false for an allowance — the total of every line discount_amount. |
| amountrequired | number | The total amount for this indicator, to at most 2 decimal places. |
Supplier party
accounting_supplier_partyThe seller issuing the invoice. Always required.
| Field | Type | Description |
|---|---|---|
| party_namerequired | string | The registered legal name of the supplier business or individual. |
| tinrequired | string | The supplier Taxpayer Identification Number in NNNNNNNN-NNNN format or the new Tax ID format, e.g. TIN - "57161140-8935" (13 characters), Tax ID - "2511444152270" (13-digit numeric characters). |
| emailrequired | string | The supplier official email address for invoice and tax correspondence. |
| telephone | string | The supplier contact number, which must begin with a country code, e.g. "+2348100000000". |
| business_description | string | A brief description of the supplier line of business, e.g. "Technology Firm". Minimum 5 characters. |
| postal_addressrequired | object | The official business address — see Postal address below. |
Customer party
accounting_customer_party (Conditional)The buyer receiving the goods or services. Required for B2B and B2G invoices; a B2C invoice may omit the whole object. Once it is sent, every field below applies.
| Field | Type | Description |
|---|---|---|
| party_namerequired | string | The registered name of the buyer. |
| tinrequired | string | The buyer Taxpayer Identification Number in NNNNNNNN-NNNN format, or the new Tax ID format, e.g. TIN - "57161140-8935" (13 characters), Tax ID - "2511444152270" (13-digit numeric characters). |
| emailrequired | string | The buyer official email address. |
| telephone | string | The buyer contact number, which must begin with a country code, e.g. "+2349122944976". |
| business_description | string | A brief description of the buyer line of business. Minimum 5 characters. |
| postal_addressrequired | object | The buyer business address — see Postal address below. |
Other parties
payee_party · ship_party · tax_representative_party (Optional)All optional, and all share the party shape below. payee_party is the entity that receives the payment when it is not the supplier; ship_party is where the goods are delivered when that differs from the buyer; tax_representative_party is the tax agent handling compliance. Once any one of them is sent, the fields marked required below apply to it.
| Field | Type | Description |
|---|---|---|
| party_namerequired | string | The registered name of the party. |
| tinrequired | string | The party Taxpayer Identification Number or Tax ID, e.g. "89487982-0001". |
| emailrequired | string | The party email address. |
| telephone | string | The party contact number, which must begin with a country code. |
| business_description | string | A brief description of the party line of business. Minimum 5 characters. |
| postal_addressrequired | object | The party address — see Postal address below. |
Postal address
postal_addressUsed by every party object. All fields are required once the parent party is sent.
| Field | Type | Description |
|---|---|---|
| street_namerequired | string | The street line of the address. |
| city_namerequired | string | The city or town (up to 100 characters). |
| postal_zonerequired | string | The postal code of the location (up to 20 characters). Some countries don't use postal code instead use district code or regional code |
| lgarequired | string | The NRS local government code, e.g. "NG-AB-ANO". For other countries pass the proper county/district HASC (Hierarchical Administrative Subdivision Code) in that country or just pass the county/disctrict name |
| staterequired | string | The NRS state code, e.g. "NG-LA". For other countries pass the ISO 3166-2 subdivision code |
| countryrequired | string | The two-letter country code, e.g. "NG". Must be a valid ISO 3166-1 alpha-2 code. |
Invoice lines
invoice_line[]One entry per item or service invoiced. At least one line is required. Every monetary field here is re-computed on our side — see Calculation rules below the table.
| Field | Type | Description |
|---|---|---|
| invoiced_quantityrequired | number | The number of units sold on this line. Must be greater than zero. |
| line_extension_amountrequired | number | The net amount for this line before tax. Must equal (invoiced_quantity × price.price_amount) − discount_amount + fee_amount. |
| item.namerequired | string | The name of the item or service sold. Minimum 3 characters. |
| item.descriptionrequired | string | A longer description of the item or service. Minimum 5 characters. |
| item.sellers_item_identificationrequired | string | Your own SKU or item code for this product. Minimum 2 characters. |
| price.price_amountrequired | number | The cost of one unit of the item. |
| price.base_quantityrequired | number | The quantity the unit price refers to, normally 1. |
| price.price_unitrequired | string | NRS Invoice Quantity (unit of measure) code, e.g. "H87". Fetch valid codes from the NRS resources. |
| tax_category[].idrequired | string | The NRS tax category applied to this line, e.g. "STANDARD_VAT". Must be a valid NRS tax category id. |
| tax_category[].percentrequired | number | The rate for that category, e.g. 7.5. Must match the percent NRS publishes for the category id. |
| hsn_codeconditional | string | The HS product code in NNNN.NN form, e.g. "7203.10" (7 characters). Required for a GOODS line, paired with product_category. A service line sends this empty and uses isic_code instead. |
| product_categoryconditional | string | The product classification that matches the hsn_code, e.g. "Food and Beverages". Required for a GOODS line. |
| isic_codeconditional | string | The ISIC service code in NNNN form, e.g. "4100" (4 characters). Required for a SERVICE line, paired with service_category. A goods line sends this empty. |
| service_categoryconditional | string | The service classification that matches the isic_code, e.g. "Construction of buildings". Required for a SERVICE line. |
| discount_rate | number | The percentage discount applied to this line, e.g. 10 for 10%. Send it and we calculate discount_amount for you, or send both and we verify them. |
| discount_amount | number | The discount in currency for this line. If omitted it is calculated from discount_rate; if both are sent it must equal discount_rate % of (invoiced_quantity × price.price_amount). |
| fee_rate | number | The percentage of any additional fee such as a service or delivery charge, e.g. 5 for 5%. |
| fee_amount | number | The fee in currency for this line. If omitted it is calculated from fee_rate; if both are sent it must equal fee_rate % of the line amount AFTER the discount. |
Tax total
tax_total[]The total tax charged on the invoice, broken down per tax category. At least one entry is required, and the figures must reconcile with the invoice lines.
| Field | Type | Description |
|---|---|---|
| tax_amountrequired | number | The total tax for this entry. Must equal the sum of its own tax_subtotal amounts, and across all entries the total tax of every invoice line. |
| tax_subtotal[].taxable_amountrequired | number | The net amount the tax is calculated on — the total line_extension_amount of every line carrying this tax category. |
| tax_subtotal[].tax_amountrequired | number | The tax charged on that taxable amount. Must equal taxable_amount × tax_category.percent %. |
| tax_subtotal[].tax_category.idrequired | string | The NRS tax category, e.g. "STANDARD_VAT". Every category used on a line needs a matching subtotal here. |
| tax_subtotal[].tax_category.percentrequired | number | The rate applied for that category, e.g. 7.5. |
Legal monetary total
legal_monetary_totalThe document totals. All four are required and are re-computed from the invoice lines before the invoice is signed.
| Field | Type | Description |
|---|---|---|
| line_extension_amountrequired | number | The total of every invoice_line line_extension_amount. |
| tax_exclusive_amountrequired | number | The invoice total before tax — the same figure as line_extension_amount. |
| tax_inclusive_amountrequired | number | The invoice total including tax — tax_exclusive_amount plus the total tax of all lines. |
| payable_amountrequired | number | The final amount the buyer must pay — the same figure as tax_inclusive_amount. |
Billing reference
billing_reference[] (Optional)Optional. Required only when invoice_type_code is "380" (Credit Note) — it links this document to the original invoice(s) being corrected. Provide one entry per referenced invoice.
| Field | Type | Description |
|---|---|---|
| irnrequired | string | IRN of the original invoice this credit note references. |
| issue_daterequired | date | Issue date of that original invoice (YYYY-MM-DD). |
Calculation rules
Every monetary figure is re-computed before the invoice is signed. If one does not reconcile, the request is rejected with a calculation error (codes 100–131) naming the field, the value you sent and the value we expected. Rates are percentages, and the checks run in order — lines, then the document totals, then tax, then allowance and charge — so you always get one group of problems at a time.
- discount_amount = discount_rate % × (invoiced_quantity × price_amount)
- fee_amount = fee_rate % × (invoiced_quantity × price_amount − discount_amount)
- line_extension_amount = (invoiced_quantity × price_amount) − discount_amount + fee_amount
- legal_monetary_total.line_extension_amount = tax_exclusive_amount = total of every line_extension_amount
- tax_inclusive_amount = payable_amount = tax_exclusive_amount + total line tax
- tax_subtotal.taxable_amount = total line_extension_amount per tax category
- tax_subtotal.tax_amount = taxable_amount × tax_category.percent %
- allowance_charge true = total fee_amount · false = total discount_amount
Response
Transmit responses
Every response shares the same envelope — a numeric code, a status, a human-readable message, your transmission reference, and a data object.
{
"code": 0,
"status": "success",
"message": "Invoice successfully transmitted",
"reference": "019e83c7-f554-72dd-a671-a1fcad002b4d",
"data": {
"irn": "INV202600001-68415441-20260801",
"qr_code": "iVBORw0KGgoAAAANSUhEUgAA…",
"receipt_status": 4,
"is_duplicate": false
}
}{
"code": 99,
"status": "error",
"message": "The irn (Invoice Reference Number) field is required.",
"reference": null,
"data": {
"errors": {
"irn": [
"The irn field is required."
]
}
}
}{
"code": 40,
"status": "error",
"message": "A goods line needs an hsn_code (HS/product code). A goods line needs a product_category. The accounting_customer_party.tin field is required.",
"reference": null,
"data": {
"errors": {
"invoice_line.0.hsn_code": [
"A goods line needs an hsn_code (HS/product code)."
],
"invoice_line.0.product_category": [
"A goods line needs a product_category."
],
"accounting_customer_party.tin": [
"The accounting_customer_party.tin field is required."
]
}
}
}{
"code": 120,
"status": "error",
"message": "tax_total.0.tax_amount is 6885 but its tax_subtotal entries add up to 68850. tax_total declares 6885 but the total tax of all invoice lines is 68850.",
"reference": null,
"data": {
"errors": {
"tax_total.0.tax_amount": {
"code": 120,
"field": "tax_total.0.tax_amount",
"message": "tax_total.0.tax_amount is 6885 but its tax_subtotal entries add up to 68850.",
"expected": 68850,
"provided": 6885
},
"tax_total": {
"code": 120,
"field": "tax_total",
"message": "tax_total declares 6885 but the total tax of all invoice lines is 68850.",
"expected": 68850,
"provided": 6885
}
}
}
}Reference
Error codes
The code field tells you precisely what happened. Always branch on code, not the HTTP status alone.
| Code | HTTP | Meaning |
|---|---|---|
| 0 | 200 | Success The invoice was accepted and forwarded to the Access Point. Inspect data.receipt_status for the downstream stage. |
| 10 | 401 | Missing credentials The x-client-id and/or x-client-secret header was not provided. |
| 11 | 401 | Invalid credentials The client id/secret pair did not match an active client, or the client is disabled. |
| 12 | 401 | Account not approved Your company account is pending, suspended or rejected. Transmission is blocked until an administrator approves it. |
| 13 | 401 | Account not found No company account is linked to these credentials. |
| 14 | 401 | Crypto keys not found NRS public key and certificate not setup for this account. |
| 15 | 403 | Production disabled Production API access is disabled for this account because production billing has not been activated. |
| 16 | 429 | Too many requests You have exceeded the per-minute request limit for this endpoint. Wait the number of seconds in data.retry_after (and the Retry-After header) before retrying. |
| 17 | 401 | Business and Service ID maybe missing NRS Business ID and Service ID may not have been provided for this account. |
| 18 | 401 | Business ID mismatch Business ID in request payload not the same as the provided resolved client business id. |
| 19 | 401 | Tax ID mismatch Tax ID in request payload not the same as the provided resolved client TIN. |
| 20 | 415 | Unsupported content type The Content-Type header must be application/json. |
| 21 | 400 | Empty body No request body was sent. |
| 22 | 400 | Invalid JSON The request body could not be parsed as JSON. |
| 23 | 404 | IRN record not found The provided IRN record not found, please transmit. |
| 24 | 401 | Service ID mismatch IRN is badly formed. Service ID in iRN format is not the same as the provided resolved client service id. |
| 25 | 400 | Update invoice status failed Update invoice status failed on NRS server |
| 26 | 404 | APP not set on NRS portal We are not set as your Access Point Provider on the NRS portal, update your APP and retry. |
| 27 | 404 | E-invoicing not enabled on NRS portal Please confirm your NRS e-invoicing is enabled, and that we are set as your Access Point Provider on the NRS portal. |
| 28 | 422 | Acknowledge transmission failed Acknowledge Transmission failed, try again or contact APP support. |
| 30 | 422 | Invalid format header The x-invoice-format header must be "nrs". |
| 40 | 422 | Validation failed One or more required fields are missing or invalid. See data.errors for the exact fields. |
| 50 | 422 | Rejected by Access Point The downstream Access Point (e.g. FIRS) rejected the invoice. See data for the provider message. |
| 51 | 502 | Transmission failed An unexpected error occurred while forwarding the invoice to the Access Point. |
| 52 | 504 | Access Point unreachable The downstream Access Point did not respond in time. |
| 53 | 501 | Network connection error We are unable to reach the NRS network at this time. Try again later. If the problem persists, contact support. |
| 54 | 500 | Provider not configured Your account has not been configured with Access Point credentials. Contact support. |
| 55 | 501 | Network connection error Our network is currently down due to maintenance or technical issues. Try again later. If the problem persists, contact support. |
| 56 | 200 | Signed but not transmitted (APP Error) Invoice is Signed but could not transmit. Accounting parties APP might be offline or busy, Try again later. If the problem persists, contact support. |
| 90 | 422 | Invoice number not provided The required invoice number field is missing from the payload. |
| 91 | 422 | IRN not provided The required irn (Invoice Reference Number) field is missing from the payload. |
| 92 | 400 | Invalid IRN format The provided irn format is invalid. Check the irn format in the documentation. |
| 93 | 422 | Irn confirmation failed The irn confirmation failed, try again or check irn format. |
| 94 | 400 | Invalid business ID The provided business ID format is invalid. Check the business ID format in the documentation. |
| 95 | 422 | Invoice signing failed The invoice signing failed, might be due to a duplicate signing, try again or contact APP support. |
| 96 | 422 | Invoice transmission failed The invoice transmission failed, might be due to a duplicate request or network issues, try again or contact support. |
| 100 | 422 | Invoice line basis invalid An invoice line is missing a numeric invoiced_quantity or price.price_amount, so its amounts cannot be calculated. |
| 101 | 422 | Line discount amount invalid invoice_line[].discount_amount does not match discount_rate % of (invoiced_quantity x price.price_amount). |
| 102 | 422 | Line fee amount invalid invoice_line[].fee_amount does not match fee_rate % of the line amount after discount. |
| 103 | 422 | Line extension amount invalid invoice_line[].line_extension_amount must equal (invoiced_quantity x price.price_amount) - discount_amount + fee_amount. |
| 104 | 422 | Line tax percent invalid An invoice line tax_category is missing a numeric percent, so the line tax cannot be calculated. |
| 105 | 422 | Line tax category invalid An invoice line tax_category is missing or invalid, so the line tax cannot be calculated. |
| 110 | 422 | Total line extension amount invalid legal_monetary_total.line_extension_amount must equal the sum of every invoice_line line_extension_amount. |
| 111 | 422 | Tax exclusive amount invalid legal_monetary_total.tax_exclusive_amount must equal the sum of every invoice_line line_extension_amount. |
| 112 | 422 | Tax inclusive amount invalid legal_monetary_total.tax_inclusive_amount must equal tax_exclusive_amount plus the total line tax. |
| 113 | 422 | Payable amount invalid legal_monetary_total.payable_amount must equal legal_monetary_total.tax_inclusive_amount. |
| 120 | 422 | Tax total invalid tax_total[].tax_amount must equal the tax of all invoice lines, and each entry must equal the sum of its own tax_subtotal amounts. |
| 121 | 422 | Tax subtotal taxable amount invalid tax_subtotal[].taxable_amount must equal the total line_extension_amount of every invoice line carrying that tax category. |
| 122 | 422 | Tax subtotal tax amount invalid tax_subtotal[].tax_amount must equal taxable_amount x tax_category.percent %. |
| 123 | 422 | Tax subtotal category mismatch The tax categories declared in tax_subtotal do not match the tax categories used on the invoice lines, or is not a valid NRS tax category id. |
| 124 | 422 | Tax subtotal category percent invalid The tax categories percent declared in tax_subtotal do not match NRS valid tax category percent |
| 125 | 422 | Tax subtotal invalid The tax_total.tax_subtotal array is missing or invalid. |
| 130 | 422 | Allowance charge fee total invalid allowance_charge with charge_indicator true must equal the total fee_amount of all invoice lines. |
| 131 | 422 | Allowance charge discount total invalid allowance_charge with charge_indicator false must equal the total discount_amount of all invoice lines. |
Reference
Receipt statuses
On success, data.receipt_status reports how far the invoice has progressed through the Access Point pipeline.
Initiated
Invoice transmission initiated, not yet signed or transmitted.
Signed
Invoice signed, but not yet transmitted to NRS.
Transmitting
Invoice is been transmitted, waiting for confirmation.
Transmitted
Invoice transmitted to NRS successfully.
Reference
Update payment status
After an invoice is transmitted you can update its recorded settlement status — for example when a pending invoice is paid. The IRN goes in the URL; send the new status in the body. When you mark it partial, also include the amount paid.
curl -X PATCH https://einvoice.doftwerks.com/api/v1/einvoice/update-status/{irn} \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-H "x-client-id: cli_live_xxxxxxxxxxxx" \
-H "x-client-secret: ••••••••••••••••" \
-d '{ "payment_status": "PAID" }'| Field | Type | Description |
|---|---|---|
| payment_statusrequired | string | New settlement state of the invoice: pending, partial, paid, or rejected (case-insensitive). |
| amount | number | Amount paid. Optional in general, but required when payment_status is partial — it records how much of the invoice has been settled. |
| reason | string | Optional note explaining the change (e.g. why an invoice was rejected). |
{
"payment_status": "PAID"
}{
"code": 0,
"status": "success",
"message": "Invoice status updated successfully",
"reference": "019e83c7-f554-72dd-a671-a1fcad002b4d",
"data": {
"irn": "INV202600001-68415441-20260101",
"payment_status": "paid",
"transmit_status": "transmitted"
}
}Reference
Report VAT (post-payment)
Report an invoice to NRS for financial (VAT) actions after a payment is received — partial or full. The supplier TIN (agent_tin) and our Access Point service id (integrator_service_id) are added automatically, so you don't send them.
curl -X POST https://einvoice.doftwerks.com/api/v1/einvoice/report \
-H "Accept: application/json" \
-H "Content-Type: application/json" \
-H "x-client-id: cli_live_xxxxxxxxxxxx" \
-H "x-client-secret: ••••••••••••••••" \
-d @report.json| Field | Type | Description |
|---|---|---|
| irnrequired | string | Invoice Reference Number of the transmitted invoice being reported. |
| beneficiary_tinrequired | string | Accounting Buyer Party TIN (the customer). |
| currencyrequired | string | Document currency code in ISO 4217 format, e.g. NGN. |
| transaction_daterequired | date | Invoice issue date (YYYY-MM-DD). |
| base_amountrequired | string | Line extension amount — the amount to be taxed. |
| total_amountrequired | string | Payable amount — the amount to be collected. |
| vat_calculatedrequired | string | Tax amount for the tax category (STANDARD_VAT, ZERO_VAT or REDUCED_VAT). |
| vat_raterequired | string | Percentage attached to the tax category, e.g. 7.5. |
| vat_statusrequired | string | The VAT tax category ID, e.g. STANDARD_VAT. |
| other_taxes | string | Sum of non-VAT tax amounts. |
| item_description | string | Item description within the invoice line. |
{
"irn": "INV001-E9E0C0D3-20240619",
"beneficiary_tin": "29334238-0001",
"currency": "NGN",
"transaction_date": "2024-11-18",
"base_amount": "100000",
"total_amount": "112500",
"vat_calculated": "7500",
"vat_rate": "7.5",
"vat_status": "STANDARD_VAT",
"other_taxes": "5000",
"item_description": "Items"
}{
"code": 0,
"status": "success",
"message": "VAT reported successfully",
"reference": "INV001-E9E0C0D3-20240619",
"data": {
"ok": true,
"irn": "INV3030-09012ED1ES-20250912"
}
}Reference
Acknowledge a transmission
Acknowledging is you confirming that you have received a transmitted invoice. When you call this, we tell NRS that the received transmission has been received and acknowledged on your behalf. The IRN of the invoice goes in the URL — there is no request body.
You can turn on Auto-acknowledge in the portal (Settings → Auto-acknowledge) and we will acknowledge every transmitted invoice for you automatically. Leave it off to acknowledge yourself by calling this endpoint each time you receive a transmitted-invoice webhook.
curl -X POST https://einvoice.doftwerks.com/api/v1/einvoice/acknowledge/{irn} \
-H "Accept: application/json" \
-H "x-client-id: cli_live_xxxxxxxxxxxx" \
-H "x-client-secret: ••••••••••••••••"{
"code": 0,
"status": "success",
"message": "Transmission acknowledged successfully",
"reference": "INV202600003-4D9B5ECF-20260101",
"data": []
}Reference
Webhooks
Every business must provide a webhook URL that accepts POST requests. Whenever a transmitted invoice changes status, we POST a JSON event to that URL so your systems stay in sync without polling.
Respond with 200
Your endpoint must return an HTTP 200 success code to acknowledge receipt. Anything else is treated as a failed delivery and may be retried.
No body or query params
We don't read the response body and never send query parameters — only the JSON payload below in the request body and the 200 status code matter.
You configure your webhook URL from your portal Settings — separately for the sandbox and production environments. Here is a sample of the event we send:
{
"eventType": "TransmissionStatusEvent",
"irn": "INV202500015-4D9B5ECF-20251230",
"tenant": "abc-cde-fgh-ijk",
"status": "Transmitting",
"environment": "sandbox",
"timestamp": "2026-06-22T08:57:40+00:00",
"invoice": {
"irn": "INV202500015-4D9B5ECF-20251230",
"due_date": "2026-01-31",
"tax_total": [
{
"tax_amount": 10.5,
"tax_subtotal": [
{
"tax_amount": 10.5,
"tax_category": {
"id": "STANDARD_VAT",
"percent": 7.5
},
"taxable_amount": 140
}
]
}
],
"issue_date": "2025-12-30",
"issue_time": "00:00:00",
"business_id": "cb02f028-5ecf-4107-9c40-c1c6b6e64000",
"invoice_kind": "B2B",
"invoice_line": [
{
"item": {
"name": "[E-COM11] Cabinet with Doors",
"description": "[E-COM11] Cabinet with Doors",
"sellers_item_identification": "E-COM11"
},
"price": {
"price_unit": "EA",
"price_amount": 140,
"base_quantity": 1
},
"fee_rate": 0,
"hsn_code": "0101.30",
"isic_code": null,
"fee_amount": 0,
"discount_rate": 0,
"discount_amount": 0,
"product_category": "Office",
"service_category": null,
"invoiced_quantity": 1,
"line_extension_amount": 140,
"tax_category": [
{
"id": "STANDARD_VAT",
"percent": 7.5
}
]
}
],
"payment_status": "PENDING",
"tax_point_date": "2025-12-30",
"invoice_type_code": "381",
"tax_currency_code": "NGN",
"payment_terms_note": "End of Following Month",
"legal_monetary_total": {
"payable_amount": 150.5,
"tax_exclusive_amount": 140,
"tax_inclusive_amount": 150.5,
"line_extension_amount": 140
},
"document_currency_code": "NGN",
"accounting_customer_party": {
"tin": "68452114-4234",
"email": "[email protected]",
"telephone": "+2555958393",
"party_name": "Colleen Diaz",
"postal_address": {
"lga": "NG-AB-ANO",
"state": "NG-AB",
"country": "NG",
"city_name": "Fremont",
"postal_zone": "94538",
"street_name": "4557 De Silva St"
},
"business_description": "This is a sample info"
},
"accounting_supplier_party": {
"tin": "18221471-0001",
"email": "[email protected]",
"telephone": "+2349122944976",
"party_name": "John & sons",
"postal_address": {
"lga": "NG-AB-ANO",
"state": "NG-AB",
"country": "NG",
"city_name": "Island",
"postal_zone": "100323",
"street_name": "8000 Marina Blvd, Suite 300"
},
"business_description": "Here is a simple test"
}
}
}Ready to transmit your first invoice?
Create an account, get approved, and generate sandbox credentials in minutes.