Introduction

The FimePay API is a RESTful HTTP API for generating, signing, and submitting NRS-compliant e-invoices in Nigeria. It is designed for:

  • Nigerian SMEs and freelancers who want to automate invoice generation and NRS submission
  • Developers building accounting, ERP, or finance integrations on top of FimePay
  • Enterprise finance teams integrating FimePay with Xero, QuickBooks, or in-house systems

Everything your product UI can do — create invoices, record payments, send to customers, submit to NRS — is available via API.

Base URL

Base URL
https://api.fimepay.com/api/v1

All paths in this documentation are relative to this base URL.

Authentication overview

Two authentication methods are supported. See the Authentication section for the full reference.

1. JWT Bearer token

For server-side integrations after a user has logged in:

cURL — JWT
curl https://api.fimepay.com/api/v1/invoices \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN"

Access tokens expire after 15 minutes. Use POST /auth/refresh with your refresh token cookie to obtain a new access token.

2. API key (recommended for server-to-server)

cURL — API key
curl https://api.fimepay.com/api/v1/invoices \
  -H "x-api-key: fp_live_YOUR_API_KEY"

API keys do not expire by default. They are scoped — each key only has access to the operations you grant. Generate API keys in Settings → API Keys.

API key format: fp_live_ followed by 40 hex characters. The full key is shown only once at creation time — store it securely.

Your first request

Step 1 — Get an API key from Settings → API Keys (scope: invoices:read).

Step 2 — List your invoices:

List invoices
curl https://api.fimepay.com/api/v1/invoices \
  -H "x-api-key: fp_live_YOUR_API_KEY"

Step 3 — Parse the response:

Response
{
  "data": [
    {
      "id": "019547a2-3b1c-7d8e-9f0a-1b2c3d4e5f6a",
      "invoice_number": "INV-2025-001",
      "status": "paid",
      "total_amount": "607375.00",
      "amount_due": "0.00",
      "currency": "NGN",
      "irn": "a3f9c2d1e8b74f6a952c1d0e3f7a8b9c",
      "issue_date": "2025-03-24",
      "due_date": "2025-04-23",
      "customer_snapshot": {
        "name": "TechVentures Nigeria Ltd",
        "tin": "98765432-0001",
        "email": "finance@techventures.ng"
      }
    }
  ],
  "next_cursor": "eyJpZCI6IjAxOTU0N2EyLTNiMWMtN2Q4ZS05ZjBhLTFiMmMzZDRlNWY2YSJ9",
  "has_more": true
}

The response is paginated using cursor-based pagination. Pass next_cursor as ?cursor= to get the next page.

Authentication

FimePay supports two authentication methods: JWT Bearer tokens (for user-initiated sessions) and API keys (recommended for server-to-server integrations).

JWT authentication flow

The standard flow: register → login → receive access token → use in header → refresh when expired → logout.

Register

POST/auth/registerPublic

Create a new account and tenant. Returns an access token immediately — no email verification required to start.

ParameterTypeRequiredDescription
first_namestringrequiredFirst name
last_namestringrequiredLast name
emailstringrequiredEmail address used to log in
passwordstringrequiredMin 8 chars — must include an uppercase letter and a number
business_namestringrequiredYour company or business name
business_typestringrequiredOne of: individual, sme, enterprise
tinstringoptionalTax Identification Number — required for NRS submission
plan_slugstringoptionalSubscription plan to activate. Defaults to free
cURL — Register
curl -X POST https://api.fimepay.com/api/v1/auth/register \
  -H "Content-Type: application/json" \
  -d '{
    "first_name": "Adaeze",
    "last_name": "Okonkwo",
    "email": "ada@fimedigital.ng",
    "password": "Secure123!",
    "business_name": "Fime Digital Ltd",
    "business_type": "sme",
    "tin": "12345678-0001"
  }'
Response
{
  "data": {
    "access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
    "plan_slug": "free"
  }
}

Login

POST/auth/loginPublic

Authenticate with email and password. Returns an access token in the response body and sets a refresh token as an httpOnly SameSite=Strict cookie.

ParameterTypeRequiredDescription
emailstringrequiredYour account email address
passwordstringrequiredYour account password
cURL — Login
curl -X POST https://api.fimepay.com/api/v1/auth/login \
  -H "Content-Type: application/json" \
  -d '{ "email": "ada@fimedigital.ng", "password": "Secure123!" }'
Response
{
  "data": {
    "access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
    "user": {
      "id": "019547a2-3b1c-7d8e-9f0a-1b2c3d4e5f6a",
      "email": "ada@fimedigital.ng",
      "first_name": "Adaeze",
      "last_name": "Okonkwo",
      "role": "owner"
    },
    "tenant": {
      "id": "01954abc-1234-5678-9abc-def012345678",
      "name": "Fime Digital Ltd"
    }
  }
}

The refresh token is set as an httpOnly cookie — it is not visible in the response body and cannot be accessed by JavaScript. This is intentional for security.


Get current user

GET/auth/meJWT or API Key

Return the profile of the currently authenticated user and their tenant.

cURL — Get current user
curl https://api.fimepay.com/api/v1/auth/me \
  -H "Authorization: Bearer <access_token>"
Response
{
  "data": {
    "id": "019547a2-3b1c-7d8e-9f0a-1b2c3d4e5f6a",
    "email": "ada@fimedigital.ng",
    "first_name": "Adaeze",
    "last_name": "Okonkwo",
    "role": "owner",
    "mfa_enabled": false,
    "tenant": {
      "id": "01954abc-1234-5678-9abc-def012345678",
      "name": "Fime Digital Ltd",
      "tin": "12345678-0001",
      "plan": "starter"
    }
  }
}

Refresh access token

POST/auth/refreshPublic

Rotate the access token using the refresh token cookie. Returns a new access token. The refresh token is automatically rotated (7-day sliding window).

Access tokens expire after 15 minutes. Call this endpoint before expiry. On expiry the API returns 401 TOKEN_EXPIRED.

cURL — Refresh token
# Browser clients send the cookie automatically.
# Server-side: pass the cookie header explicitly.
curl -X POST https://api.fimepay.com/api/v1/auth/refresh \
  -H "Cookie: refresh_token=<your_refresh_token>"
Response
{
  "data": {
    "access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9..."
  }
}

Logout

POST/auth/logoutJWT

Invalidate the current session. Clears the refresh token cookie and revokes the token server-side.

cURL — Logout
curl -X POST https://api.fimepay.com/api/v1/auth/logout \
  -H "Authorization: Bearer <access_token>"
Response
{ "data": { "message": "Logged out successfully" } }

