Single Filing
Authentication Required
This endpoint requires a valid API key. Include your API key in the x-api-key header of every request. You can generate a test key in your developer dashboard.
The Single VAT Filing API processes an individual Value Added Tax (VAT) transaction for compliance tracking. This is optimal for point-of-sale systems or individual e-commerce checkout flows where transaction classification triggers are needed in your downstream orchestration.
Integration Flow
- 1Encrypt Credentials: Generate your
encryptedPayloadto securely carry credentials. - 2Submit Transaction: Send the itemized VAT details, amount, and related metadata.
- 3Handle Result: Track the
vat.filing.successwebhook for the final state confirmation.
HTTP Request
Request Body
The following parameters are required for this action.
| Parameter | Type | Required | Description |
|---|---|---|---|
| encryptedPayload | string | Required | Your Base64-encoded, AES-256-GCM in-flight encrypted TaxPromax credentials (email and password). See Encryption Guide for generation steps. |
| filingId | string | Required | Your unique internal ID representing this VAT filing request. Limits duplicate filings (Idempotency Key). |
| month | number | Required | Numeric month you are filing for. Valid values: 1 to 12. |
| year | number | Required | 4-digit year for the filing (e.g. 2026). |
| vatAmount | number | Required | Total VAT amount to file. |
| taxId | string | Required | Tax Identification Number (TIN) for the business entity. |
| interestAmount | number | Required | Accrued interest amount if filing past statutory deadline. |
| penaltyAmount | number | Required | Late filing penalty amount if applicable. |
| scheduleUrl | string | One of | URL of a schedule workbook you built yourself (xlsx). Use this instead of sales / purchases for large entries. Send inline rows or scheduleUrl, not both. |
| sales | array | One of | Inline sales for the VAT return (max 500 rows) โ we place them on the NRS "A - Sales" schedule. See the sales[] item below. |
| purchases | array | One of | Inline purchases for the VAT return (max 500 rows) โ we place them on the NRS C_2 purchases list. See the purchases[] item below. |
Send the schedule in one of two ways. Either inline: sales and/or purchases (up to 500 rows each), from which we build the NRS VAT schedule workbook for you. Or as a workbook you built yourself, via scheduleUrl, which is the route for large entries. Sending inline rows together with scheduleUrl, or neither, returns 400 Bad Request. Send only purchases for a nil-sales return. When you send inline rows we build and upload the workbook before accepting the request, so the response can take around 20 seconds.
sales[] item
| Parameter | Type | Required | Description |
|---|---|---|---|
| customerName | string | Required | Name of the customer. |
| customerTaxId | string | Optional | Customer Tax ID. If omitted it is filed as 0. |
| item | string | Required | Product or service name. |
| description | string | Optional | Product or service description. |
| vatStatus | string | Required | VAT treatment: standard-rated, zero-rated or vat-exempt. |
| state | string | Optional | Place of consumption: a Nigerian state name, ISO code (e.g. NG-LA) or FCT. Defaults to Lagos. An unknown state returns 400. |
| lga | string | Optional | Place of consumption: Local Government Area. Left blank if omitted. |
| amountExclVat | number | Required | Sale amount excluding VAT, filed exactly as given. Must be 0 or more. |
purchases[] item
| Parameter | Type | Required | Description |
|---|---|---|---|
| sellerName | string | Required | Name of the supplier. |
| sellerTaxId | string | Optional | Supplier Tax ID. If omitted it is filed as 0. |
| description | string | Required | What was purchased. |
| amountExclVat | number | Required | Purchase amount excluding VAT. Must be 0 or more. |
| vatStatus | string | Required | VAT treatment: standard-rated, zero-rated or vat-exempt. |
| supplyType | string | Required | goods or service. Purchases without a supply type cannot be placed on the schedule. |
Encrypted Payload (In-Flight Encryption)
FLUX requires sensitive credentials to be encrypted at the application layer before transmission. This provides defense-in-depth on top of HTTPS.
The encryptedPayload field must contain a Base64-encoded AES-256-GCM encrypted JSON object.
Encryption Specification
| Property | Value |
|---|---|
| Algorithm | AES-256-GCM |
| Key Derivation | SHA-256(sharedSecret) |
| IV (Nonce) | 12 bytes (96-bit) random |
| Auth Tag | 16 bytes (128-bit) |
| Encoding | Base64 |
Payload Format
The final transmitted value must be:
- IV is randomly generated per request
- Ciphertext is encrypted JSON
- AuthTag is automatically appended by GCM
Idempotency
The filingId you send is the idempotency key for this request. It lets you retry safely after a timeout or network error without filing, or being charged, twice. Keys are tracked per environment, so a sandbox request never collides with a production one.
| You send | You get |
|---|---|
A new filingId | The filing is accepted and charged as normal. |
The same filingId with the same payload | The original response again, with "replayed": true. Nothing is filed or charged a second time. |
The same filingId with a different payload | 409 Conflict. Use a new filingId for a different filing. |
The same filingId while the first request is still processing | 409 Conflict. Wait a moment and retry; you will then receive the replayed response. |
The same filingId after a failed request (400, 402, 403, 5xx) | Treated as a fresh request. A failed request releases the key, so you can fix the problem and retry with the same filingId. |
A replayed response looks like this:
{
"status": "accepted",
"message": "single filing accepted successfully",
"data": { ... same as the original response ... },
"replayed": true
}The payload comparison ignores encryptedPayload, so re-encrypting the same filing for a retry is still a replay. Replays are remembered for 7 days.
Response Documentation
A successful invocation of this endpoint returns a 202 Accepted or 200 OK. Since filing operations are processed asynchronously alongside tax gateways, the immediate response acknowledges successful receipt and enqueueing of the schedule payload.
| Field Name | Type | Description |
|---|---|---|
| status | string | The high-level outcome of the request context. Value is typically accepted indicating payload validation passed. |
| message | string | A human-readable confirmation note (e.g. "schedule accepted successfully"). |
| replayed | boolean | Only present, and always true, when this is a replay of an earlier request with the same filingId. Nothing was filed or charged again. See Idempotency above. |
| data.id | string | The tracking UUID for this schedule. This precise ID correlates to the filingId in downstream webhook events. |
| data.created_at | string (date-time) | Timestamp marking the instant the schedule safely enqueued, in ISO 8601 format. |
Error Handling
The API applies standard HTTP status codes indicating success or failure. In case of an anomaly, the response body contains an errors array detailing specific field-level validation issues to facilitate rapid integration debugging.
| HTTP Status | Error Key | Cause & Resolution |
|---|---|---|
| 400 | Bad Request | Cause: Missing required fields, invalid attributes (e.g. malformed TaxID), or structural JSON anomalies. Resolution: Traverse the errors array in the response to target the invalid field constraints, format properly, and re-submit. |
| 401 | Unauthorized | Cause: The API key credential is missing, malformed, or revoked. Resolution: Ensure your x-api-key is populated in the request header and that you are matching the correct environment bounds (live vs test). |
| 402 | Payment Required | Cause: Your credit balance is too low for this request. Each filing costs 2 credits, and a balance of 4 or fewer is treated as empty. Resolution: Top up your credits, then retry with the same filingId. |
| 403 | Forbidden | Cause: No active subscription for this service, or the API key belongs to a plan that does not include Flux. Sandbox keys (txsm_test_) are not affected.Resolution: Subscribe to Flux, or use a key from a Flux plan. |
| 409 | Conflict | Cause: The filingId was already used with a different payload, or the first request with this filingId is still being processed.Resolution: Use a new filingId for a different filing. If the first request is still processing, wait briefly and retry with the same payload to receive the replayed response. |
| 429 | Too Many Requests | Cause: Network volume exceeds the rate envelope associated with your tier configuration. Resolution: Gracefully throttle via exponential backoff semantics checking the Retry-After header variable, or upgrade capacity constraints. |
curl -X POST https://api.taxstreem.com/v1/flux/vat-filing/single \ -H "x-api-key: txsm_test_SK489c..." \ -H "Content-Type: application/json" \ -d '{ "encryptedPayload": "aXZJVkpTT05...YWx1ZQ==", "filingId": "filing_2343dfw", "month": 1, "year": 2026, "vatAmount": 0, "taxId": "", "interestAmount": 0, "penaltyAmount": 0, "sales": [ { "customerName": "Shoprite Nigeria Ltd", "customerTaxId": "1234567890123", "item": "Consulting retainer", "description": "March retainer", "vatStatus": "standard-rated", "state": "Lagos", "lga": "Ikeja", "amountExclVat": 900000 } ], "purchases": [ { "sellerName": "Acme Consulting Ltd", "sellerTaxId": "1234567890123", "description": "Consulting, March", "amountExclVat": 250000, "vatStatus": "standard-rated", "supplyType": "service" } ] }'
{
ย ย "status": "accepted",
ย ย "message": "single filing accepted successfully",
ย ย "data": {
ย ย ย ย "id": "fil_a1b2c3d4e5f6",
ย ย ย ย "created_at": "2026-02-20T10:00:00Z"
ย ย }
}