Taxlyne Docs
API

Your first invoice

A working request that creates an invoice, with every required field explained.

The request

curl -X POST https://staging-e-invoice.harmonizedx.com/api/v1/invoices \
  -H "X-API-Key: sk_test_a1b2c3d4e5f67890abcdef1234567890" \
  -H "Content-Type: application/json" \
  -H "idempotency-key: 9f8c1e40-3b7a-4a2f-8c1d-7e2b5a904f11" \
  -d '{
    "businessId": "clxyz123abc456def789",
    "buyer": {
      "type": "B2B",
      "tin": "12345678-0001",
      "name": "Acme Nigeria Limited",
      "email": "accounts@acme.example"
    },
    "dueDate": "2026-10-01T23:59:59.000Z",
    "lines": [
      {
        "description": "Professional consulting services — September 2026",
        "quantity": 10,
        "unitPrice": 15000.00,
        "taxRate": 7.5,
        "hsnCode": "8471.30"
      }
    ]
  }'

The invoice is created as a draft. It is not with FIRS yet — see Filing with FIRS for the next three calls.

What is required

FieldNotes
businessIdThe business the invoice belongs to. A BUSINESS key may only use its own.
buyer.typeB2B, B2C or B2G. The only required field inside buyer.
dueDateISO 8601.
linesAt least one. An invoice with no lines is rejected.

Every line requires five things:

Line fieldNotes
description
quantity
unitPrice
taxRateA percentage — 7.5, not 0.075. Required on every line; there is no invoice-level tax field.
hsnCode or isicCodeA classification code. One of the two is required — see below.

sku, discountRate, inventoryItemId, productCategory and serviceCategory are genuinely optional.

idempotency-key is required, not optional, on invoice creation. Leaving it out is a 400. See Idempotency for how to choose one.

Fields worth getting right

buyer.tin is optional to us and effectively mandatory in practice. FIRS accepts a placeholder without complaint, so a wrong or missing TIN produces a filing that looks successful and names the wrong party. Send the real one.

Classifying a line

Every line needs a classification code, and there are two kinds. Send whichever fits the line — the correct FIRS fields are populated for you.

Line isSendFormatGet valid codes from
A physical goodhsnCodeNNNN.NN — e.g. 8471.30GET /firs/hs-codes
A serviceisicCodefour digits — e.g. 6190GET /firs/service-codes

A four-digit code is read as an ISIC service-activity code and checked against the 419 published classes. It is not a truncated HS code, and it is not rejected.

Never pad a four-digit code to NNNN.00. 8471 is not an ISIC class; padding it to 8471.00 names an unrelated and usually nonexistent product, and FIRS accepts it without complaint. If the line is a good and you only have four digits, look up the real HS code — for 8471 it is probably 8471.30.

Placeholders are rejected. Neither 0000.00 nor 0000 is accepted. Omitting both fields is a 400: "has an invalid or missing classification code".

Sending both hsnCode and isicCode with different values is also a 400 — the invoice line stores one code, so we ask rather than guess which you meant.

productCategory and serviceCategory are optional labels, but if you send one it must be descriptive — a very short value is rejected. Omit them to use the default.

Totals are yours to get right

We calculate the invoice totals from the lines you send, and FIRS does not check the arithmetic at all — a total that contradicts its own lines is accepted. Nothing downstream will catch a mistake here, so reconcile on your side.

On this page