Forgot password

POST/auth/forgot-passwordPublic

Send a password reset link to the given email. Always returns 200 — the response never reveals whether the email is registered.

ParameterTypeRequiredDescription
emailstringrequiredThe email address associated with the account
cURL — Forgot password
curl -X POST https://api.fimepay.com/api/v1/auth/forgot-password \
  -H "Content-Type: application/json" \
  -d '{ "email": "ada@fimedigital.ng" }'
Response
{ "data": { "message": "If this email exists, a reset link has been sent." } }

Reset password

POST/auth/reset-passwordPublic

Set a new password using the token from the reset email. Tokens are single-use and expire after 1 hour.

ParameterTypeRequiredDescription
tokenstringrequiredReset token from the email link
passwordstringrequiredNew password — min 8 chars, must include uppercase and a number
cURL — Reset password
curl -X POST https://api.fimepay.com/api/v1/auth/reset-password \
  -H "Content-Type: application/json" \
  -d '{
    "token": "a1b2c3d4e5f6...",
    "password": "NewSecure456!"
  }'
Response
{ "data": { "message": "Password updated successfully." } }

API key authentication

API keys are recommended for server-to-server integrations. They do not expire by default and are scoped to specific operations.

Pass the key in the x-api-key header on every request:

API key header
curl https://api.fimepay.com/api/v1/invoices \
  -H "x-api-key: fp_live_abc123..."

Available scopes

ScopeAccess granted
invoices:readGET invoices, download PDF and XML
invoices:writeCreate, update, send, finalize invoices
customers:readGET customers and contacts
customers:writeCreate and update customers
payments:writeRecord payments against invoices
reports:readAccess all report endpoints
webhooks:manageRegister and manage webhook endpoints
adminFull access — server-to-server only

Never expose API keys in client-side code, browser extensions, or public repositories. Always use environment variables and server-side requests.


OAuth (Google and Microsoft)

Redirect the user's browser to initiate OAuth login:

OAuth redirect URLs
GET https://api.fimepay.com/api/v1/auth/oauth/google
GET https://api.fimepay.com/api/v1/auth/oauth/microsoft

After authentication, the user is redirected back to the dashboard with the session set. OAuth accounts are provisioned into their own tenant automatically.


Multi-factor authentication (MFA)

TOTP-based MFA (Google Authenticator, Authy, 1Password) is available for all accounts.

Set up MFA

POST/auth/mfa/setupJWT

Generate a TOTP secret and QR code for authenticator app enrollment. Returns a base32 secret, a data URI QR code, and one-time backup codes.

cURL — Setup MFA
curl -X POST https://api.fimepay.com/api/v1/auth/mfa/setup \
  -H "Authorization: Bearer <access_token>"
Response
{
  "data": {
    "secret": "JBSWY3DPEHPK3PXP",
    "qr_code": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUg...",
    "backup_codes": [
      "8d3f-92a1", "4c7b-1e5d", "9f2a-3b8c",
      "1d6e-7a4f", "5c9b-2e1d", "7a3f-8c6e"
    ]
  }
}

Verify MFA code

POST/auth/mfa/verifyJWT

Verify a TOTP code to complete MFA enrollment or confirm identity during a login challenge.

ParameterTypeRequiredDescription
codestringrequired6-digit TOTP code from the authenticator app
cURL — Verify MFA
curl -X POST https://api.fimepay.com/api/v1/auth/mfa/verify \
  -H "Authorization: Bearer <access_token>" \
  -H "Content-Type: application/json" \
  -d '{ "code": "482931" }'
Response
{ "data": { "verified": true } }

Making your first request

This guide walks you through creating and sending your first invoice via the API in under 5 minutes.

Prerequisites

  • An FimePay account (sign up free at fimepay.com)
  • An API key with invoices:write and customers:write scopes
  • curl or any HTTP client

Step 1 — Create a customer

Create a customer
curl -X POST https://api.fimepay.com/api/v1/customers \
  -H "x-api-key: fp_live_YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Fime Technologies Ltd",
    "email": "accounts@fime.ng",
    "tin": "12345678-0001",
    "address": "14 Broad Street, Lagos Island",
    "country": "NG"
  }'
Response
{
  "id": "019547b3-0000-0000-0000-000000000001",
  "name": "Fime Technologies Ltd",
  "email": "accounts@fime.ng",
  "tin": "12345678-0001"
}

Save the id — you need it to create the invoice.

Step 2 — Create a draft invoice

Create invoice
curl -X POST https://api.fimepay.com/api/v1/invoices \
  -H "x-api-key: fp_live_YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "customer_id": "019547b3-0000-0000-0000-000000000001",
    "issue_date": "2025-03-24",
    "due_date": "2025-04-23",
    "currency": "NGN",
    "line_items": [
      {
        "description": "Software Consulting - March 2025",
        "quantity": 10,
        "unit_price": 35000,
        "discount_percent": 0
      }
    ],
    "notes": "Payment due within 30 days."
  }'
Response
{
  "id": "019547a2-3b1c-7d8e-9f0a-1b2c3d4e5f6a",
  "invoice_number": "INV-2025-001",
  "status": "draft",
  "total_amount": "350000.00",
  "amount_due": "350000.00"
}

Step 3 — Finalize to generate IRN

Finalize invoice
curl -X POST https://api.fimepay.com/api/v1/invoices/019547a2-3b1c-7d8e-9f0a-1b2c3d4e5f6a/finalize \
  -H "x-api-key: fp_live_YOUR_API_KEY"
Response
{
  "id": "019547a2-3b1c-7d8e-9f0a-1b2c3d4e5f6a",
  "status": "finalized",
  "irn": "a3f9c2d1e8b74f6a952c1d0e3f7a8b9c2d1e8b74",
  "digital_signature": "eyJhbGciOiJSUzI1NiJ9..."
}

Step 4 — Send to customer

Send invoice
curl -X POST https://api.fimepay.com/api/v1/invoices/019547a2-3b1c-7d8e-9f0a-1b2c3d4e5f6a/send \
  -H "x-api-key: fp_live_YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"channels": ["email"]}'

Your customer receives the invoice by email with a PDF attachment and a "View Invoice" link.

Step 5 — Submit to NRS (optional, Starter+)

Submit to NRS
curl -X POST https://api.fimepay.com/api/v1/invoices/019547a2-3b1c-7d8e-9f0a-1b2c3d4e5f6a/submit-firs \
  -H "x-api-key: fp_live_YOUR_API_KEY"

This returns 202 Accepted immediately. Poll GET /invoices/:id and check firs_submitted_at to confirm NRS receipt.

