MiTax Rails

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.

How it works

One key. Three server-to-server verbs.

calc

scope: tax:calc

Compute 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:initiate

Turn a calculation into a real filing with a computed deadline, under the server-authoritative calculation-id contract.

pay

scope: payment:initiate

Get a payment handoff for the amount due plus the MiTax service fee. Funds flow customer → licensed provider → government. MiTax never holds money.

Authentication

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>
API endpoints, required scopes and purpose
EndpointRequired scopePurpose
POST /tax-rails/calctax:calcCalculate tax owed
POST /tax-rails/filefiling:initiateCreate a filing
POST /tax-rails/paypayment:initiateInitiate a payment handoff
A key is scoped to exactly what you need. You can hold tax:calc alone (a pricing/quote integration), or all three for full calculate-file-pay automation. Scopes are granted during onboarding.
Endpoints

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.

POST /tax-rails/calc requires tax:calc

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.

POST /tax-rails/file requires filing:initiate

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.

POST /tax-rails/pay requires payment:initiate

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…"
}
Money, handled honestly

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.

  1. calc returns the tax the customer owes.
  2. file creates the obligation and a deadline.
  3. pay returns the amount + fee as a PSP handoff.
  4. The PSP collects and remits to Government; the webhook settles the intent.
Rate limits & errors

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 codes and their meaning
HTTPerrorMeaning
401missing_api_keyNo X-API-Key header.
403forbiddenUnknown, revoked, or suspended key.
403insufficient_scopeKey lacks the scope for this verb.
429rate_limitedOver the key’s per-minute cap.
404customer_not_foundNo customer for that customer_ref.
404customer_not_linkedCustomer not yet linked to a MiTax profile.
400calculation_id_requiredFile a VAT/CIT/PAYE/WHT/CGT return with a calc id.
400plan_requiredThe linked customer’s plan does not cover this tax.
Partner onboarding

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