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.
Parameter
Type
Required
Description
first_name
string
required
First name
last_name
string
required
Last name
email
string
required
Email address used to log in
password
string
required
Min 8 chars — must include an uppercase letter and a number
business_name
string
required
Your company or business name
business_type
string
required
One of: individual, sme, enterprise
tin
string
optional
Tax Identification Number — required for NRS submission
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.
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>"
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 status
Error code
Meaning
400
VALIDATION_ERROR
Request body failed validation. details[] contains field errors
400
OAUTH_ACCOUNT
Account was created via Google/Microsoft OAuth. Use OAuth login
401
UNAUTHORIZED
Missing or invalid authentication token or API key
401
TOKEN_EXPIRED
Access token has expired. Call POST /auth/refresh
402
PLAN_LIMIT_EXCEEDED
Invoice or API call limit reached for the current billing period
402
FEATURE_NOT_AVAILABLE
Feature not included in current subscription plan
403
FORBIDDEN
Authenticated but insufficient role or API key scope
404
NOT_FOUND
Resource does not exist or belongs to another tenant
409
CONFLICT
Duplicate — email, invoice number, or payment reference already exists
413
PAYLOAD_TOO_LARGE
File upload exceeds the 10 MB limit
422
UNPROCESSABLE
Business rule violation — see message for details (e.g. "Invoice is not in draft status")
429
RATE_LIMIT_EXCEEDED
Too many requests. Check X-RateLimit-Reset header for reset time
500
INTERNAL_ERROR
Unexpected 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.
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
Parameter
Type
Required
Description
cursor
string
optional
Opaque cursor from the previous response's next_cursor field. Omit for the first page.
limit
number
optional
Number of records per page. Default: 25. Max: 100.
Pass as ?cursor= to get the next page. null when there are no more pages
has_more
boolean
true 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:
Parameter
Type
Description
status
string
Filter by record status (e.g. ?status=paid)
from
date
Filter records created on or after this date (ISO 8601)
to
date
Filter records created on or before this date (ISO 8601)
customer_id
uuid
Filter by customer
q
string
Full-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).
Update a draft invoice. Returns 422 UNPROCESSABLE if the invoice is not in draft status.
Scope: invoices:write
Parameter
Type
Required
Description
customer_id
uuid
optional
Reassign to a different customer
due_date
date
optional
Update payment due date
line_items
array
optional
Replace all line items (full replacement, not merge)
notes
string
optional
Update 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.
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
Parameter
Type
Required
Description
channels
array
required
Delivery channels — email and/or whatsapp
message
string
optional
Custom 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
Parameter
Type
Required
Description
amount
number
required
Payment amount in the invoice currency
payment_method
string
required
UBL 2.1 payment means code — e.g. 30 = Bank transfer, 10 = Cash, 48 = Card
payment_date
date
required
Date payment was received (ISO 8601)
reference
string
optional
Payment reference (bank transfer ref, transaction ID)
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.
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.
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.
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.
Full 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.
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
Parameter
Type
Required
Description
url
string
required
Your HTTPS endpoint URL
events
array
required
List of event names to subscribe to. Use ["*"] to subscribe to all events.
description
string
optional
Human-readable description for this webhook endpoint
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.
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')
);
}
If your endpoint does not return a 2xx status within 30 seconds, FimePay retries the delivery:
Attempt
Delay
1st retry
30 seconds
2nd retry
5 minutes
3rd retry
30 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
Create invoice (POST /invoices) — draft
Finalize invoice (POST /invoices/:id/finalize) — IRN and signature generated
Submit to NRS (POST /invoices/:id/submit-firs) — async, check firs_submitted_at
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 event
FimePay action
charge.success
Records payment against invoice, updates status
subscription.create
Activates tenant subscription
invoice.payment_failed
Sets 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.