That's it — in 5 API calls you created a customer, issued a NRS-compliant signed invoice, emailed it with a PDF, and submitted it to NRS. The full API reference is in the sections below.

Errors & status codes

All errors follow a standard response shape. The error.code is machine-readable and stable — it will not change between API versions.

Error response shape
{
  "error": {
    "code": "INVOICE_NOT_FOUND",
    "message": "Invoice with id '019547a2-...' was not found.",
    "details": [],
    "request_id": "req_01954def-1234-5678-9abc-def012345678"
  }
}

For VALIDATION_ERROR, the details array contains field-level errors:

Validation error example
{
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Request body validation failed.",
    "details": [
      { "field": "due_date", "message": "due_date must be a valid ISO date" },
      { "field": "line_items.0.unit_price", "message": "unit_price must be a positive number" }
    ],
    "request_id": "req_01954def-..."
  }
}

Error code reference

HTTP statusError codeMeaning
400VALIDATION_ERRORRequest body failed validation. details[] contains field errors
400OAUTH_ACCOUNTAccount was created via Google/Microsoft OAuth. Use OAuth login
401UNAUTHORIZEDMissing or invalid authentication token or API key
401TOKEN_EXPIREDAccess token has expired. Call POST /auth/refresh
402PLAN_LIMIT_EXCEEDEDInvoice or API call limit reached for the current billing period
402FEATURE_NOT_AVAILABLEFeature not included in current subscription plan
403FORBIDDENAuthenticated but insufficient role or API key scope
404NOT_FOUNDResource does not exist or belongs to another tenant
409CONFLICTDuplicate — email, invoice number, or payment reference already exists
413PAYLOAD_TOO_LARGEFile upload exceeds the 10 MB limit
422UNPROCESSABLEBusiness rule violation — see message for details (e.g. "Invoice is not in draft status")
429RATE_LIMIT_EXCEEDEDToo many requests. Check X-RateLimit-Reset header for reset time
500INTERNAL_ERRORUnexpected server error. Contact support with the request_id
!

All 404 responses for tenant-scoped resources are intentional. If you request a resource that belongs to a different tenant, you receive 404 — not 403 — to prevent information leakage about whether the resource exists at all.

Idempotency

All POST endpoints accept an Idempotency-Key header. If you send the same key twice within 24 hours, the second request returns the same response as the first without creating a duplicate resource.

Using Idempotency-Key
curl -X POST https://api.fimepay.com/api/v1/invoices \
  -H "x-api-key: fp_live_YOUR_KEY" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{"customer_id": "...", "issue_date": "2025-03-24", ...}'

Use a UUID or a hash of the request parameters as the idempotency key.

Rate limiting

All API endpoints are rate-limited per tenant. Limits vary by subscription plan.

PlanAPI calls per dayBurst (per minute)
Free50030
Starter5,000100
Growth50,000300
EnterpriseCustomCustom

Rate limit headers

Every API response includes these headers:

HeaderDescription
X-RateLimit-LimitYour plan's daily limit
X-RateLimit-RemainingCalls remaining in the current window
X-RateLimit-ResetUnix timestamp when the window resets

When you hit the limit

Response
{
  "error": {
    "code": "RATE_LIMIT_EXCEEDED",
    "message": "API rate limit exceeded. Resets at 2025-03-25T00:00:00.000Z.",
    "details": [],
    "request_id": "req_01954def-..."
  }
}

If you consistently approach your plan limit, upgrade your plan or contact support for an Enterprise plan with custom limits. You can also purchase additional API call add-ons from the billing page.

Best practices

  • Cache responses where appropriate — invoices and customers don't change frequently
  • Use webhooks instead of polling for status changes (e.g. NRS submission results)
  • Batch operations where possible — create all line items in a single POST /invoices call

Pagination

All list endpoints use cursor-based pagination. Cursor pagination is more efficient than offset pagination for large datasets and avoids the "skipped row" problem when records are inserted between pages.

Request parameters

ParameterTypeRequiredDescription
cursorstringoptionalOpaque cursor from the previous response's next_cursor field. Omit for the first page.
limitnumberoptionalNumber of records per page. Default: 25. Max: 100.

Response shape

Paginated response
{
  "data": [...],
  "next_cursor": "eyJpZCI6IjAxOTU0N2EyLTNiMWMtN2Q4ZS05ZjBhLTFiMmMzZDRlNWY2YSJ9",
  "has_more": true
}
FieldTypeDescription
dataarrayThe current page of records
next_cursorstring | nullPass as ?cursor= to get the next page. null when there are no more pages
has_morebooleantrue if there are more records after this page

Iterating all pages

Fetching all invoices (TypeScript)
async function getAllInvoices(apiKey: string) {
  const results = [];
  let cursor: string | null = null;

  do {
    const params = new URLSearchParams({ limit: '100' });
    if (cursor) params.set('cursor', cursor);

    const res = await fetch(
      `https://api.fimepay.com/api/v1/invoices?${params}`,
      { headers: { 'x-api-key': apiKey } }
    );
    const page = await res.json();

    results.push(...page.data);
    cursor = page.has_more ? page.next_cursor : null;
  } while (cursor);

  return results;
}

Cursor tokens are opaque and must be treated as strings. Do not attempt to decode or construct them manually — the format may change between API versions.

Filtering list endpoints

Most list endpoints accept additional query parameters for filtering. See each endpoint's documentation for the full list. Common filters:

ParameterTypeDescription
statusstringFilter by record status (e.g. ?status=paid)
fromdateFilter records created on or after this date (ISO 8601)
todateFilter records created on or before this date (ISO 8601)
customer_iduuidFilter by customer
qstringFull-text search on relevant fields

Invoices

Invoices are the core resource of FimePay. An invoice captures a billable event between your business and a customer. Once finalized, an invoice is cryptographically signed and assigned a NRS IRN (Invoice Reference Number).

The invoice object

Response
{
  "id": "019547a2-3b1c-7d8e-9f0a-1b2c3d4e5f6a",
  "invoice_number": "INV-2025-001",
  "status": "sent",
  "currency": "NGN",
  "issue_date": "2025-03-24",
  "due_date": "2025-04-23",
  "subtotal": "350000.00",
  "tax_total": "26250.00",
  "total_amount": "376250.00",
  "amount_due": "376250.00",
  "notes": "Payment due within 30 days.",
  "irn": "a3f9c2d1e8b74f6a952c1d0e3f7a8b9c2d1e8b74",
  "digital_signature": "eyJhbGciOiJSUzI1NiJ9...",
  "public_token": "37c1c605-521a-4c35-8d6f-c32d69c060d5",
  "firs_submitted_at": null,
  "customer_snapshot": {
    "name": "TechVentures Nigeria Ltd",
    "tin": "98765432-0001",
    "email": "finance@techventures.ng"
  },
  "line_items": [
    {
      "description": "Web Application Development",
      "quantity": 1,
      "unit_price": "350000.00",
      "discount_percent": "0.00",
      "tax_rate": "7.5",
      "total": "376250.00"
    }
  ],
  "payments": []
}

