FIRS E-Invoicing


FIRS E-Invoicing Platform — Technical & API Documentation

Overview

The Interswitch FIRS E-Invoicing Platform facilitates the secure validation, signing, transmission, and reconciliation of sales invoices between taxpayers and the Federal Inland Revenue Service (FIRS). It supports both administrative and client-facing functions, exposes secure APIs for integration, and provides a 2-factor authentication (2FA) enabled web application for system interaction.


Core Components

Web API

The API layer is built on .NET with OAuth 2.0 authentication and role-based authorization.

Token Generation

Endpoint: POST /Api/SwitchTax/Token

{
  "ClientId": "TEST001",
  "ClientSecret": "RTR56457%4k"
}

Sales Data API

Validates IRN format, invoice payload against schema, digitally signs the invoice, stores the signed version for audit, and transmits to FIRS.

Endpoint: POST /Api/SwitchTax/postInvoice

Sales Invoice Status Update API

Updates invoice status and exposes the updated status.

Endpoint: PATCH /api/v1/invoice/update/{TEST_IRN}

Webhook Endpoint

Receives invoice status events, sends acknowledgment, and updates the system.


Web Application

A secure, 2FA-enabled portal for admins and clients.

Admin View

  • Taxpayer Onboarding
  • Posted Invoices
  • FIRS Resources
  • Developer Console
  • Email Templates
  • User Roles

Client View

  • Profile
  • Posted Invoices
  • FIRS Resources
  • Developer Console
  • Reports

ERP/Accounting Software Customization

To facilitate seamless transmission of invoice data from an ERP/Accounting system to FIRS, the following customizations are required:

3.1 Mapping VAT Codes to FIRS Tax Categories

FIRS provides a standardized list of tax categories. Existing VAT/tax codes in the ERP must be mapped to these categories to ensure the correct tax type is included in the invoice payload.

3.2 Capturing Corporate Customer Tax Information

Corporate customer master data must capture the following fields:

  • Tax Identification Number (TIN)
  • Email Address
  • Phone Number

3.3 API Call Integration

When an invoice is posted in the ERP system:

  1. Invoice data is fetched automatically at posting time.
  2. Data is converted to JSON and transmitted to the e-invoicing sales endpoint.
  3. The middleware will:
  • Validate the Invoice Reference Number (IRN)
  • Validate invoice data against the FIRS schema
  • Digitally sign the invoice
  • Transmit the invoice to FIRS
  1. A response containing a QR Code is returned to the ERP for record-keeping and printing.

3.4 Invoice Printout Redesign

The invoice printout template must be redesigned to include the QR Code returned by FIRS to ensure all invoices are verifiable and compliant.


Data Flow Summary

  1. Onboarding: Admin generates Client ID/Secret and shares with taxpayer.
  2. Invoice Submission: Client submits invoices — validated, signed, and transmitted to FIRS.
  3. Status Updates: FIRS sends status events to the webhook; system updates and notifies all parties.

Security & Compliance

FeatureDetail
AuthenticationOAuth 2.0 with role-based access
Login Security2FA enabled for all logins
Data in TransitTLS 1.3
Data at RestDatabase encryption
AuditFull logging of all API calls and actions
ComplianceFIRS regulations and Nigerian data protection laws

API Reference

Authentication

Token Generation

POST /Api/SwitchTax/Token

Request

{
  "ClientId": "TEST001",
  "ClientSecret": "RTR56457%4k"
}

Response

{
  "Token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "token_type": "bearer",
  "expires_in": 3600
}

Use the returned Token as a Bearer Token in the Authorization header for all subsequent requests.


Post Invoice

POST /Api/SwitchTax/postInvoice

Invoice Header Fields

FieldNameTypeRequiredMax Length
business_idBusiness ID (UUID)StringYes36
irnInvoice Reference NumberStringYes50
invoice_kindInvoice Nature (B2B, B2C, B2G)StringNo3
issue_dateInvoice Issue DateDate (YYYY-MM-DD)Yes10
due_dateInvoice Due DateDate (YYYY-MM-DD)Yes10
issue_timeInvoice Issue TimeTime (HH:mm:ss)Yes8
invoice_type_codeInvoice Type CodeStringYes10
tax_point_dateTax Point DateDate (YYYY-MM-DD)Yes10
document_currency_codeDocument Currency CodeStringYes3
tax_currency_codeTax Currency CodeStringYes3
billing_reference.irnOriginal Invoice IRN (credit note only)StringYes50
billing_reference.issue_dateOriginal Invoice Issue Date (credit note only)Time (HH:mm:ss)Yes8

