NRS · FIRS e-Invoicing

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. 1

    Get credentials

    Register, get approved, then generate sandbox & production keys from your dashboard.

  2. 2

    Send the invoice

    POST your invoice JSON to the transmit endpoint with your headers.

  3. 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.

ResourceUsed to fill
Invoice typeinvoice_type_code (e.g. 381, 380, 384)
Payment meanspayment method / settlement codes
Tax categoriestax_total[].tax_subtotal[].tax_category.id and invoice_line[].tax_category.id (e.g. STANDARD_VAT)
Currenciesdocument_currency_code, tax_currency_code (e.g. NGN)
Invoice Quantity Codesinvoice_line[].price.price_unit (e.g. H87)
Products Codesinvoice_line[].hsn_code + product_category (goods lines)
Service Codesinvoice_line[].isic_code + service_category (service lines)
Local governmentspostal_address.lga (e.g. NG-AB-ANO)
State codespostal_address.state (e.g. NG-LA)
Countriespostal_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 endpoints

Security

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.

Keep secret server-sideNever embed your secret in a browser, mobile app or public repository.

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).

HeaderExampleDescription
Accept
required
application/jsonTells the gateway to return a JSON response.
Content-Type
required
application/jsonThe request body is JSON. A different value returns code 20.
x-client-id
required
cli_live_xxxxxxxxxxxxYour 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.

BASEhttps://einvoice.doftwerks.com/api/v1/einvoice

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.

POST/transmit
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.json

Request

Transmit payload

The complete NRS invoice schema. Send this object as the raw JSON body. Every field is explained in the field reference below.

POST /transmit · body
{
  "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

root

Top-level fields that describe the invoice document.

FieldDescription
business_idrequiredThe 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_numberrequiredYour 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.
irnconditionalInvoice 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_kindrequiredThe kind of transaction being invoiced — B2B, B2C or B2G. B2C invoices may omit accounting_customer_party.
invoice_type_coderequiredNRS 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_daterequiredThe date the document was issued by the sender (YYYY-MM-DD).
issue_timeThe time the document was issued, in 24-hour HH:MM:SS format, e.g. "17:59:04".
due_dateThe date the invoice becomes due for payment (YYYY-MM-DD).
tax_point_dateThe date the tax actually becomes applicable, when it differs from the issue date (YYYY-MM-DD).
payment_statusWhether 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_coderequiredThe 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_coderequiredThe currency tax amounts are calculated and reported in, e.g. "NGN". Must be a valid NRS currency code in ISO 4217 format.
accounting_costThe accounting category or cost centre the transaction is booked against, e.g. "Office Supplies" or "5000 NGN".
noteFree-form text about the invoice that is not carried by any other field, e.g. withholding-tax or bank instructions. Minimum 5 characters.
buyer_referenceA 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_referenceThe 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_noteThe terms and conditions of payment in plain words, e.g. "Net 30 days (30 days after the issue date)".
actual_delivery_dateThe date the goods or services were actually delivered (YYYY-MM-DD).
invoice_delivery_periodThe window over which goods or services were delivered — see Invoice delivery period below.
payment_meansHow the invoice will be paid and when each amount is due — see Payment means below.
allowance_chargeInvoice-level discounts (allowances) and extra charges — see Allowance and charge below.
billing_referenceLinks this document to earlier billing documents, such as the invoice a credit note corrects — see Billing reference below.
dispatch_document_referenceA reference to the despatch advice used to track the dispatch of the goods — see Document references below.
receipt_document_referenceA reference to the receipt advice associated with this invoice — see Document references below.
originator_document_referenceA reference to the originating document that initiated this invoice — see Document references below.
contract_document_referenceA reference to the contract governing the transaction — see Document references below.
additional_document_referenceA general-purpose list of any other related documents — see Document references below.
accounting_supplier_partyrequiredThe seller or supplier issuing the invoice — see Supplier party below.
accounting_customer_partyconditionalThe buyer receiving the goods or services. Required for B2B and B2G; may be omitted for B2C — see Customer party below.
payee_partyThe party that actually receives the payment, when it is not the supplier — see Other parties below.
ship_partyThe party the goods are shipped to, when it differs from the buyer — see Other parties below.
tax_representative_partyThe tax agent handling invoicing and compliance for the supplier — see Other parties below.
tax_totalrequiredThe total tax charged on the invoice, broken down by category — see Tax total below.
legal_monetary_totalrequiredThe document totals the buyer has to pay, before and after tax — see Legal monetary total below.
invoice_linerequiredOne 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.

FieldDescription
start_daterequiredFirst day of the delivery window (YYYY-MM-DD).
end_daterequiredLast 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.

FieldDescription
irnrequiredThe 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_daterequiredThe 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.

FieldDescription
payment_means_coderequiredNRS Payment Means code describing the settlement method, e.g. "10" or "30". Fetch valid codes from the NRS resources.
payment_due_daterequiredThe 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.

FieldDescription
charge_indicatorrequiredtrue for a charge — the total of every line fee_amount. false for an allowance — the total of every line discount_amount.
amountrequiredThe total amount for this indicator, to at most 2 decimal places.

Supplier party

accounting_supplier_party

The seller issuing the invoice. Always required.

FieldDescription
party_namerequiredThe registered legal name of the supplier business or individual.
tinrequiredThe 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).
emailrequiredThe supplier official email address for invoice and tax correspondence.
telephoneThe supplier contact number, which must begin with a country code, e.g. "+2348100000000".
business_descriptionA brief description of the supplier line of business, e.g. "Technology Firm". Minimum 5 characters.
postal_addressrequiredThe 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.