Invoice lifecycle

Invoices move through a defined set of states. Only certain transitions are allowed.

draft → finalized → sent ──────────► paid
            │          └─────────► partial → paid
            │
            ↓
        cancelled

finalized/sent → firs_submission_failed (NRS error — can retry)
StatusMeaningEditable
draftCreated but not yet finalizedYes
finalizedLocked, signed, IRN assignedNo
sentSent to customer via email or WhatsAppNo
partialPartially paid — amount_due > 0No
paidFully paid — amount_due == 0No
cancelledVoided by the issuerNo

GET /invoices

GET/invoicesJWT or API Key

List invoices for the authenticated tenant. Supports cursor pagination and filtering by status, date range, and customer.

Scope: invoices:read

ParameterTypeRequiredDescription
cursorstringoptionalPagination cursor from previous response
limitnumberoptionalRecords per page. Default 25, max 100
statusstringoptionalFilter by status — e.g. paid, sent, draft
customer_iduuidoptionalFilter by customer ID
fromdateoptionalFilter invoices issued on or after this date (ISO 8601)
todateoptionalFilter invoices issued on or before this date (ISO 8601)
qstringoptionalSearch invoice number or customer name
Response
{
  "data": [
    {
      "id": "019547a2-3b1c-7d8e-9f0a-1b2c3d4e5f6a",
      "invoice_number": "INV-2025-001",
      "status": "paid",
      "total_amount": "376250.00",
      "amount_due": "0.00",
      "currency": "NGN",
      "issue_date": "2025-03-24",
      "due_date": "2025-04-23",
      "customer_snapshot": { "name": "TechVentures Nigeria Ltd" }
    }
  ],
  "next_cursor": "eyJpZCI6IjAxOTU0N2EyIn0",
  "has_more": false
}

POST /invoices

POST/invoicesJWT or API Key

Create a new invoice in draft status. The invoice is not locked or submitted to NRS at this stage — call POST /invoices/:id/finalize when ready.

Scope: invoices:write

ParameterTypeRequiredDescription
customer_iduuidrequiredID of the customer to bill
issue_datedaterequiredInvoice issue date (ISO 8601) — e.g. 2025-03-24
due_datedaterequiredPayment due date (ISO 8601) — e.g. 2025-04-23
currencystringoptionalISO 4217 currency code. Default: NGN
line_itemsarrayrequiredArray of line item objects (see below). Minimum 1 item.
notesstringoptionalInternal or customer-facing notes
invoice_numberstringoptionalCustom invoice number. Auto-generated if omitted.
Line item object
{
  "description": "Web Application Development",
  "quantity": 1,
  "unit_price": 350000,
  "discount_percent": 0,
  "tax_rate_id": "019547c0-0000-0000-0000-000000000001"
}
Response
{
  "id": "019547a2-3b1c-7d8e-9f0a-1b2c3d4e5f6a",
  "invoice_number": "INV-2025-001",
  "status": "draft",
  "total_amount": "376250.00",
  "amount_due": "376250.00",
  "currency": "NGN"
}

GET /invoices/:id

GET/invoices/:idJWT or API Key

Retrieve a single invoice with all fields including line items, payments, and NRS submission status.

Scope: invoices:read

ParameterTypeRequiredDescription
iduuidrequiredInvoice ID

PATCH /invoices/:id

PATCH/invoices/:idJWT or API Key

Update a draft invoice. Returns 422 UNPROCESSABLE if the invoice is not in draft status.

Scope: invoices:write

ParameterTypeRequiredDescription
customer_iduuidoptionalReassign to a different customer
due_datedateoptionalUpdate payment due date
line_itemsarrayoptionalReplace all line items (full replacement, not merge)
notesstringoptionalUpdate notes

PATCH on line_items is a full replacement — the entire array is replaced, not merged. Include all line items you want to keep.


DELETE /invoices/:id

DELETE/invoices/:idJWT or API Key

Soft-delete a draft invoice. Finalized invoices cannot be deleted — use POST /invoices/:id/void instead.

Scope: invoices:write

Response
{ "message": "Invoice deleted." }

POST /invoices/:id/finalize

POST/invoices/:id/finalizeJWT or API Key

Lock the invoice, generate an RSA digital signature, and assign a NRS IRN. The invoice moves from draft to finalized.

Scope: invoices:write

Requires Starter+ plan or higher

!

Once finalized, an invoice is immutable. Finalization generates an RSA digital signature using your tenant's private signing key and assigns a NRS IRN. The IRN is the SHA-256 hash of invoice_number + tenant_TIN + issue_date.

Response
{
  "id": "019547a2-3b1c-7d8e-9f0a-1b2c3d4e5f6a",
  "status": "finalized",
  "irn": "a3f9c2d1e8b74f6a952c1d0e3f7a8b9c2d1e8b74",
  "digital_signature": "eyJhbGciOiJSUzI1NiJ9..."
}

POST /invoices/:id/submit-firs

POST/invoices/:id/submit-firsJWT or API Key

Enqueue the invoice for submission to the NRS e-Invoice API. The invoice must be finalized first. Returns 202 Accepted immediately — submission is asynchronous.

Scope: invoices:write

Requires Starter+ plan or higher

This endpoint enqueues an async job. Poll GET /invoices/:id and check firs_submitted_at — this is set when NRS confirms receipt. On failure, firs_response.error contains the NRS error message.

Response
{
  "message": "Invoice queued for NRS submission.",
  "job_id": "firs-submission:019547a2"
}

POST /invoices/:id/send

POST/invoices/:id/sendJWT or API Key

Send the invoice to the customer via email and/or WhatsApp. A PDF attachment is included in email sends. The invoice must be finalized.

Scope: invoices:write

ParameterTypeRequiredDescription
channelsarrayrequiredDelivery channels — email and/or whatsapp
messagestringoptionalCustom message included in the email body
Response
{
  "dispatched": ["email"],
  "message": "Invoice sent via email."
}

POST /invoices/:id/record-payment

POST/invoices/:id/record-paymentJWT or API Key

Record a payment received against the invoice. Updates amount_due and moves the invoice to paid or partial status automatically.

