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
Authentication
Authenticate using email & password to receive access_token and access_secret.
LIVEPOST /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"
}
| Field | Required | Description |
|---|---|---|
| Yes | User login email | |
| password | Yes | User login password |
System Integrator API Endpoints
Generate IRN
Client-side IRN formation pattern (server only receives the resulting IRN).
LOCALClient: generate IRN string
{invoiceNumber}-{FIRS_SERVICE_ID}-{YYYYMMDD}
e.g. INV2549-4B2A4F6E-20260327
INV2549-4B2A4F6E-20260327
| Field | Required | Description |
|---|---|---|
| invoiceNumber | Yes | Alphanumeric only (no spaces/special chars) |
| FIRS_SERVICE_ID | Yes | Your assigned service id from FIRS |
| invoiceDate | Yes | YYYY-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
}
}
}
| Header | Value / Notes |
|---|---|
| Content-Type | application/json |
| x-api-key | Backend injects when proxying to FIRS (do not expose) |
| x-api-secret | Backend 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"
}
| Field | Type | Required | Description |
|---|---|---|---|
| irn | string | Yes | 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.
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.
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.
/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"
}
}
| Field | Type | Required | Description |
|---|---|---|---|
| tin | string | Yes | Taxpayer Identification Number (TIN) — must be pre-enabled on the FIRS MBS portal |
| business_name | string | Yes | Legal registered business name |
| string | Yes | Primary contact email for the taxpayer | |
| telephone | string | Yes | Contact phone number in international format |
| business_description | string | No | Short description of the business activity |
| postal_address | object | Yes | Business address — see Address Object below |
| service_id | string | Yes | FIRS-assigned Service ID for the taxpayer |
Address Object
| Field | Type | Required | Description |
|---|---|---|---|
| street_name | string | Yes | Street address |
| city_name | string | Yes | City |
| postal_zone | string | Yes | Postal / ZIP code |
| lga | string | Yes | LGA code (FIRS reference) |
| state | string | Yes | State code (FIRS reference) |
| country | string | Yes | ISO 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"
}
}
| Field | Type | Required | Description |
|---|---|---|---|
| business_id | string (UUID) | Yes | Taxpayer's business UUID from registration |
| irn | string | Yes | Pre-generated IRN: {invoiceNo}-{SERVICE_ID}-{YYYYMMDD} |
| issue_date | string (YYYY-MM-DD) | Yes | Invoice issue date |
| invoice_type_code | string | Yes | FIRS invoice type: 380 (Commercial Invoice), 381 (Credit Note), 383 (Debit Note), 396 (Factored Invoice) |
| invoice_kind | string | Yes | B2B or B2G for pre-clearance; B2C for near-real-time reporting |
| accounting_supplier_party | object | Yes | Supplier details including TIN, name, address |
| accounting_customer_party | object | Yes | Buyer details including TIN, name, address |
| tax_total | array | Yes | VAT and other tax totals with subtotals |
| legal_monetary_total | object | Yes | Invoice monetary totals |
| invoice_line | array | Yes | Line items with HSN code, quantity, price, tax |
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"
}
}
| Param | Type | Required | Description |
|---|---|---|---|
| irn | string (path) | Yes | The Invoice Reference Number to query |
FIRS Status Values
| Status | Meaning |
|---|---|
PENDING | Submitted; awaiting FIRS validation |
CLEARED | FIRS validated and issued CSID |
REJECTED_BY_FIRS | Failed FIRS validation — correct and resubmit |
REJECTED_BY_BUYER | Buyer rejected within 72-hour window |
ACCEPTED | Buyer confirmed acceptance |
CANCELLED | Invoice 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" }
}
]
}
}
| Param | Type | Required | Description |
|---|---|---|---|
| irn | string (path) | Yes | The Invoice Reference Number to download |
| format | string (query) | No | Response format: json (default) or pdf |
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"
}
}
| Field | Type | Required | Description |
|---|---|---|---|
| irn | string | Yes | Invoice Reference Number |
| action | string | Yes | ACCEPT or REJECT |
| buyer_tin | string | Yes | TIN of the buyer performing the action |
| rejection_reason_code | string | If REJECT | Standardized rejection code (see below) |
| rejection_note | string | No | Human-readable rejection explanation |
Rejection Reason Codes
| Code | Description |
|---|---|
INCORRECT_AMOUNT | Invoice amount does not match agreed terms |
GOODS_NOT_RECEIVED | Goods or services not yet delivered |
DUPLICATE_INVOICE | Invoice already received for same transaction |
INCORRECT_TAX | Tax calculation is incorrect |
WRONG_BUYER | Invoice not addressed to correct entity |
OTHER | Other 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 Param | Type | Required | Description |
|---|---|---|---|
| tin | string | No | Filter by supplier or buyer TIN |
| status | string | No | Filter by FIRS status: PENDING, CLEARED, REJECTED_BY_FIRS, REJECTED_BY_BUYER, ACCEPTED, CANCELLED |
| invoice_kind | string | No | B2B, B2G, or B2C |
| from | string (YYYY-MM-DD) | No | Start date filter (issue_date) |
| to | string (YYYY-MM-DD) | No | End date filter (issue_date) |
| page | integer | No | Page number (default: 1) |
| per_page | integer | No | Results 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"
}
}
| Field | Type | Required | Description |
|---|---|---|---|
| original_irn | string | Yes | IRN of the invoice being cancelled |
| credit_note_irn | string | Yes | New IRN for the credit note (follow same IRN pattern) |
| business_id | string (UUID) | Yes | Supplier's business UUID |
| issue_date | string (YYYY-MM-DD) | Yes | Credit note issue date |
| cancellation_reason | string | Yes | Human-readable reason for cancellation |
| document_currency_code | string | Yes | Currency code (e.g. NGN) |
| legal_monetary_total | object | Yes | Monetary totals mirroring the original invoice for full cancellation; partial for partial credit |
invoice_type_code for credit notes is 381.
For debit notes, use 383. These are automatically set by the endpoint.
💡 APP Integration Workflow Summary
- Get OAuth2 Token — exchange
client_id+client_secretfor bearer token via/api/app/oauth/token - Register Taxpayer — onboard each business under your APP via
/api/app/register-taxpayer - Issue Certificate — request SHA-256 + ECDSA / JAdES certificate for SI's signing key via
/api/app/certificates/request - Verify TIN — pre-validate buyer TIN before invoice creation via
/api/app/verify-tin/{tin} - Generate IRN — client-side:
{invoiceNo}-{SERVICE_ID}-{YYYYMMDD} - Submit & Validate Invoice — APP validates schema, JAdES signature, and cryptographic stamp, then relays to NRS via
/api/app/submit-invoice; receive CSID on clearance - Confirm Status — poll
/api/app/confirm-irn/{irn}to track NRS & buyer status - Buyer Downloads — buyer APP retrieves via
/api/app/download-invoice/{irn} - Buyer Action — accept/reject within 72 hours via
/api/app/invoice-action - 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
2xxrange indicate success. - Codes in the
4xxrange indicate client-side errors (e.g., invalid data, missing fields). - Codes in the
5xxrange indicate server-side errors (contact support if persistent).
| Code | Status | Description |
|---|---|---|
200 | OK | Request succeeded. Response includes requested data. |
400 | Bad Request | Malformed JSON, missing required fields, or invalid data format. |
401 | Unauthorized | Missing or invalid authentication credentials. |
403 | Forbidden | Your account lacks permission to access this resource. |
404 | Not Found | The requested endpoint or resource does not exist. |
422 | Unprocessable Entity | Semantic errors (e.g., IRN format invalid, TIN mismatch, FIRS validation failed). |
500 | Internal Server Error | An unexpected error occurred on our server. Retry or contact support. |
502/503 | Service Unavailable | FIRS 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
}