API Documentation

Introduction

Welcome to the Afrinvoice API documentation. This guide provides all required information for developers integrating with our e-Invoicing platform, including authentication, invoice validation, IRN generation, QR generation, and response formats.

The API follows RESTful principles and accepts and returns data in JSON format. All requests must be authenticated using your Access Key and Access Secret obtained from the authentication endpoint.

Main Capabilities
  • Generate IRN (client-side pattern)
  • Validate Invoice Payload
  • Generate encrypted QR Code (FIRS standard)
  • Download & Confirm Invoices
  • Access Point Provider (APP) relay & taxpayer management
Base URL: https://api.e-afrinvoice.com

Authentication

Authenticate using email & password to receive access_token and access_secret.

LIVE POST /api/login
// Login endpoint
POST /api/login
Content-Type: application/json

Authenticate with email + password
and receive a token pair used in all requests.
{
  "email": "user@example.com",
  "password": "yourpassword"
}
{
  "status": "success",
  "access_token": "YXNkZjEyMzQtYWJjZC01Njc4LWpoazAtc2VjcmV0dG9rZW4=",
  "access_secret": "c2VjcmV0LXZhbHVlLWFzZGZhc2RmLWhhbmRsZW1l"
}
FieldRequiredDescription
emailYesUser login email
passwordYesUser login password

System Integrator API Endpoints

Generate IRN

Client-side IRN formation pattern (server only receives the resulting IRN).

LOCAL Client: generate IRN string
{invoiceNumber}-{FIRS_SERVICE_ID}-{YYYYMMDD}
                  
e.g. INV2549-4B2A4F6E-20260327
INV2549-4B2A4F6E-20260327
FieldRequiredDescription
invoiceNumberYesAlphanumeric only (no spaces/special chars)
FIRS_SERVICE_IDYesYour assigned service id from FIRS
invoiceDateYesYYYY-MM-DD (used to produce YYYYMMDD)

Validate Invoice

Validate invoice payload with FIRS via your backend proxy.

POST /api/validate-invoice
{
  "business_id": "6389237e-4f6e-4371-aece-aea5d2536df3",
  "irn": "INV2549-4B2A4F6E-20260327",
  "issue_date": "2026-03-27",
  "due_date": "2026-04-07",
  "issue_time": "17:51:14",
  "invoice_type_code": "396",
  "invoice_kind": "B2B",
  "payment_status": "PENDING",
  "note": "",
  "tax_point_date": "2026-03-27",
  "document_currency_code": "NGN",
  "tax_currency_code": "NGN",
  "accounting_cost": "100232 NGN",
  "buyer_reference": "",
  "invoice_delivery_period": {
    "start_date": "2026-03-27",
    "end_date": "2025-10-25"
  },
  "order_reference": "",
  "billing_reference": null,
  "dispatch_document_reference": null,
  "receipt_document_reference": null,
  "originator_document_reference": null,
  "contract_document_reference": null,
  "additional_document_reference": null,
  "accounting_supplier_party": {
    "party_name": "AFRI INVOICE NIG LTD",
    "tin": "32492774-0001",
    "email": "developer@afrinvoice.com",
    "telephone": "+2348026664444",
    "business_description": "Consulting services",
    "postal_address": {
      "street_name": "10, Gbolagade Street, off arol",
      "city_name": "ANTHONY",
      "postal_zone": "100232",
      "lga": "70",
      "state": "4",
      "country": "NG"
    }
  },
  "accounting_customer_party": {
    "party_name": "C-ADE CONSULTS",
    "tin": "33577312-0001",
    "email": "mark@markodenore.com",
    "telephone": "+2348026665555",
    "business_description": null,
    "postal_address": {
      "street_name": "3, PLOT 121 YAHAYA CLOSE OFF ADEJOBI CRESCENT ANTHONY LAGOS",
      "city_name": "ANTHONY",
      "postal_zone": "100232",
      "lga": "70",
      "state": "4",
      "country": "NG"
    }
  },
  "payee_party": null,
  "bill_party": null,
  "ship_party": null,
  "tax_representative_party": null,
  "actual_delivery_date": "2026-03-27",
  "payment_means": null,
  "payment_terms_note": null,
  "tax_total": [
    {
      "tax_amount": 750000,
      "tax_subtotal": [
        {
          "taxable_amount": 10000000,
          "tax_amount": 750000,
          "tax_category": {
            "id": "LOCAL_SALES_TAX",
            "percent": 7.5
          }
        }
      ]
    }
  ],
  "invoice_line": [
    {
      "hsn_code": "9022.29",
      "product_category": "Industrial equipment",
      "discount_rate": 0,
      "discount_amount": 0,
      "fee_rate": 7.5,
      "fee_amount": 750000,
      "invoiced_quantity": 10,
      "line_extension_amount": 10000000,
      "item": {
        "name": "Industrial Gamma Radiography Inspection System",
        "description": "Apparatus based on the use of ionising radiations",
        "sellers_item_identification": "item-1"
      },
      "price": {
        "price_amount": 1000000,
        "base_quantity": 1,
        "price_unit": "NGN per 1"
      }
    }
  ]
}
{
    "message": "FIRS validation successful",
    "data": {
        "code": 200,
        "data": {
            "ok": true
        }
    }
}
HeaderValue / Notes
Content-Typeapplication/json
x-api-keyBackend injects when proxying to FIRS (do not expose)
x-api-secretBackend injects when proxying to FIRS (do not expose)