FieldDescription
party_namerequiredThe registered name of the buyer.
tinrequiredThe 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).
emailrequiredThe buyer official email address.
telephoneThe buyer contact number, which must begin with a country code, e.g. "+2349122944976".
business_descriptionA brief description of the buyer line of business. Minimum 5 characters.
postal_addressrequiredThe 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.

FieldDescription
party_namerequiredThe registered name of the party.
tinrequiredThe party Taxpayer Identification Number or Tax ID, e.g. "89487982-0001".
emailrequiredThe party email address.
telephoneThe party contact number, which must begin with a country code.
business_descriptionA brief description of the party line of business. Minimum 5 characters.
postal_addressrequiredThe party address — see Postal address below.

Postal address

postal_address

Used by every party object. All fields are required once the parent party is sent.

FieldDescription
street_namerequiredThe street line of the address.
city_namerequiredThe city or town (up to 100 characters).
postal_zonerequiredThe postal code of the location (up to 20 characters). Some countries don't use postal code instead use district code or regional code
lgarequiredThe 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
staterequiredThe NRS state code, e.g. "NG-LA". For other countries pass the ISO 3166-2 subdivision code
countryrequiredThe 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.

FieldDescription
invoiced_quantityrequiredThe number of units sold on this line. Must be greater than zero.
line_extension_amountrequiredThe net amount for this line before tax. Must equal (invoiced_quantity × price.price_amount) − discount_amount + fee_amount.
item.namerequiredThe name of the item or service sold. Minimum 3 characters.
item.descriptionrequiredA longer description of the item or service. Minimum 5 characters.
item.sellers_item_identificationrequiredYour own SKU or item code for this product. Minimum 2 characters.
price.price_amountrequiredThe cost of one unit of the item.
price.base_quantityrequiredThe quantity the unit price refers to, normally 1.
price.price_unitrequiredNRS Invoice Quantity (unit of measure) code, e.g. "H87". Fetch valid codes from the NRS resources.
tax_category[].idrequiredThe NRS tax category applied to this line, e.g. "STANDARD_VAT". Must be a valid NRS tax category id.
tax_category[].percentrequiredThe rate for that category, e.g. 7.5. Must match the percent NRS publishes for the category id.
hsn_codeconditionalThe 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_categoryconditionalThe product classification that matches the hsn_code, e.g. "Food and Beverages". Required for a GOODS line.
isic_codeconditionalThe 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_categoryconditionalThe service classification that matches the isic_code, e.g. "Construction of buildings". Required for a SERVICE line.
discount_rateThe 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_amountThe 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_rateThe percentage of any additional fee such as a service or delivery charge, e.g. 5 for 5%.
fee_amountThe 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.