Supplier Party Fields (accounting_supplier_party)

FieldNameTypeRequiredMax Length
party_nameSupplier NameStringYes100
tinSupplier TINStringYes20
emailSupplier EmailStringYes100
telephoneSupplier TelephoneStringYes20
business_descriptionBusiness DescriptionStringNo255
postal_address.street_nameStreet NameStringYes150
postal_address.city_nameCity NameStringYes100
postal_address.postal_zonePostal CodeStringNo20
postal_address.countryCountry CodeStringYes2

Customer Party Fields (accounting_customer_party)

FieldNameTypeRequiredMax Length
party_nameCustomer NameStringYes100
tinCustomer TINStringYes20
emailCustomer EmailStringYes100
telephoneCustomer TelephoneStringYes20
business_descriptionBusiness DescriptionStringNo255
postal_address.street_nameStreet NameStringNo150
postal_address.city_nameCity NameStringYes100
postal_address.postal_zonePostal CodeStringNo20
postal_address.countryCountry CodeStringYes2

Invoice Line Fields (invoice_line[])

FieldNameTypeRequired
hsn_codeHSN CodeStringYes
product_categoryProduct CategoryStringYes
discount_rateDiscount RateDecimalNo
discount_amountDiscount AmountDecimalNo
fee_rateFee RateDecimalNo
fee_amountFee AmountDecimalNo
invoiced_quantityInvoiced QuantityDecimalYes
line_extension_amountLine Extension AmountDecimalYes
item.nameItem NameStringYes
item.descriptionItem DescriptionStringNo
item.sellers_item_identificationItem IDStringYes
price.price_amountPrice AmountDecimalYes
price.base_quantityBase QuantityDecimalYes
price.price_unitPrice UnitStringYes

Tax Total Fields (tax_total[])

FieldNameTypeRequired
tax_amountTotal Tax AmountDecimalYes
tax_subtotal[].taxable_amountTaxable AmountDecimalYes
tax_subtotal[].tax_amountTax AmountDecimalYes
tax_subtotal[].tax_category.idTax Category IDStringYes
tax_subtotal[].tax_category.percentTax Rate (%)DecimalYes

Legal Monetary Total Fields (legal_monetary_total)

FieldNameTypeRequired
line_extension_amountLine Extension TotalDecimalYes
tax_exclusive_amountTax Exclusive TotalDecimalYes
tax_inclusive_amountTax Inclusive TotalDecimalYes
payable_amountPayable AmountDecimalYes

Sample Invoice Request

