Skip to main content

Introduction

Overview

The e-Invoicing API is an online backend service designed to streamline the process of issuing, managing, and processing tax payer's electronic invoices in compliance with the Federal Inland Revenue Services (FIRS) requirements. This API enables businesses to automate their invoicing workflows, ensuring efficient, accurate, and compliant financial transactions. This section defines the architectural requirements and API designs for the FIRS e-invoicing Service.

Sequence Diagram

Sequence Diagram

Authentication

Authentication will use an API Key and HMAC-SHA256 signature computed with the client’s secret in the request header. This ensures every request is uniquely signed and verified. Each API request must include the following headers:

  • X-API-Key: Identifies the client application.
  • X-API-Signature: HMAC-SHA256 signature generated from (request body + timestamp) using the client’s secret key.
  • X-API-Timestamp: ISO-20022 timestamp of the request (±5 min allowed) to prevent replay attacks.

If any of these headers are missing, invalid, or the timestamp is outside the allowed window, the API will return an appropriate error message from the server.

Sandbox BaseURL: https://firseinvoicedemo.etranzactng.com

Sample Header Request:

POST
curl -X POST
-H "X-API-Key: CLIENT_APP_001"
-H "X-API-Signature: 9f4e3a8d7c9b12345abcdef67890fedcba9876543210abcdef98765"
-H "X-API-Timestamp: 2025-09-28T12:30:00+01:00"
-H "Content-Type: application/json"
-d '{"key": "value"}'
'

Sample Code snippet In JavaScript, usable on postman:

const CryptoJs = require("crypto-js");
const clientSecret = "NcnXs3aK7IAPZiDKXp/qENaJ2Yf5XkE2ToSK1PFU+g0SRJR8J7c=";
const timestamp = new Date().toISOString().replace(/\.\d{3}Z$/, "Z");

let payload = "";

if (pm.request.body && pm.request.body.mode === "raw"){
payload = pm.request.body.raw.trim();
}
const message = payload + timestamp;
const hash = CryptoJs.HmacSHA256(message,clientSecret);
const signature = CryptoJs.enc.Base64.stringify(hash);

pm.environment.set("signature", signature);
pm.environment.set("timestamp",timestamp);

const myHeaders = new Headers();
myHeaders.append("x-api-key", "30181103155636");
myHeaders.append("x-api-signature", "BpPO2EyFmag4sl3gQjpmUdGrrghSS91sbBqiZVPn7zA=");
myHeaders.append("x-api-timestamp", "2025-11-17T12:50:05Z");
myHeaders.append("Content-Type", "application/json");

const raw = JSON.stringify({
"business_id": "8a94a32a-99b2-41ff-80ae-a0dd9a3c4626",
"invoice_number": "PZ32kgfNW015792",
"issuance_date": "20250731",
"service_id": "F40299B2"
});

const requestOptions = {
method: "POST",
headers: myHeaders,
body: raw,
redirect: "follow"
};

fetch("https://firseinvoicedemo.etranzactng.com/api/v1/si/generate-irn", requestOptions)
.then((response) => response.text())
.then((result) => console.log(result))
.catch((error) => console.error(error));

Code sample in Java:

import com.fasterxml.jackson.databind.ObjectMapper;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.charset.StandardCharsets;
import java.time.Instant;
import java.time.format.DateTimeFormatter;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;

public class IrnGeneratorClient {

private static final String CLIENT_SECRET = "NcnXs3aK7IAPZiDKXp/qENaJ2Yf5XkE2ToSK1PFU+g0SRJR8J7c=";
private static final String API_KEY = "30181103155636";
private static final ObjectMapper objectMapper = new ObjectMapper();

public static void main(String[] args) throws Exception {
// 1. Generate timestamp in ISO 8601 format
String timestamp = DateTimeFormatter.ISO_INSTANT.format(Instant.now()).replaceAll("\\.\\d{3}Z$", "Z");
// 2. Build payload to send
Map<String, Object> payload = new HashMap<>();
payload.put("business_id", "8a94a32a-99b2-41ff-80ae-a0dd9a3c4626");
payload.put("invoice_number", "PZ32kgfNW015792");
payload.put("issuance_date", "20250731");
payload.put("service_id", "F40299B2");

String jsonPayload = objectMapper.writeValueAsString(payload);

// 3. Message = payload + timestamp
String message = jsonPayload + timestamp;
// 4. Generate HMAC-SHA256 signature (Base64 encoded)
String signature = generateSignature(message, CLIENT_SECRET);
// 5. Build HTTP request
HttpClient client = HttpClient.newHttpClient();

HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://firseinvoicedemo.etranzactng.com/api/v1/si/generate-irn"))
.header("Content-Type", "application/json")
.header("x-api-key", API_KEY)
.header("x-api-signature", signature)
.header("x-api-timestamp", timestamp)
.POST(HttpRequest.BodyPublishers.ofString(jsonPayload))
.build();

// 6. Execute request
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
// 7. Print result
System.out.println("STATUS = " + response.statusCode());
System.out.println("RESPONSE = " + response.body());
}

// Generate HMAC-SHA256 signature
private static String generateSignature(String message, String secret) throws Exception {
SecretKeySpec secretKey = new SecretKeySpec(secret.getBytes(StandardCharsets.UTF_8), "HmacSHA256");

Mac mac = Mac.getInstance("HmacSHA256");
mac.init(secretKey);

byte[] hash = mac.doFinal(message.getBytes(StandardCharsets.UTF_8));

return Base64.getEncoder().encodeToString(hash);
}
}