Top-level required fields: business_id, irn, issue_date, invoice_line, legal_monetary_total.

Generate QR Code

Encrypt IRN + server-side timestamp with FIRS public key and return QR image (base64 data URL).

POST /api/generate-qr
{
  "irn": "INV2549-4B2A4F6E-20260327"
}
{
    "success": true,
    "qrCodeDataUrl": "data:image/png;base64,iVBORw0KGgoAAAANS...",
    "encryptedBase64": "o24MzpmIqWG55j97118z...",
    "message": "QR generated successfully"
}
FieldTypeRequiredDescription
irnstringYes IRN generated by client (server appends timestamp internally)

Server will append a unix timestamp before encrypting, per FIRS spec.

Access Point Provider (APP) API Endpoints

Key APP responsibilities include: data validation against NRS schema, cryptographic stamp validation (SHA-256 hashing + ECDSA), digital certificate management (issuance, renewal, revocation), secure transmission over encrypted channels, error handling, audit logging, and interoperability between different ERP systems and the NRS platform.

Authentication: All APP endpoints use OAuth 2.0 (as mandated by NRS). Your APP client credentials (client_id + client_secret) are exchanged for a bearer token, which is passed as Authorization: Bearer <token> on every request. These credentials are separate from System Integrator credentials and are issued only to NITDA-accredited APPs.
Cryptographic Standards: All invoice signatures must use SHA-256 hashing + ECDSA (Elliptic Curve Digital Signature Algorithm). JSON invoices must carry a JAdES (JSON Advanced Electronic Signature) digital signature. Private keys must be stored in HSMs or equivalent secure storage.

1. Obtain OAuth 2.0 Access Token

All APP API calls require a bearer token obtained via the OAuth 2.0 Client Credentials grant flow, as mandated by NRS. Exchange your client_id and client_secret (issued to your accredited APP account) for a short-lived access token. Tokens must be refreshed before expiry to ensure uninterrupted service.

POST /api/app/oauth/token
POST /api/app/oauth/token
Content-Type: application/x-www-form-urlencoded

grant_type=client_credentials
&client_id=YOUR_APP_CLIENT_ID
&client_secret=YOUR_APP_CLIENT_SECRET
&scope=app:invoices app:taxpayers app:certificates
{
  "access_token": "eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9...",
  "token_type": "Bearer",
  "expires_in": 3600,
  "scope": "app:invoices app:taxpayers app:certificates"
}
curl -X POST "https://api.e-afrinvoice.com/api/app/oauth/token" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "grant_type=client_credentials&client_id=YOUR_CLIENT_ID&client_secret=YOUR_CLIENT_SECRET&scope=app:invoices app:taxpayers"
$ch = curl_init("https://api.e-afrinvoice.com/api/app/oauth/token");
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query([
  "grant_type"    => "client_credentials",
  "client_id"     => "YOUR_CLIENT_ID",
  "client_secret" => "YOUR_CLIENT_SECRET",
  "scope"         => "app:invoices app:taxpayers app:certificates"
]));
curl_setopt($ch, CURLOPT_HTTPHEADER, ["Content-Type: application/x-www-form-urlencoded"]);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$res = json_decode(curl_exec($ch), true);
curl_close($ch);
$token = $res['access_token']; // Use as: "Authorization: Bearer {$token}"
import axios from "axios";
import qs from "querystring";

const { data } = await axios.post(
  "https://api.e-afrinvoice.com/api/app/oauth/token",
  qs.stringify({
    grant_type: "client_credentials",
    client_id: "YOUR_CLIENT_ID",
    client_secret: "YOUR_CLIENT_SECRET",
    scope: "app:invoices app:taxpayers app:certificates"
  }),
  { headers: { "Content-Type": "application/x-www-form-urlencoded" } }
);

const token = data.access_token;
// Use as: headers: { Authorization: `Bearer ${token}` }
import requests

res = requests.post(
  "https://api.e-afrinvoice.com/api/app/oauth/token",
  data={
    "grant_type": "client_credentials",
    "client_id": "YOUR_CLIENT_ID",
    "client_secret": "YOUR_CLIENT_SECRET",
    "scope": "app:invoices app:taxpayers app:certificates"
  }
)
token = res.json()["access_token"]
# Use as: headers={"Authorization": f"Bearer {token}"}

2. Register Taxpayer (Onboard Business)

Register and onboard a taxpayer business under the APP account. This links the taxpayer's TIN and business profile to your Access Point for invoice relay. Per FIRS MBS rules, a taxpayer must be enabled on the einvoice.firs.gov.ng portal before the APP can register them via API.

POST /api/app/register-taxpayer
POST /api/app/register-taxpayer
Content-Type: application/json
Authorization: Bearer YOUR_OAUTH2_ACCESS_TOKEN