{
  "business_id": "1c6eaf77-d0bd-455c-9c5c-500a3f1dbfb2",
  "irn": "NISW007611-6AFCD0BD-20250901",
  "invoice_kind": "B2B",
  "issue_date": "2025-09-01",
  "due_date": "2025-09-01",
  "issue_time": "13:34:34",
  "invoice_type_code": "396",
  "tax_point_date": "2025-09-01",
  "document_currency_code": "NGN",
  "tax_currency_code": "NGN",
  "accounting_supplier_party": {
    "party_name": "NG",
    "tin": "15631438-0242",
    "email": "[email protected]",
    "telephone": "+23416283888",
    "business_description": "Financial technology services",
    "postal_address": {
      "street_name": "Oko-Awo Street, Victoria Island",
      "city_name": "Lagos",
      "postal_zone": "80164",
      "country": "NG"
    }
  },
  "accounting_customer_party": {
    "party_name": "Sterling Bank Plc",
    "tin": "15631438-0242",
    "email": "[email protected]",
    "telephone": "+254712034397",
    "business_description": null,
    "postal_address": {
      "street_name": "",
      "city_name": "Victoria Island",
      "postal_zone": "",
      "country": "NG"
    }
  },
  "invoice_line": [
    {
      "hsn_code": "2TG27",
      "product_category": "3D Secure Monthly Acquiring Fee",
      "discount_rate": 0.00,
      "discount_amount": 0.00,
      "fee_rate": 0.0,
      "fee_amount": 0.0,
      "invoiced_quantity": 10.00,
      "line_extension_amount": 35000.00,
      "item": {
        "name": "3D Secure Monthly Acquiring Fee",
        "description": "10.00 Each at 3500.00 each",
        "sellers_item_identification": "2TG27"
      },
      "price": {
        "price_amount": 3500.00,
        "base_quantity": 1,
        "price_unit": "NGN per Each"
      }
    },
    {
      "hsn_code": "1CD02",
      "product_category": "CollegePAY",
      "discount_rate": 0.00,
      "discount_amount": 0.00,
      "fee_rate": 0.0,
      "fee_amount": 0.0,
      "invoiced_quantity": 3.00,
      "line_extension_amount": 12000.00,
      "item": {
        "name": "CollegePAY",
        "description": "3.00 Each at 4000.00 each",
        "sellers_item_identification": "1CD02"
      },
      "price": {
        "price_amount": 4000.00,
        "base_quantity": 1,
        "price_unit": "NGN per Each"
      }
    }
  ],
  "tax_total": [
    {
      "tax_amount": 2625.00,
      "tax_subtotal": [
        {
          "taxable_amount": 35000.00,
          "tax_amount": 2625.00,
          "tax_category": {
            "id": "STANDARD_VAT",
            "percent": 7.50
          }
        },
        {
          "taxable_amount": 12000.00,
          "tax_amount": 0.00,
          "tax_category": {
            "id": "ZERO_VAT",
            "percent": 0.00
          }
        }
      ]
    }
  ],
  "legal_monetary_total": {
    "line_extension_amount": 47000.00,
    "tax_exclusive_amount": 47000.00,
    "tax_inclusive_amount": 49625.00,
    "payable_amount": 49625.00
  }
}

Sample Credit Note Request

Same structure as an invoice, with the addition of billing_reference to link to the original invoice:

{
  "business_id": "1c6eaf77-d0bd-455c-9c5c-500a3f1dbfb2",
  "irn": "NISW007611-6AFCD0BD-20250901",
  "invoice_kind": "B2B",
  "issue_date": "2025-09-01",
  "billing_reference": [
    {
      "irn": "ITW001-E9E0C0D3-20250619",
      "issue_date": "2025-05-14"
    }
  ],
  "..."  : "...remaining fields same as invoice..."
}

Post Invoice Response

{
  "code": 201,
  "message": "Transmitted successfully",
  "data": {
    "IRN": "NISW-008634-6AFCD0BD-20251014",
    "PostingDateTime": "2025-10-19 22:28:24",
    "QRCodeData": "UkXGoG5AWjj..."
  }
}

Update Invoice Status

POST /UpdateStatus

Request

{
  "payment_status": "PAID",
  "reference": "payment_reference_or_note",
  "irn": "NISW008608-6AFCD0BD-20250930"
}

Response

{
  "invoiceId": "INV-2025-001",
  "status": "ACCEPTED",
  "timestamp": "2025-07-01T12:00:00Z"
}

Transmit Invoice

POST /transmit/{IRN}

Response

{
  "code": 200,
  "data": {
    "ok": true
  }
}

Post Queued Invoices

Transmits previously queued invoices for a given TIN within a date range.

Request

{
  "tin": "15631438-0242",
  "startDate": "2025-05-01",
  "endDate": "2025-12-31"
}

Response

{
  "code": "200",
  "message": "Transmitted successfully",
  "InvoicesPosted": [
    {
      "IRN": "ITW005-6AFCD0BD-20250730",
      "PostingDateTime": "2025-11-17 21:04:34",
      "QRCodeData": "Z4QjuUDN8CY..."
    },
    {
      "IRN": "ITW009-6AFCD0BD-20250730",
      "PostingDateTime": "2025-11-17 21:04:34",
      "QRCodeData": "PiiEpggiZF0..."
    }
  ]
}

Error Handling

All errors follow a standard structured format.

Invalid Token — 401 Unauthorized

{
  "error": "invalid_token",
  "error_description": "The access token is invalid or has expired."
}

Missing Required Field — 400 Bad Request

{
  "errorCode": "400",
  "errorMessage": "Validation Failed",
  "details": "CustomerTIN is required."
}

