Tax compliance, as an API.
Calculate, file and initiate payment for Nigerian taxes from your own product, server-to-server, with one scoped API key. The engine behind MiTax, exposed for banks, fintechs and platforms.
Access is granted through partner onboarding. There is no public self-serve signup yet. We provision your partner record, mint your key, and link the customers you file on behalf of.
One key. Three server-to-server verbs.
calc
scope: tax:calcCompute PIT, VAT, CIT, PAYE, WHT or CGT for a customer. Every figure comes straight from the MiTax tax engine, and nothing is recomputed on the edge.
file
scope: filing:initiateTurn a calculation into a real filing with a computed deadline, under the server-authoritative calculation-id contract.
pay
scope: payment:initiateGet a payment handoff for the amount due plus the MiTax service fee. Funds flow customer → licensed provider → government. MiTax never holds money.
Your key is the auth.
Every request carries your partner key in the X-API-Key header. There is no end-user login and no OAuth. The caller is your backend. Keys look like mtk_live_…; MiTax stores only their SHA-256 hash, so a key is shown to you exactly once at mint time.
Requests without a valid, active key return 403 forbidden. That is the correct state before onboarding, not an error to work around.
Base URL
https://<your-project>.functions.supabase.co/tax-rails/<verb>
| Endpoint | Required scope | Purpose |
|---|---|---|
POST /tax-rails/calc | tax:calc | Calculate tax owed |
POST /tax-rails/file | filing:initiate | Create a filing |
POST /tax-rails/pay | payment:initiate | Initiate a payment handoff |
tax:calc alone (a pricing/quote integration), or all three for full calculate-file-pay automation. Scopes are granted during onboarding.
Request & response.
Each call names a customer_ref, your own identifier for a customer you provisioned and we linked to a consented MiTax taxpayer profile. You can also route by a body action field instead of the path.
Request
{
"customer_ref": "acme-cust-42",
"tax_type": "vat", // pit | vat | cit | paye | wht | cgt
"inputs": {
"output_vat": 750000,
"input_vat": 120000,
"period": "2025-08"
}
}
Response
{
"ok": true,
"total_kobo": 63000000,
"breakdown": [ /* per-line, from the MiTax engine */ ],
"calculation_id": "6f1c…", // feed this into /file
"request_id": "a2e9…",
"tax_type": "vat"
}
PIT uses inputs.gross, inputs.annual_rent, inputs.pension, inputs.nhis, and other relief fields. The vat/cit/paye/wht/cgt family returns a calculation_id, the token /file requires.
Request
{
"customer_ref": "acme-cust-42",
"tax_type": "vat",
"period_type": "monthly", // monthly | annual
"year": 2025,
"month": 8,
"calculation_id": "6f1c…" // from /calc, same customer + period
}
Response
{
"ok": true,
"filing_id": "b73a…",
"amount_due": 630000,
"deadline": "2025-09-21",
"amount_source": "server_verified",
"request_id": "c41d…"
}
For vat/cit/paye/wht/cgt, calculation_id is mandatory and must match the same customer, tax type and period. MiTax will not file a self-declared amount. pit is recomputed from the linked profile’s stored income.
Request
{
"customer_ref": "acme-cust-42",
"filing_id": "b73a…" // from /file
}
Response
{
"ok": true,
"intent": {
"id": "9d20…",
"amount": 630000, // outstanding tax
"fee": 6300, // MiTax service fee
"total": 636300, // what the taxpayer transfers
"status": "pending"
},
"never_holds_funds": true,
"settlement": "customer_to_psp_to_government",
"request_id": "e88f…"
}
MiTax never holds funds.
/pay does not move money. It returns a server-authoritative payment intent: the outstanding tax recomputed from the filing, plus MiTax’s service fee. That intent is the checkout handoff.
The taxpayer settles it by bank transfer or card through the payment service provider. Funds flow directly customer → licensed provider → government; MiTax records the payment and its fee but never takes custody of the money.
- calc returns the tax the customer owes.
- file creates the obligation and a deadline.
- pay returns the amount + fee as a PSP handoff.
- The PSP collects and remits to Government; the webhook settles the intent.
Predictable, per-key limits.
Each key has its own rolling-minute cap (set when the key is minted, typically 60 to 120 requests/minute). Exceed it and the API returns 429 rate_limited; back off and retry. Every response carries a request_id for support and reconciliation.
| HTTP | error | Meaning |
|---|---|---|
| 401 | missing_api_key | No X-API-Key header. |
| 403 | forbidden | Unknown, revoked, or suspended key. |
| 403 | insufficient_scope | Key lacks the scope for this verb. |
| 429 | rate_limited | Over the key’s per-minute cap. |
| 404 | customer_not_found | No customer for that customer_ref. |
| 404 | customer_not_linked | Customer not yet linked to a MiTax profile. |
| 400 | calculation_id_required | File a VAT/CIT/PAYE/WHT/CGT return with a calc id. |
| 400 | plan_required | The linked customer’s plan does not cover this tax. |
Build MiTax into your product.
Rails access is onboarded, not self-serve. Tell us what you’re building and we’ll provision your partner record, scopes and key.
- Strictly non-custodial
- Scoped, hashed API keys
- Server-authoritative amounts