{
  "tin": "32492774-0001",
  "business_name": "AFRI INVOICE NIG LTD",
  "email": "developer@afrinvoice.com",
  "telephone": "+2348026664444",
  "business_description": "Technology and consulting services",
  "postal_address": {
    "street_name": "10, Gbolagade Street, off Arol",
    "city_name": "ANTHONY",
    "postal_zone": "100232",
    "lga": "70",
    "state": "4",
    "country": "NG"
  },
  "service_id": "4B2A4F6E"
}
{
  "status": "success",
  "message": "Taxpayer registered successfully under APP",
  "data": {
    "business_id": "6389237e-4f6e-4371-aece-aea5d2536df3",
    "tin": "32492774-0001",
    "business_name": "AFRI INVOICE NIG LTD",
    "service_id": "4B2A4F6E",
    "app_reference": "APP-NG-20260327-00142",
    "status": "ACTIVE",
    "registered_at": "2026-03-27T10:14:00Z"
  }
}
FieldTypeRequiredDescription
tinstringYesTaxpayer Identification Number (TIN) — must be pre-enabled on the FIRS MBS portal
business_namestringYesLegal registered business name
emailstringYesPrimary contact email for the taxpayer
telephonestringYesContact phone number in international format
business_descriptionstringNoShort description of the business activity
postal_addressobjectYesBusiness address — see Address Object below
service_idstringYesFIRS-assigned Service ID for the taxpayer

Address Object

FieldTypeRequiredDescription
street_namestringYesStreet address
city_namestringYesCity
postal_zonestringYesPostal / ZIP code
lgastringYesLGA code (FIRS reference)
statestringYesState code (FIRS reference)
countrystringYesISO 3166-1 alpha-2 country code (e.g. NG)
curl -X POST "https://api.e-afrinvoice.com/api/app/register-taxpayer" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer YOUR_OAUTH2_ACCESS_TOKEN" \
  -d '{
    "tin": "32492774-0001",
    "business_name": "AFRI INVOICE NIG LTD",
    "email": "developer@afrinvoice.com",
    "telephone": "+2348026664444",
    "business_description": "Technology and consulting services",
    "postal_address": {
      "street_name": "10, Gbolagade Street, off Arol",
      "city_name": "ANTHONY",
      "postal_zone": "100232",
      "lga": "70",
      "state": "4",
      "country": "NG"
    },
    "service_id": "4B2A4F6E"
  }'
$payload = [
  "tin" => "32492774-0001",
  "business_name" => "AFRI INVOICE NIG LTD",
  "email" => "developer@afrinvoice.com",
  "telephone" => "+2348026664444",
  "business_description" => "Technology and consulting services",
  "postal_address" => [
    "street_name" => "10, Gbolagade Street, off Arol",
    "city_name" => "ANTHONY",
    "postal_zone" => "100232",
    "lga" => "70",
    "state" => "4",
    "country" => "NG"
  ],
  "service_id" => "4B2A4F6E"
];

$ch = curl_init("https://api.e-afrinvoice.com/api/app/register-taxpayer");
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($payload));
curl_setopt($ch, CURLOPT_HTTPHEADER, [
  "Content-Type: application/json",
  "Authorization: Bearer YOUR_OAUTH2_ACCESS_TOKEN"
]);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
curl_close($ch);
echo $response;
import axios from "axios";

axios.post("https://api.e-afrinvoice.com/api/app/register-taxpayer", {
  tin: "32492774-0001",
  business_name: "AFRI INVOICE NIG LTD",
  email: "developer@afrinvoice.com",
  telephone: "+2348026664444",
  business_description: "Technology and consulting services",
  postal_address: {
    street_name: "10, Gbolagade Street, off Arol",
    city_name: "ANTHONY",
    postal_zone: "100232",
    lga: "70",
    state: "4",
    country: "NG"
  },
  service_id: "4B2A4F6E"
}, {
  headers: {
    "Authorization": "Bearer YOUR_OAUTH2_ACCESS_TOKEN"
  }
}).then(res => console.log(res.data))
  .catch(err => console.error(err));
import requests

payload = {
  "tin": "32492774-0001",
  "business_name": "AFRI INVOICE NIG LTD",
  "email": "developer@afrinvoice.com",
  "telephone": "+2348026664444",
  "business_description": "Technology and consulting services",
  "postal_address": {
    "street_name": "10, Gbolagade Street, off Arol",
    "city_name": "ANTHONY",
    "postal_zone": "100232",
    "lga": "70",
    "state": "4",
    "country": "NG"
  },
  "service_id": "4B2A4F6E"
}

headers = {
  "Authorization": "Bearer YOUR_OAUTH2_ACCESS_TOKEN"
}
  json=payload,
  headers=headers
)
print(r.json())

3. Submit Invoice to FIRS (Relay)

Submit a validated invoice payload to NRS MBS on behalf of a registered taxpayer. Before relay, the APP validates all mandatory fields against the NRS e-invoice schema (TIN, VAT calculations, amounts, cryptographic stamps) and verifies the JAdES digital signature using NRS-issued public keys. On successful NRS clearance, a Cryptographic Stamp Identifier (CSID) is returned and the invoice is made available to the buyer. Buyers have 72 hours to reject the invoice.

POST /api/app/submit-invoice
POST /api/app/submit-invoice
Content-Type: application/json
Authorization: Bearer YOUR_OAUTH2_ACCESS_TOKEN
# token obtained via OAuth 2.0 client_credentials grant