Schema Validation Failed — 422 Unprocessable Entity

{
  "errorCode": "422",
  "errorMessage": "Schema validation failed",
  "details": "InvoiceDate must be in YYYY-MM-DD format."
}

Internal Server Error — 500

{
  "errorCode": "500",
  "errorMessage": "An unexpected error occurred. Please try again later.",
  "supportId": "b7f4c7d2-82f3-4a3e-b9f5-0a12345xyz"
}

Duplicate IRN — 400

{
  "code": 400,
  "data": null,
  "message": "error has occurred",
  "error": {
    "id": "96b4a6dc-ab2c-403e-adec-8a4c0200d87e",
    "handler": "invoice_actions",
    "details": "unable to complete this operation at this time, kindly try again later",
    "public_message": "validation failed: we are unable to process your request. also confirm this is not a duplicate request"
  }
}

Invalid Business ID — 400

{
  "code": 400,
  "data": null,
  "message": "error has occurred",
  "error": {
    "id": "d64ba7c8-e62e-4b3a-aa55-12f49226fcad",
    "handler": "invoice_actions",
    "details": "invalid UUID length: 35",
    "public_message": "validation failed: we are unable to process your request. also confirm this is not a duplicate request"
  }
}

Invalid Tax Category — 400

{
  "code": 400,
  "data": null,
  "message": "error has occurred",
  "error": {
    "id": "a4f0d39d-5780-492f-9f0d-e11cc6acfad2",
    "handler": "invoice_actions",
    "details": "invoicerequest.invoice.taxtotal[0].taxsubtotal[0].taxcategory.id must be a valid tax category, refer to the invoice resource apis",
    "public_message": "validation failed: we are unable to process your request. also confirm this is not a duplicate request"
  }
}

Invalid Tax Point Date — 400

{
  "code": 400,
  "data": null,
  "message": "error has occurred",
  "error": {
    "id": "bbeaab9c-6a28-4ca6-b2fe-9bb82406f01a",
    "handler": "invoice_actions",
    "details": "invoicerequest.invoice.taxpointdate must be a valid date value yyyy-mm-dd (e.g: 2024-04-29)",
    "public_message": "validation failed: we are unable to process your request. also confirm this is not a duplicate request"
  }
}

Invalid Country Code — 400

{
  "code": 400,
  "data": null,
  "message": "error has occurred",
  "error": {
    "id": "2b2775ae-1b7b-4fb7-9124-dcac8e670996",
    "handler": "invoice_actions",
    "details": "invoicerequest.invoice.accountingsupplierparty.postaladdress.country must be a valid country code, refer to the invoice resource apis",
    "public_message": "validation failed: we are unable to process your request. also confirm this is not a duplicate request"
  }
}

Invalid TIN — 400

{
  "code": 400,
  "data": null,
  "message": "error has occurred",
  "error": {
    "id": "8172fe11-e6b7-4c10-af5c-57d3a920a832",
    "handler": "invoice_actions",
    "details": "invoicerequest.invoice.accountingcustomerparty.tin must be at least in length or value 5",
    "public_message": "validation failed: we are unable to process your request. also confirm this is not a duplicate request"
  }
}

Queued Invoice Errors

Invalid TIN

{
  "code": "400",
  "message": "Invalid TIN"
}

No Queued Invoices

{
  "code": "400",
  "message": "There are no queued invoices pending to be transmitted"
}

FIRS Not Reachable

{
  "code": "500",
  "message": "FIRS system is currently offline. Please try again later"
}

HTTP Status Code Reference

Status CodeMeaningTypical Scenarios
200 OKRequest successfulInvoice submitted, status updated, token generated
201 CreatedResource successfully createdNew invoice or client record created
400 Bad RequestInvalid input providedMissing required field, malformed JSON
401 UnauthorizedAuthentication failedInvalid or expired access token
403 ForbiddenAccess deniedClient lacks permission for the resource
404 Not FoundResource not foundInvalid endpoint or record not found
422 Unprocessable EntityValidation failedSchema or business rule validation error
429 Too Many RequestsRate limit exceededClient exceeded request quota
500 Internal Server ErrorUnexpected server errorUnhandled exception, service error
503 Service UnavailableService temporarily unavailableScheduled downtime or overload