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
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"
}
Field Required Description
email Yes User login email
password Yes User login password
curl -X POST "https://your-backend.example.com/api/login" \
  -H "Content-Type: application/json" \
  -d '{
        "email": "user@example.com",
        "password": "yourpassword"
      }'
$payload = [
  "email" => "user@example.com",
  "password" => "yourpassword"
];

$curl = curl_init();
curl_setopt_array($curl, [
  CURLOPT_URL => "https://your-backend.example.com/api/login",
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_POST => true,
  CURLOPT_HTTPHEADER => ["Content-Type: application/json"],
  CURLOPT_POSTFIELDS => json_encode($payload)
]);

$response = curl_exec($curl);
curl_close($curl);

echo $response;
import axios from "axios";

axios.post("https://your-backend.example.com/api/login", {
  email: "user@example.com",
  password: "yourpassword"
})
.then(res => console.log(res.data))
.catch(err => console.error(err));
import requests

payload = {
    "email": "user@example.com",
    "password": "yourpassword"
}

res = requests.post(
    "https://your-backend.example.com/api/login",
    json=payload
)

print(res.json())

System Integrator API Endpoints

Generate IRN

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

LOCAL Client: generate IRN string
// IRN format (client-side)
{invoiceNumber}-{FIRS_SERVICE_ID}-{YYYYMMDD}
INV001-SERVICEID-20251005
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)
// Client should produce IRN and send that string to backend endpoints below.
// (No server-side call required for generation itself)

Validate Invoice

Validate invoice payload with FIRS via your backend proxy.