{
  "business_id": "6389237e-4f6e-4371-aece-aea5d2536df3",
  "irn": "INV2549-4B2A4F6E-20260327",
  "issue_date": "2026-03-27",
  "due_date": "2026-04-07",
  "issue_time": "17:51:14",
  "invoice_type_code": "396",
  "invoice_kind": "B2B",
  "payment_status": "PENDING",
  "document_currency_code": "NGN",
  "tax_currency_code": "NGN",
  "accounting_supplier_party": {
    "party_name": "AFRI INVOICE NIG LTD",
    "tin": "32492774-0001",
    "email": "developer@afrinvoice.com",
    "telephone": "+2348026664444",
    "postal_address": {
      "street_name": "10, Gbolagade Street",
      "city_name": "ANTHONY",
      "postal_zone": "100232",
      "lga": "70",
      "state": "4",
      "country": "NG"
    }
  },
  "accounting_customer_party": {
    "party_name": "C-ADE CONSULTS",
    "tin": "33577312-0001",
    "email": "buyer@cadeconsults.com",
    "telephone": "+2348026665555",
    "postal_address": {
      "street_name": "3, Plot 121 Yahaya Close",
      "city_name": "ANTHONY",
      "postal_zone": "100232",
      "lga": "70",
      "state": "4",
      "country": "NG"
    }
  },
  "tax_total": [
    {
      "tax_amount": 750000,
      "tax_subtotal": [
        {
          "taxable_amount": 10000000,
          "tax_amount": 750000,
          "tax_category": { "id": "LOCAL_SALES_TAX", "percent": 7.5 }
        }
      ]
    }
  ],
  "legal_monetary_total": {
    "line_extension_amount": 10000000,
    "tax_exclusive_amount": 10000000,
    "tax_inclusive_amount": 10750000,
    "payable_amount": 10750000
  },
  "invoice_line": [
    {
      "hsn_code": "9022.29",
      "product_category": "Industrial Equipment",
      "invoiced_quantity": 10,
      "line_extension_amount": 10000000,
      "fee_rate": 7.5,
      "fee_amount": 750000,
      "discount_rate": 0,
      "discount_amount": 0,
      "item": {
        "name": "Industrial Gamma Radiography Inspection System",
        "description": "Apparatus based on ionising radiations",
        "sellers_item_identification": "item-1"
      },
      "price": {
        "price_amount": 1000000,
        "base_quantity": 1,
        "price_unit": "NGN per 1"
      }
    }
  ]
}
{
  "status": "success",
  "message": "Invoice submitted to FIRS successfully",
  "data": {
    "irn": "INV2549-4B2A4F6E-20260327",
    "csid": "CSID-FIRS-20260327-XK9P2M7Q",
    "submission_timestamp": "2026-03-27T17:52:03Z",
    "firs_status": "CLEARED",
    "qr_code_data_url": "data:image/png;base64,iVBORw0KGgoAAAANS...",
    "buyer_rejection_deadline": "2026-03-30T17:52:03Z"
  }
}
FieldTypeRequiredDescription
business_idstring (UUID)YesTaxpayer's business UUID from registration
irnstringYesPre-generated IRN: {invoiceNo}-{SERVICE_ID}-{YYYYMMDD}
issue_datestring (YYYY-MM-DD)YesInvoice issue date
invoice_type_codestringYesFIRS invoice type: 380 (Commercial Invoice), 381 (Credit Note), 383 (Debit Note), 396 (Factored Invoice)
invoice_kindstringYesB2B or B2G for pre-clearance; B2C for near-real-time reporting
accounting_supplier_partyobjectYesSupplier details including TIN, name, address
accounting_customer_partyobjectYesBuyer details including TIN, name, address
tax_totalarrayYesVAT and other tax totals with subtotals
legal_monetary_totalobjectYesInvoice monetary totals
invoice_linearrayYesLine items with HSN code, quantity, price, tax
B2C Invoices: For invoice_kind: "B2C", the invoice is issued directly to the customer first, then must be reported to FIRS within 24 hours. accounting_customer_party.tin is not required for B2C.
curl -X POST "https://api.e-afrinvoice.com/api/app/submit-invoice" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer YOUR_OAUTH2_ACCESS_TOKEN" \
  -H // token obtained via OAuth 2.0 client_credentials grant \
  -d '{ ... invoice payload ... }'
$ch = curl_init("https://api.e-afrinvoice.com/api/app/submit-invoice");
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($invoicePayload));
curl_setopt($ch, CURLOPT_HTTPHEADER, [
  "Content-Type: application/json",
  "Authorization: Bearer YOUR_OAUTH2_ACCESS_TOKEN",
  // token obtained via OAuth 2.0 client_credentials grant
]);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
curl_close($ch);
echo $response;
import axios from "axios";