Scope: payments:write

ParameterTypeRequiredDescription
amountnumberrequiredPayment amount in the invoice currency
payment_methodstringrequiredUBL 2.1 payment means code — e.g. 30 = Bank transfer, 10 = Cash, 48 = Card
payment_datedaterequiredDate payment was received (ISO 8601)
referencestringoptionalPayment reference (bank transfer ref, transaction ID)
notesstringoptionalInternal notes about this payment
Response
{
  "id": "019547a2-3b1c-7d8e-9f0a-1b2c3d4e5f6a",
  "status": "paid",
  "amount_due": "0.00",
  "payments": [
    {
      "id": "019547d0-0000-0000-0000-000000000001",
      "amount": "376250.00",
      "payment_method": "30",
      "payment_date": "2025-03-24",
      "reference": "TXN-123456"
    }
  ]
}

POST /invoices/:id/void

POST/invoices/:id/voidJWT or API Key

Cancel a finalized or sent invoice. Requires accountant role or higher.

Scope: invoices:write

Response
{ "id": "019547a2-3b1c-7d8e-9f0a-1b2c3d4e5f6a", "status": "cancelled" }

POST /invoices/:id/clone

POST/invoices/:id/cloneJWT or API Key

Duplicate an invoice as a new draft. Copies all fields except invoice_number (auto-generated), status (set to draft), and payment history.

Scope: invoices:write

Response
{ "id": "019547b0-0000-0000-0000-000000000001", "invoice_number": "INV-2025-002", "status": "draft" }

GET /invoices/:id/pdf

GET/invoices/:id/pdfJWT or API Key

Download the invoice as a PDF. Returns application/pdf binary. The invoice must be finalized.

Scope: invoices:read

Requires Starter+ plan or higher


GET /invoices/:id/xml

GET/invoices/:id/xmlJWT or API Key

Download the UBL 2.1 XML representation of the invoice as required by NRS. The invoice must be finalized.

Scope: invoices:read

Requires Starter+ plan or higher


POST /invoices/:id/credit-note

POST/invoices/:id/credit-noteJWT or API Key

Issue a credit note against a finalized invoice. Creates a new invoice of type credit_note (UBL type code 381) linked to the original.

Scope: invoices:write

Requires Starter+ plan or higher

ParameterTypeRequiredDescription
reasonstringrequiredReason for the credit note
amountnumberoptionalPartial credit amount. Defaults to the full invoice total.
line_itemsarrayoptionalSpecific line items to credit. Defaults to all.
Response
{
  "id": "019547e0-0000-0000-0000-000000000001",
  "invoice_number": "CN-2025-001",
  "status": "finalized",
  "type": "credit_note",
  "original_invoice_id": "019547a2-3b1c-7d8e-9f0a-1b2c3d4e5f6a",
  "total_amount": "376250.00"
}

GET /invoices/stats

GET/invoices/statsJWT or API Key

Aggregated invoice statistics for the authenticated tenant.

Scope: invoices:read

Response
{
  "total_invoiced": "4521750.00",
  "total_outstanding": "825000.00",
  "total_overdue": "125000.00",
  "counts": { "draft": 3, "finalized": 2, "sent": 5, "paid": 18, "partial": 2 },
  "this_month": { "invoiced": "1075000.00", "collected": "825000.00", "invoices_count": 6 }
}

Customers

Customers represent the businesses or individuals you invoice. A customer record stores contact details, TIN, and billing address — all snapshotted onto every invoice at creation time so historical invoices are never affected by later edits.

Customer TIN is required for NRS-compliant B2B invoicing. Invoices submitted to NRS without a customer TIN will be rejected at the validation step.


List customers

GET/customersJWT or API Key

List all customers for the tenant. Supports cursor pagination and full-text search by name, email, or TIN.

Scope: customers:read

ParameterTypeRequiredDescription
limitnumberoptionalResults per page (max 100). Default 20
cursorstringoptionalPagination cursor from a previous response
searchstringoptionalSearch by name, email, or TIN
cURL — List customers
curl "https://api.fimepay.com/api/v1/customers?limit=20&search=Konga" \
  -H "x-api-key: <your_api_key>"
Response
{
  "data": [
    {
      "id": "019547a2-3b1c-7d8e-9f0a-1b2c3d4e5f6a",
      "name": "Konga Technologies Ltd",
      "email": "finance@konga.com",
      "phone": "+2348012345678",
      "tin": "98765432-0001",
      "invoice_count": 14,
      "outstanding_balance": "450000.00",
      "created_at": "2026-01-15T09:22:00.000Z"
    }
  ],
  "meta": { "has_more": false, "next_cursor": null, "total": 1 }
}

Create customer

POST/customersJWT or API Key

Create a new customer. TIN is required for NRS-compliant invoicing.

Scope: customers:write

ParameterTypeRequiredDescription
namestringrequiredCustomer business or individual name
emailstringrequiredPrimary billing email address
phonestringoptionalPhone number with country code — e.g. +2348012345678
tinstringoptionalTax Identification Number — required for NRS submission
billing_addressobjectoptionalObject with street_name, city_name, postal_zone, country
currencystringoptionalDefault invoice currency. Defaults to NGN
notesstringoptionalInternal notes about this customer
cURL — Create customer
curl -X POST https://api.fimepay.com/api/v1/customers \
  -H "x-api-key: <your_api_key>" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Konga Technologies Ltd",
    "email": "finance@konga.com",
    "phone": "+2348012345678",
    "tin": "98765432-0001",
    "billing_address": {
      "street_name": "Plot 33, Kudirat Abiola Way",
      "city_name": "Lagos",
      "postal_zone": "100281",
      "country": "NG"
    }
  }'
Response
{
  "data": {
    "id": "019547a2-3b1c-7d8e-9f0a-1b2c3d4e5f6a",
    "name": "Konga Technologies Ltd",
    "email": "finance@konga.com",
    "tin": "98765432-0001",
    "invoice_count": 0,
    "outstanding_balance": "0.00",
    "created_at": "2026-04-09T10:00:00.000Z"
  }
}

Get customer

GET/customers/:idJWT or API Key

Retrieve a single customer with all profile fields and contacts.

Scope: customers:read

cURL — Get customer
curl https://api.fimepay.com/api/v1/customers/019547a2-3b1c-7d8e-9f0a-1b2c3d4e5f6a \
  -H "x-api-key: <your_api_key>"