FieldDescription
tax_amountrequiredThe 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_amountrequiredThe net amount the tax is calculated on — the total line_extension_amount of every line carrying this tax category.
tax_subtotal[].tax_amountrequiredThe tax charged on that taxable amount. Must equal taxable_amount × tax_category.percent %.
tax_subtotal[].tax_category.idrequiredThe NRS tax category, e.g. "STANDARD_VAT". Every category used on a line needs a matching subtotal here.
tax_subtotal[].tax_category.percentrequiredThe rate applied for that category, e.g. 7.5.

Legal monetary total

legal_monetary_total

The document totals. All four are required and are re-computed from the invoice lines before the invoice is signed.

FieldDescription
line_extension_amountrequiredThe total of every invoice_line line_extension_amount.
tax_exclusive_amountrequiredThe invoice total before tax — the same figure as line_extension_amount.
tax_inclusive_amountrequiredThe invoice total including tax — tax_exclusive_amount plus the total tax of all lines.
payable_amountrequiredThe 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.

FieldDescription
irnrequiredIRN of the original invoice this credit note references.
issue_daterequiredIssue 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.

200 · SuccessThe invoice was accepted and forwarded. Check data.receipt_status.
200 OK
{
  "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
  }
}
422 · Validation errorA single required field is missing — here, the IRN (code 99).
422 Unprocessable
{
  "code": 99,
  "status": "error",
  "message": "The irn (Invoice Reference Number) field is required.",
  "reference": null,
  "data": {
    "errors": {
      "irn": [
        "The irn field is required."
      ]
    }
  }
}
422 · Multiple errorsThe payload is validated as a whole, so every failing field comes back at once — fix them all in one pass. Read data.errors (keyed by dotted path).
422 Unprocessable · all errors
{
  "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."
      ]
    }
  }
}
422 · Calculation errorThe payload is structurally valid but the figures do not add up. Each entry in data.errors gives the field, the expected value and the one you provided.
422 Unprocessable · calculation
{
  "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.

CodeMeaning
0

Success

The invoice was accepted and forwarded to the Access Point. Inspect data.receipt_status for the downstream stage.

10

Missing credentials

The x-client-id and/or x-client-secret header was not provided.

11

Invalid credentials

The client id/secret pair did not match an active client, or the client is disabled.

12

Account not approved

Your company account is pending, suspended or rejected. Transmission is blocked until an administrator approves it.

13

Account not found

No company account is linked to these credentials.

14

Crypto keys not found

NRS public key and certificate not setup for this account.

15

Production disabled

Production API access is disabled for this account because production billing has not been activated.

16

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

Business and Service ID maybe missing

NRS Business ID and Service ID may not have been provided for this account.

18

Business ID mismatch

Business ID in request payload not the same as the provided resolved client business id.

19

Tax ID mismatch

Tax ID in request payload not the same as the provided resolved client TIN.

20

Unsupported content type

The Content-Type header must be application/json.

21

Empty body

No request body was sent.

22

Invalid JSON

The request body could not be parsed as JSON.

23

IRN record not found

The provided IRN record not found, please transmit.

24

Service ID mismatch

IRN is badly formed. Service ID in iRN format is not the same as the provided resolved client service id.

25

Update invoice status failed

Update invoice status failed on NRS server

26

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

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

Acknowledge transmission failed

Acknowledge Transmission failed, try again or contact APP support.

30

Invalid format header

The x-invoice-format header must be "nrs".

40

Validation failed

One or more required fields are missing or invalid. See data.errors for the exact fields.

50

Rejected by Access Point

The downstream Access Point (e.g. FIRS) rejected the invoice. See data for the provider message.

51

Transmission failed

An unexpected error occurred while forwarding the invoice to the Access Point.

52

Access Point unreachable

The downstream Access Point did not respond in time.

53

Network connection error

We are unable to reach the NRS network at this time. Try again later. If the problem persists, contact support.

54

Provider not configured

Your account has not been configured with Access Point credentials. Contact support.

55

Network connection error

Our network is currently down due to maintenance or technical issues. Try again later. If the problem persists, contact support.

56

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

Invoice number not provided

The required invoice number field is missing from the payload.

91

IRN not provided

The required irn (Invoice Reference Number) field is missing from the payload.

92

Invalid IRN format

The provided irn format is invalid. Check the irn format in the documentation.

93

Irn confirmation failed

The irn confirmation failed, try again or check irn format.

94

Invalid business ID

The provided business ID format is invalid. Check the business ID format in the documentation.

95

Invoice signing failed

The invoice signing failed, might be due to a duplicate signing, try again or contact APP support.

96

Invoice transmission failed

The invoice transmission failed, might be due to a duplicate request or network issues, try again or contact support.

100

Invoice line basis invalid

An invoice line is missing a numeric invoiced_quantity or price.price_amount, so its amounts cannot be calculated.

101

Line discount amount invalid

invoice_line[].discount_amount does not match discount_rate % of (invoiced_quantity x price.price_amount).

102

Line fee amount invalid

invoice_line[].fee_amount does not match fee_rate % of the line amount after discount.

103

Line extension amount invalid

invoice_line[].line_extension_amount must equal (invoiced_quantity x price.price_amount) - discount_amount + fee_amount.

104

Line tax percent invalid

An invoice line tax_category is missing a numeric percent, so the line tax cannot be calculated.

105

Line tax category invalid

An invoice line tax_category is missing or invalid, so the line tax cannot be calculated.

110

Total line extension amount invalid

legal_monetary_total.line_extension_amount must equal the sum of every invoice_line line_extension_amount.

111

Tax exclusive amount invalid

legal_monetary_total.tax_exclusive_amount must equal the sum of every invoice_line line_extension_amount.

112

Tax inclusive amount invalid

legal_monetary_total.tax_inclusive_amount must equal tax_exclusive_amount plus the total line tax.

113

Payable amount invalid

legal_monetary_total.payable_amount must equal legal_monetary_total.tax_inclusive_amount.

120

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

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

Tax subtotal tax amount invalid

tax_subtotal[].tax_amount must equal taxable_amount x tax_category.percent %.

123

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

Tax subtotal category percent invalid

The tax categories percent declared in tax_subtotal do not match NRS valid tax category percent

125

Tax subtotal invalid

The tax_total.tax_subtotal array is missing or invalid.

130

Allowance charge fee total invalid

allowance_charge with charge_indicator true must equal the total fee_amount of all invoice lines.

131

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.

1

Initiated

Invoice transmission initiated, not yet signed or transmitted.

2

Signed

Invoice signed, but not yet transmitted to NRS.

3

Transmitting

Invoice is been transmitted, waiting for confirmation.

4

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.

PATCH/update-status/{irn}
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" }'
FieldDescription
payment_statusrequiredNew settlement state of the invoice: pending, partial, paid, or rejected (case-insensitive).
amountAmount paid. Optional in general, but required when payment_status is partial — it records how much of the invoice has been settled.
reasonOptional note explaining the change (e.g. why an invoice was rejected).
PATCH /update-status/{irn} · body
{
  "payment_status": "PAID"
}
200 · Success
200 OK
{
  "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.

POST/report
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
FieldDescription
irnrequiredInvoice Reference Number of the transmitted invoice being reported.
beneficiary_tinrequiredAccounting Buyer Party TIN (the customer).
currencyrequiredDocument currency code in ISO 4217 format, e.g. NGN.
transaction_daterequiredInvoice issue date (YYYY-MM-DD).
base_amountrequiredLine extension amount — the amount to be taxed.
total_amountrequiredPayable amount — the amount to be collected.
vat_calculatedrequiredTax amount for the tax category (STANDARD_VAT, ZERO_VAT or REDUCED_VAT).
vat_raterequiredPercentage attached to the tax category, e.g. 7.5.
vat_statusrequiredThe VAT tax category ID, e.g. STANDARD_VAT.
other_taxesSum of non-VAT tax amounts.
item_descriptionItem description within the invoice line.
POST /report · body
{
  "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"
}
200 · Success
200 OK
{
  "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.

POST/acknowledge/{irn}
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: ••••••••••••••••"
200 · Success
200 OK
{
  "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:

POST {your-webhook-url} · body
{
  "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.