const res = await axios.post(
  "https://api.e-afrinvoice.com/api/app/submit-invoice",
  invoicePayload,
  { headers: { "Authorization": "Bearer YOUR_OAUTH2_ACCESS_TOKEN", // OAuth 2.0 bearer token } }
);
console.log(res.data);
import requests

r = requests.post(
  "https://api.e-afrinvoice.com/api/app/submit-invoice",
  json=invoice_payload,
  headers={"Authorization": "Bearer YOUR_OAUTH2_ACCESS_TOKEN", // OAuth 2.0 bearer token}
)
print(r.json())

4. Confirm IRN & CSID Status

Query the current FIRS clearance status of an invoice using its IRN. Returns the CSID, clearance timestamp, and buyer acceptance or rejection status. Use this to verify invoice state before delivering to the buyer.

GET /api/app/confirm-irn/{irn}
GET /api/app/confirm-irn/INV2549-4B2A4F6E-20260327
Authorization: Bearer YOUR_OAUTH2_ACCESS_TOKEN
# token obtained via OAuth 2.0 client_credentials grant
{
  "status": "success",
  "data": {
    "irn": "INV2549-4B2A4F6E-20260327",
    "csid": "CSID-FIRS-20260327-XK9P2M7Q",
    "firs_status": "CLEARED",
    "buyer_status": "PENDING_ACCEPTANCE",
    "submitted_at": "2026-03-27T17:52:03Z",
    "cleared_at": "2026-03-27T17:54:11Z",
    "buyer_rejection_deadline": "2026-03-30T17:52:03Z",
    "invoice_kind": "B2B",
    "supplier_tin": "32492774-0001",
    "buyer_tin": "33577312-0001"
  }
}
ParamTypeRequiredDescription
irnstring (path)YesThe Invoice Reference Number to query

FIRS Status Values

StatusMeaning
PENDINGSubmitted; awaiting FIRS validation
CLEAREDFIRS validated and issued CSID
REJECTED_BY_FIRSFailed FIRS validation — correct and resubmit
REJECTED_BY_BUYERBuyer rejected within 72-hour window
ACCEPTEDBuyer confirmed acceptance
CANCELLEDInvoice cancelled (credit note issued)
curl -X GET "https://api.e-afrinvoice.com/api/app/confirm-irn/INV2549-4B2A4F6E-20260327" \
  -H "Authorization: Bearer YOUR_OAUTH2_ACCESS_TOKEN" \
  -H // token obtained via OAuth 2.0 client_credentials grant
$irn = "INV2549-4B2A4F6E-20260327";
$ch = curl_init("https://api.e-afrinvoice.com/api/app/confirm-irn/{$irn}");
curl_setopt($ch, CURLOPT_HTTPHEADER, [
  "Authorization: Bearer YOUR_OAUTH2_ACCESS_TOKEN",
  // token obtained via OAuth 2.0 client_credentials grant
]);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
curl_close($ch);
echo $response;
import axios from "axios";

const irn = "INV2549-4B2A4F6E-20260327";
const res = await axios.get(
  `https://api.e-afrinvoice.com/api/app/confirm-irn/${irn}`,
  { headers: { "Authorization": "Bearer YOUR_OAUTH2_ACCESS_TOKEN", // OAuth 2.0 bearer token } }
);
console.log(res.data);
import requests

irn = "INV2549-4B2A4F6E-20260327"
r = requests.get(
  f"https://api.e-afrinvoice.com/api/app/confirm-irn/{irn}",
  headers={"Authorization": "Bearer YOUR_OAUTH2_ACCESS_TOKEN", // OAuth 2.0 bearer token}
)
print(r.json())

5. Download Invoice (Buyer Side)

Retrieve a FIRS-cleared invoice on behalf of the buyer. Returns the full invoice payload including CSID, IRN, QR code, and all line items. Buyers receive invoices transmitted by the supplier's APP via the FIRS four-corner model.

GET /api/app/download-invoice/{irn}
GET /api/app/download-invoice/INV2549-4B2A4F6E-20260327
Authorization: Bearer YOUR_OAUTH2_ACCESS_TOKEN
# token obtained via OAuth 2.0 client_credentials grant

// Optional query parameter to get PDF binary:
GET /api/app/download-invoice/INV2549-4B2A4F6E-20260327?format=pdf
{
  "status": "success",
  "data": {
    "irn": "INV2549-4B2A4F6E-20260327",
    "csid": "CSID-FIRS-20260327-XK9P2M7Q",
    "firs_status": "CLEARED",
    "issue_date": "2026-03-27",
    "due_date": "2026-04-07",
    "invoice_type_code": "396",
    "invoice_kind": "B2B",
    "document_currency_code": "NGN",
    "qr_code_data_url": "data:image/png;base64,iVBORw0KGgoAAAANS...",
    "accounting_supplier_party": {
      "party_name": "AFRI INVOICE NIG LTD",
      "tin": "32492774-0001",
      "email": "developer@afrinvoice.com"
    },
    "accounting_customer_party": {
      "party_name": "C-ADE CONSULTS",
      "tin": "33577312-0001",
      "email": "buyer@cadeconsults.com"
    },
    "legal_monetary_total": {
      "line_extension_amount": 10000000,
      "tax_exclusive_amount": 10000000,
      "tax_inclusive_amount": 10750000,
      "payable_amount": 10750000
    },
    "tax_total": [
      {
        "tax_amount": 750000,
        "tax_subtotal": [
          {
            "taxable_amount": 10000000,
            "tax_amount": 750000,
            "tax_category": { "id": "LOCAL_SALES_TAX", "percent": 7.5 }
          }
        ]
      }
    ],
    "invoice_line": [
      {
        "hsn_code": "9022.29",
        "product_category": "Industrial Equipment",
        "invoiced_quantity": 10,
        "line_extension_amount": 10000000,
        "item": {
          "name": "Industrial Gamma Radiography Inspection System",
          "sellers_item_identification": "item-1"
        },
        "price": { "price_amount": 1000000, "base_quantity": 1, "price_unit": "NGN per 1" }
      }
    ]
  }
}
ParamTypeRequiredDescription
irnstring (path)YesThe Invoice Reference Number to download
formatstring (query)NoResponse format: json (default) or pdf
The caller's APP credentials must be linked to the buyer's TIN to access the invoice. Unauthorized access returns 403 Forbidden.
curl -X GET "https://api.e-afrinvoice.com/api/app/download-invoice/INV2549-4B2A4F6E-20260327" \
  -H "Authorization: Bearer YOUR_OAUTH2_ACCESS_TOKEN" \
  -H // token obtained via OAuth 2.0 client_credentials grant

# To download as PDF:
curl -X GET "https://api.e-afrinvoice.com/api/app/download-invoice/INV2549-4B2A4F6E-20260327?format=pdf" \
  -H "Authorization: Bearer YOUR_OAUTH2_ACCESS_TOKEN" \
  -H // token obtained via OAuth 2.0 client_credentials grant \
  --output invoice.pdf
$irn = "INV2549-4B2A4F6E-20260327";
$ch = curl_init("https://api.e-afrinvoice.com/api/app/download-invoice/{$irn}");
curl_setopt($ch, CURLOPT_HTTPHEADER, [
  "Authorization: Bearer YOUR_OAUTH2_ACCESS_TOKEN",
  // token obtained via OAuth 2.0 client_credentials grant
]);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
curl_close($ch);
$data = json_decode($response, true);
echo $response;
import axios from "axios";

const irn = "INV2549-4B2A4F6E-20260327";
const res = await axios.get(
  `https://api.e-afrinvoice.com/api/app/download-invoice/${irn}`,
  { headers: { "Authorization": "Bearer YOUR_OAUTH2_ACCESS_TOKEN", // OAuth 2.0 bearer token } }
);
console.log(res.data);
import requests

irn = "INV2549-4B2A4F6E-20260327"
r = requests.get(
  f"https://api.e-afrinvoice.com/api/app/download-invoice/{irn}",
  headers={"Authorization": "Bearer YOUR_OAUTH2_ACCESS_TOKEN", // OAuth 2.0 bearer token}
)
print(r.json())

6. Accept or Reject Invoice (Buyer Action)

Allows the buyer's APP to accept or reject a FIRS-cleared invoice within the 72-hour window. A rejection must include a reason code. If no action is taken within 72 hours, FIRS treats the invoice as implicitly accepted.

POST /api/app/invoice-action
POST /api/app/invoice-action
Content-Type: application/json
Authorization: Bearer YOUR_OAUTH2_ACCESS_TOKEN
# token obtained via OAuth 2.0 client_credentials grant

// Accept:
{
  "irn": "INV2549-4B2A4F6E-20260327",
  "action": "ACCEPT",
  "buyer_tin": "33577312-0001"
}

// Reject:
{
  "irn": "INV2549-4B2A4F6E-20260327",
  "action": "REJECT",
  "buyer_tin": "33577312-0001",
  "rejection_reason_code": "INCORRECT_AMOUNT",
  "rejection_note": "Invoice amount does not match purchase order PO-2026-0041"
}
// Accept response:
{
  "status": "success",
  "message": "Invoice accepted successfully",
  "data": {
    "irn": "INV2549-4B2A4F6E-20260327",
    "action": "ACCEPT",
    "buyer_tin": "33577312-0001",
    "actioned_at": "2026-03-28T09:11:45Z",
    "firs_status": "ACCEPTED"
  }
}

// Reject response:
{
  "status": "success",
  "message": "Invoice rejected. Supplier notified via FIRS.",
  "data": {
    "irn": "INV2549-4B2A4F6E-20260327",
    "action": "REJECT",
    "rejection_reason_code": "INCORRECT_AMOUNT",
    "actioned_at": "2026-03-28T09:15:02Z",
    "firs_status": "REJECTED_BY_BUYER"
  }
}
FieldTypeRequiredDescription
irnstringYesInvoice Reference Number
actionstringYesACCEPT or REJECT
buyer_tinstringYesTIN of the buyer performing the action
rejection_reason_codestringIf REJECTStandardized rejection code (see below)
rejection_notestringNoHuman-readable rejection explanation

Rejection Reason Codes

CodeDescription
INCORRECT_AMOUNTInvoice amount does not match agreed terms
GOODS_NOT_RECEIVEDGoods or services not yet delivered
DUPLICATE_INVOICEInvoice already received for same transaction
INCORRECT_TAXTax calculation is incorrect
WRONG_BUYERInvoice not addressed to correct entity
OTHEROther reason — provide detail in rejection_note
curl -X POST "https://api.e-afrinvoice.com/api/app/invoice-action" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer YOUR_OAUTH2_ACCESS_TOKEN" \
  -H // token obtained via OAuth 2.0 client_credentials grant \
  -d '{
    "irn": "INV2549-4B2A4F6E-20260327",
    "action": "REJECT",
    "buyer_tin": "33577312-0001",
    "rejection_reason_code": "INCORRECT_AMOUNT",
    "rejection_note": "Amount mismatch with PO-2026-0041"
  }'
$payload = [
  "irn" => "INV2549-4B2A4F6E-20260327",
  "action" => "REJECT",
  "buyer_tin" => "33577312-0001",
  "rejection_reason_code" => "INCORRECT_AMOUNT",
  "rejection_note" => "Amount mismatch with PO-2026-0041"
];

$ch = curl_init("https://api.e-afrinvoice.com/api/app/invoice-action");
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($payload));
curl_setopt($ch, CURLOPT_HTTPHEADER, [
  "Content-Type: application/json",
  "Authorization: Bearer YOUR_OAUTH2_ACCESS_TOKEN",
  // token obtained via OAuth 2.0 client_credentials grant
]);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
echo curl_exec($ch);
curl_close($ch);
import axios from "axios";

const res = await axios.post(
  "https://api.e-afrinvoice.com/api/app/invoice-action",
  {
    irn: "INV2549-4B2A4F6E-20260327",
    action: "REJECT",
    buyer_tin: "33577312-0001",
    rejection_reason_code: "INCORRECT_AMOUNT",
    rejection_note: "Amount mismatch with PO-2026-0041"
  },
  { headers: { "Authorization": "Bearer YOUR_OAUTH2_ACCESS_TOKEN", // OAuth 2.0 bearer token } }
);
console.log(res.data);
import requests

r = requests.post(
  "https://api.e-afrinvoice.com/api/app/invoice-action",
  json={
    "irn": "INV2549-4B2A4F6E-20260327",
    "action": "REJECT",
    "buyer_tin": "33577312-0001",
    "rejection_reason_code": "INCORRECT_AMOUNT",
    "rejection_note": "Amount mismatch with PO-2026-0041"
  },
  headers={"Authorization": "Bearer YOUR_OAUTH2_ACCESS_TOKEN", // OAuth 2.0 bearer token}
)
print(r.json())

7. List Invoices

Retrieve a paginated list of invoices submitted or received under your APP account, filterable by TIN, status, date range, and invoice kind.

GET /api/app/invoices
GET /api/app/invoices?tin=32492774-0001&status=CLEARED&from=2026-03-01&to=2026-03-31&page=1&per_page=20
Authorization: Bearer YOUR_OAUTH2_ACCESS_TOKEN
# token obtained via OAuth 2.0 client_credentials grant
{
  "status": "success",
  "data": {
    "total": 142,
    "page": 1,
    "per_page": 20,
    "invoices": [
      {
        "irn": "INV2549-4B2A4F6E-20260327",
        "csid": "CSID-FIRS-20260327-XK9P2M7Q",
        "issue_date": "2026-03-27",
        "invoice_kind": "B2B",
        "firs_status": "CLEARED",
        "buyer_status": "PENDING_ACCEPTANCE",
        "supplier_tin": "32492774-0001",
        "buyer_tin": "33577312-0001",
        "payable_amount": 10750000,
        "currency": "NGN",
        "submitted_at": "2026-03-27T17:52:03Z"
      }
    ]
  }
}
Query ParamTypeRequiredDescription
tinstringNoFilter by supplier or buyer TIN
statusstringNoFilter by FIRS status: PENDING, CLEARED, REJECTED_BY_FIRS, REJECTED_BY_BUYER, ACCEPTED, CANCELLED
invoice_kindstringNoB2B, B2G, or B2C
fromstring (YYYY-MM-DD)NoStart date filter (issue_date)
tostring (YYYY-MM-DD)NoEnd date filter (issue_date)
pageintegerNoPage number (default: 1)
per_pageintegerNoResults per page (default: 20, max: 100)

8. Verify TIN

Validate a Taxpayer Identification Number (TIN) against the FIRS database before generating an invoice. This is a recommended pre-flight check to avoid submission failures due to invalid buyer or supplier TINs.

GET /api/app/verify-tin/{tin}
GET /api/app/verify-tin/33577312-0001
Authorization: Bearer YOUR_OAUTH2_ACCESS_TOKEN
# token obtained via OAuth 2.0 client_credentials grant
// Valid TIN:
{
  "status": "success",
  "data": {
    "tin": "33577312-0001",
    "valid": true,
    "business_name": "C-ADE CONSULTS",
    "einvoice_enabled": true,
    "state": "Lagos"
  }
}

// Invalid TIN:
{
  "status": "success",
  "data": {
    "tin": "99999999-0001",
    "valid": false,
    "einvoice_enabled": false
  }
}
curl -X GET "https://api.e-afrinvoice.com/api/app/verify-tin/33577312-0001" \
  -H "Authorization: Bearer YOUR_OAUTH2_ACCESS_TOKEN" \
  -H // token obtained via OAuth 2.0 client_credentials grant
$tin = "33577312-0001";
$ch = curl_init("https://api.e-afrinvoice.com/api/app/verify-tin/{$tin}");
curl_setopt($ch, CURLOPT_HTTPHEADER, [
  "Authorization: Bearer YOUR_OAUTH2_ACCESS_TOKEN",
  // token obtained via OAuth 2.0 client_credentials grant
]);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
echo curl_exec($ch);
curl_close($ch);
import axios from "axios";

const tin = "33577312-0001";
const res = await axios.get(
  `https://api.e-afrinvoice.com/api/app/verify-tin/${tin}`,
  { headers: { "Authorization": "Bearer YOUR_OAUTH2_ACCESS_TOKEN", // OAuth 2.0 bearer token } }
);
console.log(res.data);
import requests

tin = "33577312-0001"
r = requests.get(
  f"https://api.e-afrinvoice.com/api/app/verify-tin/{tin}",
  headers={"Authorization": "Bearer YOUR_OAUTH2_ACCESS_TOKEN", // OAuth 2.0 bearer token}
)
print(r.json())

9. Cancel Invoice / Issue Credit Note

Cancel a previously cleared invoice by submitting a credit note linked to the original IRN. Per FIRS rules, direct deletion of cleared invoices is not permitted; cancellation is achieved through a FIRS-compliant credit note.

POST /api/app/cancel-invoice
POST /api/app/cancel-invoice
Content-Type: application/json
Authorization: Bearer YOUR_OAUTH2_ACCESS_TOKEN
# token obtained via OAuth 2.0 client_credentials grant

{
  "original_irn": "INV2549-4B2A4F6E-20260327",
  "credit_note_irn": "CN2550-4B2A4F6E-20260328",
  "business_id": "6389237e-4f6e-4371-aece-aea5d2536df3",
  "issue_date": "2026-03-28",
  "cancellation_reason": "Goods returned by buyer",
  "document_currency_code": "NGN",
  "legal_monetary_total": {
    "line_extension_amount": 10000000,
    "tax_exclusive_amount": 10000000,
    "tax_inclusive_amount": 10750000,
    "payable_amount": 10750000
  }
}
{
  "status": "success",
  "message": "Invoice cancelled. Credit note submitted to FIRS.",
  "data": {
    "original_irn": "INV2549-4B2A4F6E-20260327",
    "credit_note_irn": "CN2550-4B2A4F6E-20260328",
    "credit_note_csid": "CSID-FIRS-20260328-CN9P2M7Q",
    "firs_status": "CANCELLED",
    "cancelled_at": "2026-03-28T10:05:33Z"
  }
}
FieldTypeRequiredDescription
original_irnstringYesIRN of the invoice being cancelled
credit_note_irnstringYesNew IRN for the credit note (follow same IRN pattern)
business_idstring (UUID)YesSupplier's business UUID
issue_datestring (YYYY-MM-DD)YesCredit note issue date
cancellation_reasonstringYesHuman-readable reason for cancellation
document_currency_codestringYesCurrency code (e.g. NGN)
legal_monetary_totalobjectYesMonetary totals mirroring the original invoice for full cancellation; partial for partial credit
Note: The invoice_type_code for credit notes is 381. For debit notes, use 383. These are automatically set by the endpoint.
💡 APP Integration Workflow Summary
  1. Get OAuth2 Token — exchange client_id + client_secret for bearer token via /api/app/oauth/token
  2. Register Taxpayer — onboard each business under your APP via /api/app/register-taxpayer
  3. Issue Certificate — request SHA-256 + ECDSA / JAdES certificate for SI's signing key via /api/app/certificates/request
  4. Verify TIN — pre-validate buyer TIN before invoice creation via /api/app/verify-tin/{tin}
  5. Generate IRN — client-side: {invoiceNo}-{SERVICE_ID}-{YYYYMMDD}
  6. Submit & Validate Invoice — APP validates schema, JAdES signature, and cryptographic stamp, then relays to NRS via /api/app/submit-invoice; receive CSID on clearance
  7. Confirm Status — poll /api/app/confirm-irn/{irn} to track NRS & buyer status
  8. Buyer Downloads — buyer APP retrieves via /api/app/download-invoice/{irn}
  9. Buyer Action — accept/reject within 72 hours via /api/app/invoice-action
  10. Cancel if needed — issue credit note via /api/app/cancel-invoice

Error Codes

The API uses conventional HTTP response codes to indicate the success or failure of a request. In general:

  • Codes in the 2xx range indicate success.
  • Codes in the 4xx range indicate client-side errors (e.g., invalid data, missing fields).
  • Codes in the 5xx range indicate server-side errors (contact support if persistent).
CodeStatusDescription
200OKRequest succeeded. Response includes requested data.
400Bad RequestMalformed JSON, missing required fields, or invalid data format.
401UnauthorizedMissing or invalid authentication credentials.
403ForbiddenYour account lacks permission to access this resource.
404Not FoundThe requested endpoint or resource does not exist.
422Unprocessable EntitySemantic errors (e.g., IRN format invalid, TIN mismatch, FIRS validation failed).
500Internal Server ErrorAn unexpected error occurred on our server. Retry or contact support.
502/503Service UnavailableFIRS gateway is temporarily unreachable. Implement retry logic with exponential backoff.

Error Response Format

{
  "status": "error",
  "message": "Validation failed: IRN must follow pattern {invoiceNumber}-{SERVICE_ID}-{YYYYMMDD}",
  "code": 400
}