Response
{
  "data": {
    "id": "019547a2-3b1c-7d8e-9f0a-1b2c3d4e5f6a",
    "name": "Konga Technologies Ltd",
    "email": "finance@konga.com",
    "phone": "+2348012345678",
    "tin": "98765432-0001",
    "currency": "NGN",
    "billing_address": {
      "street_name": "Plot 33, Kudirat Abiola Way",
      "city_name": "Lagos",
      "postal_zone": "100281",
      "country": "NG"
    },
    "contacts": [
      { "id": "01954def-5678-9abc-def0-123456789abc", "name": "Emeka Obi", "email": "emeka.obi@konga.com", "role": "Accounts Payable" }
    ],
    "invoice_count": 14,
    "outstanding_balance": "450000.00"
  }
}

Update customer

PATCH/customers/:idJWT or API Key

Update customer fields. All body fields are optional — only the fields you include are changed.

Scope: customers:write

cURL — Update customer
curl -X PATCH https://api.fimepay.com/api/v1/customers/019547a2-3b1c-7d8e-9f0a-1b2c3d4e5f6a \
  -H "x-api-key: <your_api_key>" \
  -H "Content-Type: application/json" \
  -d '{ "phone": "+2348099999999" }'
Response
{ "data": { "id": "019547a2-3b1c-7d8e-9f0a-1b2c3d4e5f6a", "phone": "+2348099999999", "updated_at": "2026-04-09T11:30:00.000Z" } }

Delete customer

DELETE/customers/:idJWT or API Key

Soft-delete a customer. Existing invoices and their snapshots are not affected.

Scope: customers:write

Response
{ "data": { "message": "Customer deleted." } }

List customer invoices

GET/customers/:id/invoicesJWT or API Key

List all invoices for a specific customer, newest first. Supports cursor pagination.

Scope: invoices:read

Response
{
  "data": [
    {
      "id": "01954abc-1234-5678-9abc-def012345678",
      "invoice_number": "INV20260001",
      "status": "sent",
      "total_amount": "150000.00",
      "currency": "NGN",
      "issue_date": "2026-04-01",
      "due_date": "2026-04-30"
    }
  ],
  "meta": { "has_more": false, "next_cursor": null }
}

Add contact

POST/customers/:id/contactsJWT or API Key

Add a contact person to a customer record (e.g. accounts payable contact to CC on invoices).

Scope: customers:write

ParameterTypeRequiredDescription
namestringrequiredContact full name
emailstringrequiredContact email address
phonestringoptionalContact phone number
rolestringoptionalRole description — e.g. Accounts Payable
cURL — Add contact
curl -X POST https://api.fimepay.com/api/v1/customers/019547a2-3b1c-7d8e-9f0a-1b2c3d4e5f6a/contacts \
  -H "x-api-key: <your_api_key>" \
  -H "Content-Type: application/json" \
  -d '{ "name": "Emeka Obi", "email": "emeka.obi@konga.com", "role": "Accounts Payable" }'
Response
{
  "data": { "id": "01954def-5678-9abc-def0-123456789abc", "name": "Emeka Obi", "email": "emeka.obi@konga.com", "role": "Accounts Payable" }
}

Payments

A dedicated Payments dashboard page is coming soon. In the meantime, payments are recorded directly from the invoice detail view using Record Payment, and Paystack payments are captured automatically via webhook.

Payments are created when you call POST /invoices/:id/record-payment or when a Paystack charge.success event arrives. The endpoints below provide a global view across all invoices.

Endpoints

GET/paymentsJWT or API Key

List all payment records across all invoices. Supports filtering by date range and method.

Scope: payments:write

GET/payments/:idJWT or API Key

Retrieve a single payment record with the linked invoice details.

Scope: payments:write

POST/payments/reconcileJWT or API Key

Match an unlinked bank transfer to an invoice by amount and reference.

Scope: payments:write

Record a payment against an invoice

The most common way to create a payment is via the invoice endpoint:

POST/invoices/:id/record-paymentJWT or API Key

Record a manual payment against an invoice. See the Invoices section for full documentation.

Scope: payments:write

cURL — Record payment
curl -X POST https://api.fimepay.com/api/v1/invoices/01954abc-1234-5678-9abc-def012345678/record-payment \
  -H "Authorization: Bearer <access_token>" \
  -H "Content-Type: application/json" \
  -d '{
    "amount": "150000.00",
    "method": "bank_transfer",
    "reference": "TRF-20260409-001",
    "paid_at": "2026-04-09",
    "notes": "Full payment received"
  }'
Response
{
  "data": {
    "id": "01954abc-1234-5678-9abc-def012345678",
    "status": "paid",
    "amount_paid": "150000.00",
    "payment_method": "bank_transfer",
    "payment_reference": "TRF-20260409-001",
    "paid_at": "2026-04-09T00:00:00.000Z"
  }
}

Tax rates

Full documentation for this section is coming soon. In the meantime, the endpoint reference below covers all available operations.

FimePay includes pre-seeded Nigerian tax rates: VAT 7.5% and WHT 5%/10%. You can create additional custom rates for your industry.

Endpoints

GET/tax-ratesJWT or API Key

List all tax rates configured for the tenant, including system-seeded Nigerian VAT and WHT rates.

Scope: invoices:read

POST/tax-ratesJWT or API Key

Create a custom tax rate. Requires accountant role or higher.

Scope: invoices:write

PATCH/tax-rates/:idJWT or API Key

Update a custom tax rate. System-seeded rates cannot be modified.

Scope: invoices:write

DELETE/tax-rates/:idJWT or API Key

Delete a custom tax rate. Rates in use on invoices cannot be deleted.

Scope: invoices:write

GET/tax-reportsJWT or API Key

List generated VAT and WHT filing reports.

Scope: reports:read

POST/tax-reports/generateJWT or API Key

Generate a VAT return report for a given period. Returns a downloadable PDF and structured JSON.

Scope: reports:read

Reports

Full documentation for this section is coming soon. In the meantime, the endpoint reference below covers all available operations.

All report endpoints accept ?from=, ?to=, and ?currency= query parameters for date range filtering.

Endpoints

GET/reports/profit-lossJWT or API Key

Profit and loss statement for a given period. Aggregates invoiced revenue against recorded expenses.

Scope: reports:read

GET/reports/cash-flowJWT or API Key

Cash flow report showing inflows from invoice payments and outflows from expenses.

Scope: reports:read

GET/reports/receivables-ageingJWT or API Key

Receivables ageing analysis: outstanding invoices grouped by 0-30, 31-60, 61-90, 90+ days overdue.

Scope: reports:read

GET/reports/tax-summaryJWT or API Key

VAT and WHT summary for a period. Shows total output VAT, input VAT (from expenses), and net payable.

Scope: reports:read

API Keys