POST /api/validate-invoice
{
  "business_id": "6389237e-4f6e-4371-aece-aea5d2536df3",
  "irn": "INV001-SERVICEID-20251005",
  "issue_date": "2025-10-05",
  "due_date": "2025-10-05",
  "issue_time": "13:45:00",
  "invoice_type_code": "396",
  "payment_status": "PENDING",
  "note": "Optional notes",
  "tax_point_date": "2025-10-05",
  "document_currency_code": "NGN",
  "tax_currency_code": "NGN",
  "accounting_cost": "100232 NGN",
  "invoice_delivery_period": {
    "start_date": "2025-10-05",
    "end_date": "2025-10-25"
  },
  "order_reference": "",
  "accounting_supplier_party": {
    "party_name": "Supplier Ltd",
    "tin": "32492774-0001",
    "email": "developer@afrinvoice.com",
    "telephone": "+2348026664444",
    "postal_address": {
      "street_name": "Supplier address",
      "city_name": "ANTHONY",
      "postal_zone": "100232",
      "lga": "70",
      "state": "4",
      "country": "NG"
    }
  },
  "accounting_customer_party": {
    "party_name": "Customer Inc",
    "tin": "33577312-0001",
    "email": "mark@markodenore.com",
    "telephone": "+2348026665555",
    "postal_address": {
      "street_name": "Customer address",
      "city_name": "ANTHONY",
      "postal_zone": "100232",
      "lga": "70",
      "state": "4",
      "country": "NG"
    }
  },
  "tax_total": [
    {
      "tax_amount": 250,
      "tax_subtotal": [
        {
          "taxable_amount": 2500,
          "tax_amount": 250,
          "tax_category": {
            "id": "LOCAL_SALES_TAX",
            "percent": 10
          }
        }
      ]
    }
  ],
  "legal_monetary_total": {
    "line_extension_amount": 2500,
    "tax_exclusive_amount": 2500,
    "tax_inclusive_amount": 2750,
    "payable_amount": 2750
  },
  "invoice_line": [
    {
      "hsn_code": "CC-001",
      "product_category": "Consulting Services",
      "discount_rate": 0,
      "fee_rate": 10,
      "invoiced_quantity": 1,
      "line_extension_amount": 2500,
      "item": {
        "name": "Service A",
        "description": "Consulting",
        "sellers_item_identification": "item-1"
      },
      "price": {
        "price_amount": 2500,
        "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.

curl -X POST https://yourdomain.com/api/validate-invoice \
  -H "Content-Type: application/json" \
  -d '{
    "business_id": "6389237e-4f6e-4371-aece-aea5d2536df3",
    "irn": "INV123-147-20251012",
    "issue_date": "2025-10-12",
    "due_date": "2025-10-12",
    "issue_time": "12:05:22",
    "invoice_type_code": "396",
    "payment_status": "PENDING",
    "note": "",
    "tax_point_date": "2025-10-12",
    "document_currency_code": "NGN",
    "tax_currency_code": "NGN",
    "accounting_cost": "100232 NGN",
    "buyer_reference": "",
    "invoice_delivery_period": {
      "start_date": "2025-10-12",
      "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": "Business Name",
      "tin": "32492774-0001",
      "email": "developer@afrinvoice.com",
      "telephone": "+2348026664444",
      "business_description": "Consulting services",
      "postal_address": {
        "street_name": "Business Address",
        "city_name": "ANTHONY",
        "postal_zone": "100232",
        "lga": "70",
        "state": "4",
        "country": "NG"
      }
    },
    "accounting_customer_party": {
      "party_name": "Customer Name",
      "tin": "33577312-0001",
      "email": "mark@markodenore.com",
      "telephone": "+2348026665555",
      "business_description": null,
      "postal_address": {
        "street_name": "Customer Address",
        "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": "2025-10-12",
    "payment_means": null,
    "payment_terms_note": null,
    "allowance_charge": [
      { "charge_indicator": true,  "amount": 800.6 },
      { "charge_indicator": false, "amount": 100.50 }
    ],
    "tax_total": [
      {
        "tax_amount": 250.00,
        "tax_subtotal": [
          {
            "taxable_amount": 1500.00,
            "tax_amount": 250.00,
            "tax_category": {
              "id": "LOCAL_SALES_TAX",
              "percent": 17.5
            }
          }
        ]
      }
    ],
    "legal_monetary_total": {
      "line_extension_amount": 1500.00,
      "tax_exclusive_amount": 1400.00,
      "tax_inclusive_amount": 1650.00,
      "payable_amount": 1650.00
    },
    "invoice_line": [
      {
        "hsn_code": "CC-001",
        "product_category": "Consulting Services",
        "discount_rate": 10,
        "discount_amount": 50.00,
        "fee_rate": 17.5,
        "fee_amount": 87.50,
        "invoiced_quantity": 2,
        "line_extension_amount": 1000.00,
        "item": {
          "name": "Product A",
          "description": "my wonderful product",
          "sellers_item_identification": "item-1"
        },
        "price": {
          "price_amount": 500.00,
          "base_quantity": 1,
          "price_unit": "NGN per 1"
        }
      }
    ]
  }'
        
<?php
$payload = [
  "business_id" => "6389237e-4f6e-4371-aece-aea5d2536df3",
  "irn" => "INV123-147-20251012",
  "issue_date" => "2025-10-12",
  "due_date" => "2025-10-12",
  "issue_time" => "12:05:22",
  "invoice_type_code" => "396",
  "payment_status" => "PENDING",
  "note" => "",
  "tax_point_date" => "2025-10-12",
  "document_currency_code" => "NGN",
  "tax_currency_code" => "NGN",
  "accounting_cost" => "100232 NGN",
  "buyer_reference" => "",
  "invoice_delivery_period" => [
    "start_date" => "2025-10-12",
    "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" => "Business Name",
    "tin" => "32492774-0001",
    "email" => "developer@afrinvoice.com",
    "telephone" => "+2348026664444",
    "business_description" => "Consulting services",
    "postal_address" => [
      "street_name" => "Business Address",
      "city_name" => "ANTHONY",
      "postal_zone" => "100232",
      "lga" => "70",
      "state" => "4",
      "country" => "NG"
    ]
  ],
  "accounting_customer_party" => [
    "party_name" => "Customer Name",
    "tin" => "33577312-0001",
    "email" => "mark@markodenore.com",
    "telephone" => "+2348026665555",
    "business_description" => null,
    "postal_address" => [
      "street_name" => "Customer Address",
      "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" => "2025-10-12",
  "payment_means" => null,
  "payment_terms_note" => null,
  "allowance_charge" => [
    ["charge_indicator" => true, "amount" => 800.6],
    ["charge_indicator" => false, "amount" => 100.50]
  ],
  "tax_total" => [
    [
      "tax_amount" => 250.00,
      "tax_subtotal" => [
        [
          "taxable_amount" => 1500.00,
          "tax_amount" => 250.00,
          "tax_category" => [
            "id" => "LOCAL_SALES_TAX",
            "percent" => 17.5
          ]
        ]
      ]
    ]
  ],
  "legal_monetary_total" => [
    "line_extension_amount" => 1500.00,
    "tax_exclusive_amount" => 1400.00,
    "tax_inclusive_amount" => 1650.00,
    "payable_amount" => 1650.00
  ],
  "invoice_line" => [
    [
      "hsn_code" => "CC-001",
      "product_category" => "Consulting Services",
      "discount_rate" => 10,
      "discount_amount" => 50.00,
      "fee_rate" => 17.5,
      "fee_amount" => 87.50,
      "invoiced_quantity" => 2,
      "line_extension_amount" => 1000.00,
      "item" => [
        "name" => "Product A",
        "description" => "my wonderful product",
        "sellers_item_identification" => "item-1"
      ],
      "price" => [
        "price_amount" => 500.00,
        "base_quantity" => 1,
        "price_unit" => "NGN per 1"
      ]
    ]
  ]
];

$ch = curl_init("https://yourdomain.com/api/validate-invoice");
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($payload));
curl_setopt($ch, CURLOPT_HTTPHEADER, ["Content-Type: application/json"]);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

$response = curl_exec($ch);
curl_close($ch);

echo $response;
?>
        
import axios from "axios";

axios.post("https://yourdomain.com/api/validate-invoice", {
  business_id: "6389237e-4f6e-4371-aece-aea5d2536df3",
  irn: "INV123-147-20251012",
  issue_date: "2025-10-12",
  due_date: "2025-10-12",
  issue_time: "12:05:22",
  invoice_type_code: "396",
  payment_status: "PENDING",
  note: "",
  tax_point_date: "2025-10-12",
  document_currency_code: "NGN",
  tax_currency_code: "NGN",
  accounting_cost: "100232 NGN",
  buyer_reference: "",
  invoice_delivery_period: {
    start_date: "2025-10-12",
    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: "Business Name",
    tin: "32492774-0001",
    email: "developer@afrinvoice.com",
    telephone: "+2348026664444",
    business_description: "Consulting services",
    postal_address: {
      street_name: "Business Address",
      city_name: "ANTHONY",
      postal_zone: "100232",
      lga: "70",
      state: "4",
      country: "NG"
    }
  },
  accounting_customer_party: {
    party_name: "Customer Name",
    tin: "33577312-0001",
    email: "mark@markodenore.com",
    telephone: "+2348026665555",
    business_description: null,
    postal_address: {
      street_name: "Customer Address",
      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: "2025-10-12",
  payment_means: null,
  payment_terms_note: null,
  allowance_charge: [
    { charge_indicator: true, amount: 800.6 },
    { charge_indicator: false, amount: 100.50 }
  ],
  tax_total: [
    {
      tax_amount: 250.0,
      tax_subtotal: [
        {
          taxable_amount: 1500.0,
          tax_amount: 250.0,
          tax_category: {
            id: "LOCAL_SALES_TAX",
            percent: 17.5
          }
        }
      ]
    }
  ],
  legal_monetary_total: {
    line_extension_amount: 1500.0,
    tax_exclusive_amount: 1400.0,
    tax_inclusive_amount: 1650.0,
    payable_amount: 1650.0
  },
  invoice_line: [
    {
      hsn_code: "CC-001",
      product_category: "Consulting Services",
      discount_rate: 10,
      discount_amount: 50.0,
      fee_rate: 17.5,
      fee_amount: 87.5,
      invoiced_quantity: 2,
      line_extension_amount: 1000.0,
      item: {
        name: "Product A",
        description: "my wonderful product",
        sellers_item_identification: "item-1"
      },
      price: {
        price_amount: 500.0,
        base_quantity: 1,
        price_unit: "NGN per 1"
      }
    }
  ]
})
.then(res => console.log(res.data))
.catch(err => console.error(err));
        
import requests

payload = {
  "business_id": "6389237e-4f6e-4371-aece-aea5d2536df3",
  "irn": "INV123-147-20251012",
  "issue_date": "2025-10-12",
  "due_date": "2025-10-12",
  "issue_time": "12:05:22",
  "invoice_type_code": "396",
  "payment_status": "PENDING",
  "note": "",
  "tax_point_date": "2025-10-12",
  "document_currency_code": "NGN",
  "tax_currency_code": "NGN",
  "accounting_cost": "100232 NGN",
  "buyer_reference": "",
  "invoice_delivery_period": {
    "start_date": "2025-10-12",
    "end_date": "2025-10-25"
  },
  "order_reference": "",
  "billing_reference": None,
  "dispatch_document_reference": None,
  "receipt_document_reference": None,
  "originator_document_reference": None,
  "contract_document_reference": None,
  "additional_document_reference": None,
  "accounting_supplier_party": {
    "party_name": "Business Name",
    "tin": "32492774-0001",
    "email": "developer@afrinvoice.com",
    "telephone": "+2348026664444",
    "business_description": "Consulting services",
    "postal_address": {
      "street_name": "Business Address",
      "city_name": "ANTHONY",
      "postal_zone": "100232",
      "lga": "70",
      "state": "4",
      "country": "NG"
    }
  },
  "accounting_customer_party": {
    "party_name": "Customer Name",
    "tin": "33577312-0001",
    "email": "mark@markodenore.com",
    "telephone": "+2348026665555",
    "business_description": None,
    "postal_address": {
      "street_name": "Customer Address",
      "city_name": "ANTHONY",
      "postal_zone": "100232",
      "lga": "70",
      "state": "4",
      "country": "NG"
    }
  },
  "payee_party": None,
  "bill_party": None,
  "ship_party": None,
  "tax_representative_party": None,
  "actual_delivery_date": "2025-10-12",
  "payment_means": None,
  "payment_terms_note": None,
  "allowance_charge": [
    { "charge_indicator": True, "amount": 800.6 },
    { "charge_indicator": False, "amount": 100.50 }
  ],
  "tax_total": [
    {
      "tax_amount": 250.00,
      "tax_subtotal": [
        {
          "taxable_amount": 1500.00,
          "tax_amount": 250.00,
          "tax_category": {
            "id": "LOCAL_SALES_TAX",
            "percent": 17.5
          }
        }
      ]
    }
  ],
  "legal_monetary_total": {
    "line_extension_amount": 1500.00,
    "tax_exclusive_amount": 1400.00,
    "tax_inclusive_amount": 1650.00,
    "payable_amount": 1650.00
  },
  "invoice_line": [
    {
      "hsn_code": "CC-001",
      "product_category": "Consulting Services",
      "discount_rate": 10,
      "discount_amount": 50.00,
      "fee_rate": 17.5,
      "fee_amount": 87.50,
      "invoiced_quantity": 2,
      "line_extension_amount": 1000.00,
      "item": {
        "name": "Product A",
        "description": "my wonderful product",
        "sellers_item_identification": "item-1"
      },
      "price": {
        "price_amount": 500.00,
        "base_quantity": 1,
        "price_unit": "NGN per 1"
      }
    }
  ]
}

response = requests.post("https://yourdomain.com/api/validate-invoice", json=payload)
print(response.json())
        

Generate QR Code

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

POST /api/generate-qr
{
  "irn": "INV001-SERVICEID-20251005"
}
{
    "success": true,
    "qrCodeDataUrl": "data:image/png;base64,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",
    "encryptedBase64": "o24MzpmIqWG55j97118zzhHvRuV9pSxTsobReMpzqU2hUEeNVmQhUCH92a7yIEDRn+lLvjP5KAQ2Z1R1cZnrE/GRfmM1Jz08N0M9LARUdGQw1oWSTlKQy0FBC3zQaG8mxkdHBaO8DKexH0BISJyFEugVqP6/2nZUtSl3MxB0BO3yncOkSTo8aiLz60abNaV44kZBainwBT89ikN2GTn73VKAd34iBSpmDmHFtvj54g+bDCI+r8ivJO9nT6gPE/QdMUOz6rtCbHWgxu2rvhJfEkk10R178GQxwS6+xfcrS2wW6LmpfPqtUUQDd+oTV6R0yDkmd7iiJhhheyNUHI4uGA==",
    "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.

curl -X POST "https://your-backend.example.com/api/generate-qr" \
  -H "Content-Type: application/json" \
  -d '{
        "irn": "INV001-SERVICEID-20251005"
      }'
$payload = [
  "irn" => "INV001-SERVICEID-20251005"
];

$curl = curl_init();
curl_setopt_array($curl, [
  CURLOPT_URL => "https://your-backend.example.com/api/generate-qr",
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_POST => true,
  CURLOPT_HTTPHEADER => ["Content-Type: application/json"],
  CURLOPT_POSTFIELDS => json_encode($payload)
]);

$response = curl_exec($curl);
curl_close($curl);

echo $response;
import axios from "axios";

const payload = {
  irn: "INV001-SERVICEID-20251005"
};

axios
  .post("https://your-backend.example.com/api/generate-qr", payload)
  .then((res) => console.log(res.data))
  .catch((err) => console.error(err));
import requests

payload = {
    "irn": "INV001-SERVICEID-20251005"
}

r = requests.post(
    "https://your-backend.example.com/api/generate-qr",
    json=payload
)

print(r.json())

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

Integration Examples

Below are end-to-end examples showing how to authenticate, validate an invoice, and generate a FIRS-compliant QR code.

1. Full Invoice Workflow (cURL)

# Step 1: Login to obtain tokens
curl -X POST https://api.afrinvoice.com/api/login \
  -H "Content-Type: application/json" \
  -d '{"email":"integrator@example.com","password":"secure123"}'

# Response:
# { "access_token": "abc...", "access_secret": "xyz..." }

# Step 2: Validate invoice
curl -X POST https://api.afrinvoice.com/api/validate-invoice \
  -H "Content-Type: application/json" \
  -d '{
    "business_id": "6389237e-4f6e-4371-aece-aea5d2536df3",
    "irn": "INV2025-147-20251123",
    "issue_date": "2025-11-23",
    "invoice_line": [...],
    "legal_monetary_total": {...},
    "accounting_supplier_party": {...},
    "accounting_customer_party": {...}
  }'

# Step 3: Generate QR code
curl -X POST https://api.afrinvoice.com/api/generate-qr \
  -H "Content-Type: application/json" \
  -d '{"irn":"INV2025-147-20251123"}'

2. Python Integration Snippet

import requests

# Authenticate
auth = requests.post("https://api.afrinvoice.com/api/login", json={
    "email": "integrator@example.com",
    "password": "secure123"
}).json()

# Validate
invoice_data = { /* full invoice payload as per /validate-invoice spec */ }
validation = requests.post(
    "https://api.afrinvoice.com/api/validate-invoice",
    json=invoice_data
).json()

if validation.get("data", {}).get("ok"):
    # Generate QR
    qr = requests.post("https://api.afrinvoice.com/api/generate-qr", json={
        "irn": invoice_data["irn"]
    }).json()
    print("QR Base64:", qr["qrCodeDataUrl"])
💡 Tip: Always generate the IRN client-side using the pattern {invoiceNumber}-{FIRS_SERVICE_ID}-{YYYYMMDD} before sending to /validate-invoice.