API keys provide long-lived, scoped access to the FimePay API for server-to-server integrations. Manage them under Settings → API Keys in the dashboard.

API keys are tenant-scoped — each key only has access to data within the tenant it was created in. A key created under Fime Digital Ltd cannot access data from another tenant.


List API keys

GET/api-keysJWT

List all API keys for the tenant. Key values are never returned after creation — only the prefix, scopes, and metadata.

cURL — List API keys
curl https://api.fimepay.com/api/v1/api-keys \
  -H "Authorization: Bearer <access_token>"
Response
{
  "data": [
    {
      "id": "019554b9-0a8d-e4f5-a6b7-8c9d0e1f2a3b",
      "name": "ERP Integration",
      "prefix": "fp_live_a1b2c3",
      "scopes": ["invoices:read", "invoices:write", "customers:read"],
      "last_used_at": "2026-04-09T08:45:00.000Z",
      "expires_at": null,
      "created_at": "2026-01-10T09:00:00.000Z"
    }
  ]
}

Create API key

POST/api-keysJWT

Create a new API key with specified scopes and optional expiry date. The full key value is returned only once — store it immediately.

ParameterTypeRequiredDescription
namestringrequiredHuman-readable label for this key — e.g. ERP Integration
scopesstring[]requiredArray of permission scopes. See Authentication section for full list.
expires_atstringoptionalExpiry date (ISO 8601). null means no expiry.
cURL — Create API key
curl -X POST https://api.fimepay.com/api/v1/api-keys \
  -H "Authorization: Bearer <access_token>" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "ERP Integration",
    "scopes": ["invoices:read", "invoices:write", "customers:read"],
    "expires_at": null
  }'
Response
{
  "data": {
    "id": "019554b9-0a8d-e4f5-a6b7-8c9d0e1f2a3b",
    "name": "ERP Integration",
    "key": "fp_live_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6",
    "prefix": "fp_live_a1b2c3",
    "scopes": ["invoices:read", "invoices:write", "customers:read"],
    "expires_at": null,
    "created_at": "2026-04-09T10:00:00.000Z"
  }
}
!

The key field is only returned once at creation. Copy it now and store it securely — it cannot be retrieved again. If lost, revoke the key and create a new one.


Revoke API key

DELETE/api-keys/:idJWT

Revoke an API key immediately. Any in-flight requests using the key will receive 401 UNAUTHORIZED.

cURL — Revoke API key
curl -X DELETE https://api.fimepay.com/api/v1/api-keys/019554b9-0a8d-e4f5-a6b7-8c9d0e1f2a3b \
  -H "Authorization: Bearer <access_token>"
Response
{ "data": { "message": "API key revoked." } }

Available scopes

ScopeWhat it grants
invoices:readGET invoices, download PDF and XML
invoices:writeCreate, update, send, finalize, record payments
customers:readList and view customers
customers:writeCreate and update customers
payments:writeRecord and reconcile payments
reports:readAccess all report endpoints
webhooks:manageRegister and manage webhook endpoints
adminFull tenant access — use only for trusted server-side integrations

Use the minimum scope needed. Create separate keys per integration. Revoke immediately if a key is compromised — Settings → API Keys → Revoke.

Team

Full documentation for this section is coming soon. In the meantime, the endpoint reference below covers all available operations.

Team members share access to the same tenant. Roles control what each member can do. See the RBAC reference in the authentication section for the full permissions matrix.

Roles: owner > admin > accountant > member > readonly

Endpoints

GET/teamJWT

List all team members for the tenant, including their role and invitation status.

POST/teamJWT

Invite a new team member by email. They receive an invitation email with a sign-up link pre-populated with the tenant context.

PATCH/team/:userIdJWT

Update a team member's role. Only the owner can assign or remove the admin role.

DELETE/team/:userIdJWT

Remove a team member. They lose all access immediately. The owner cannot be removed.

Settings

Full documentation for this section is coming soon. In the meantime, the endpoint reference below covers all available operations.

Endpoints

GET/settingsJWT

Get the tenant's profile: business name, TIN, address, contact email, logo URL, and FIRS configuration status.

PATCH/settingsJWT

Update tenant profile fields. All fields are optional.

GET/settings/invoice-defaultsJWT

Get default invoice settings: number prefix, payment terms, footer text, and logo.

PATCH/settings/invoice-defaultsJWT

Update invoice defaults.

GET/settings/integrationsJWT

Get integration connection status: FIRS credentials, Xero, QuickBooks, WhatsApp.

PATCH/settings/integrationsJWT

Connect or disconnect third-party integrations.

POST/settings/data-exportJWT

Request a full NDPR data export. Returns a download link valid for 24 hours.

Billing

Full documentation for this section is coming soon. In the meantime, the endpoint reference below covers all available operations.

FimePay uses Paystack for subscription billing. Plans are billed monthly or annually (20% annual discount). See the subscription plans section for the full feature matrix.

Plans: Free → Starter (₦5,000/mo) → Growth (₦20,000/mo) → Enterprise (custom)

Endpoints

GET/billing/plansPublic

List all available subscription plans with pricing, features, and limits. Public endpoint — no authentication required.

GET/billing/subscriptionJWT

Get the current subscription status, plan details, current period dates, and usage summary.

POST/billing/subscriptionJWT

Create or upgrade a subscription. Returns a Paystack checkout URL to complete payment.

PATCH/billing/subscriptionJWT

Change billing cycle (monthly/annual) or downgrade plan. Effective at end of current period.

DELETE/billing/subscriptionJWT

Cancel the subscription. Access continues until the end of the current billing period, then downgrades to Free.

GET/billing/usageJWT

Current period usage: invoices created, API calls made, storage used, versus plan limits.

GET/billing/invoicesJWT

List billing invoice history from Paystack. Shows amount, date, plan, and payment status.

Webhooks

Webhooks allow FimePay to notify your server in real time when events occur — invoice paid, NRS submission confirmed, payment failed, and more. You register an HTTPS endpoint, and FimePay sends a POST request to it for each subscribed event.

Registering an endpoint

POST/webhooksJWT or API Key

Register a new webhook endpoint. FimePay will send POST requests to your URL for the events you subscribe to.

Scope: webhooks:manage

ParameterTypeRequiredDescription
urlstringrequiredYour HTTPS endpoint URL
eventsarrayrequiredList of event names to subscribe to. Use ["*"] to subscribe to all events.
descriptionstringoptionalHuman-readable description for this webhook endpoint
Response
{
  "id": "019547f0-0000-0000-0000-000000000001",
  "url": "https://api.yourapp.com/webhooks/fimepay",
  "events": ["invoice.paid", "payment.received"],
  "secret": "whs_a3f9c2d1e8b74f6a952c1d0e3f7a8b9c",
  "status": "active",
  "created_at": "2025-03-24T08:00:00.000Z"
}
!

The secret is shown only once. Store it securely — you will use it to verify webhook signatures. If you lose it, delete the webhook and create a new one.

Webhook events

EventFired when
invoice.createdA new invoice is created (draft)
invoice.sentAn invoice is sent to the customer
invoice.paidAn invoice is fully paid (amount_due == 0)
invoice.overdueAn invoice passes its due date unpaid
invoice.firs_submittedNRS confirms receipt and assigns an IRN
payment.receivedA payment is recorded against an invoice
subscription.upgradedTenant upgrades their billing plan
subscription.payment_failedA subscription renewal payment fails
customer.createdA new customer is created
credit_note.issuedA credit note is issued against an invoice

Payload structure

Every webhook POST has the same envelope:

Webhook payload
{
  "event": "invoice.paid",
  "tenant_id": "01954abc-1234-5678-9abc-def012345678",
  "timestamp": "2025-03-24T14:32:00.000Z",
  "data": {
    "invoice_id": "019547a2-3b1c-7d8e-9f0a-1b2c3d4e5f6a",
    "invoice_number": "INV-2025-001",
    "total_amount": "376250.00",
    "amount_due": "0.00",
    "paid_at": "2025-03-24T14:32:00.000Z"
  }
}
FieldTypeDescription
eventstringEvent name — e.g. invoice.paid
tenant_iduuidYour tenant ID
timestampISO 8601When the event occurred
dataobjectEvent-specific payload

Verifying webhook signatures

Every webhook request includes an X-FimePay-Signature header containing an HMAC-SHA256 signature of the raw request body, signed with your endpoint secret.

!

Always verify the X-FimePay-Signature header before processing a webhook. Discard any webhook that fails verification — it may be forged or replayed.

Signature verification (TypeScript)
import crypto from 'crypto';

function verifyWebhookSignature(
  payload: string,     // raw request body as a string
  signature: string,   // X-FimePay-Signature header value
  secret: string       // your webhook endpoint secret
): boolean {
  const expected = crypto
    .createHmac('sha256', secret)
    .update(payload)
    .digest('hex');
  return crypto.timingSafeEqual(
    Buffer.from(expected, 'hex'),
    Buffer.from(signature, 'hex')
  );
}
Express.js handler example
app.post(
  '/webhooks/fimepay',
  express.raw({ type: 'application/json' }),
  (req, res) => {
    const signature = req.headers['x-fimepay-signature'];
    const secret = process.env.FIMEPAY_WEBHOOK_SECRET;

    if (!verifyWebhookSignature(req.body.toString(), signature, secret)) {
      return res.status(400).send('Invalid signature');
    }

    const event = JSON.parse(req.body);

    switch (event.event) {
      case 'invoice.paid':
        await markOrderFulfilled(event.data.invoice_id);
        break;
      case 'payment.received':
        await updateAccountsReceivable(event.data);
        break;
    }

    res.status(200).send('OK');
  }
);

Retry behaviour

If your endpoint does not return a 2xx status within 30 seconds, FimePay retries the delivery:

AttemptDelay
1st retry30 seconds
2nd retry5 minutes
3rd retry30 minutes

After 3 failed retries, the delivery is marked as failed. You can view delivery logs and manually retry from the dashboard or via the API.

GET/webhooks/:id/deliveriesJWT or API Key

List recent delivery attempts for a webhook endpoint.

Scope: webhooks:manage

POST/webhooks/:id/testJWT or API Key

Send a test event to the webhook endpoint with a sample payload.

Scope: webhooks:manage

Managing webhooks

GET/webhooksJWT or API Key

List all registered webhook endpoints for the authenticated tenant.

Scope: webhooks:manage

PATCH/webhooks/:idJWT or API Key

Update a webhook endpoint's URL, events subscription, or status (active/paused).

Scope: webhooks:manage

DELETE/webhooks/:idJWT or API Key

Delete a webhook endpoint and stop all deliveries to it.

Scope: webhooks:manage

NRS compliance

Full documentation for NRS integration is coming soon. In the meantime, see the key concepts below.

FimePay is designed to be fully compliant with the Nigeria Revenue Service (NRS) e-Invoice mandate for Nigerian businesses.

Key concepts

IRN (Invoice Reference Number) — A unique identifier assigned to every finalized invoice. Generated as SHA-256(invoice_number + tenant_TIN + issue_date). Required on all invoices submitted to NRS.

UBL 2.1 — The XML format required by NRS. FimePay generates fully compliant UBL 2.1 XML with Nigerian NRS extensions. Download via GET /invoices/:id/xml.

Digital signature — Every finalized invoice is signed with your tenant's RSA private key (RS256). The public key is registered with NRS. Signature is stored in invoices.digital_signature.

Sandbox vs production — Always use the sandbox API during development. Set the NRS_API_URL environment variable. Never submit test invoices to the production NRS API.

Submission flow

  1. Create invoice (POST /invoices) — draft
  2. Finalize invoice (POST /invoices/:id/finalize) — IRN and signature generated
  3. Submit to NRS (POST /invoices/:id/submit-firs) — async, check firs_submitted_at
  4. NRS confirms receipt → invoice.firs_submitted webhook fires

NRS credentials

Configure your NRS certificate and public key in Settings → NRS Credentials in the dashboard, or via PATCH /settings/integrations. Credentials are stored encrypted (AES-256-GCM) per tenant.

NRS submission requires the Starter plan or higher. Free plan invoices can be finalized and signed but cannot be submitted to NRS.

Paystack

Full Paystack integration documentation is coming soon.

Paystack is the primary payment gateway for FimePay. It handles NGN card payments, bank transfers, USSD, and subscription billing.

Webhook events handled

Paystack eventFimePay action
charge.successRecords payment against invoice, updates status
subscription.createActivates tenant subscription
invoice.payment_failedSets subscription to past_due, sends alert

Webhook endpoint

FimePay exposes a public webhook receiver at POST /webhooks/paystack. Paystack's webhook signature is verified using HMAC-SHA512 against the x-paystack-signature header.

!

Never expose your Paystack secret key in client-side code. All payment verification happens server-side.

Hosted payment page

Every invoice has a public payment page at /p/:invoiceToken/pay. Share this URL with your customer for self-service payment. No authentication required — the token is the access control.

Interactive API explorer

Try any endpoint directly in the browser. Click Authorize and enter your API key to make authenticated requests.

Loading API explorer…