Your role
Three roles share the platform and the boundaries between them are enforced, not advisory. Businesses record their own trade. Tax lawyers review and file. You run the platform underneath both.
What belongs to you alone:
- Creating a business and its single login.
- Storing and rotating FBR tokens, which nobody else can read.
- Assigning tax lawyers to businesses.
- Moving a business from sandbox to production.
- Syncing FBR reference data for the whole platform.
- Reading the audit log and the raw request and response of every FBR call.
- Suspending or reactivating a business.
You do not record trade and you do not approve invoices. Those belong to the business and the lawyer. Your access is to the platform, not to the accounting decisions made on it.
Signing in and finding your way
Go to /admin.
Dashboard
- Platform overview
Platform
- Businesses
- Tax Lawyers
FBR
- Reference data
- Submissions
- FBR monitoring
- Sandbox scenarios
Monitoring
- Audit Log
There are three panels in the application and they are separate front doors: /admin for you, /lawyer for reviewers, /app for businesses. An account signs in to exactly one of them.
The platform dashboard
| Figure | Meaning |
|---|---|
| Businesses | Total, and how many are active. |
| On production | How many are filing live with FBR. |
| In sandbox | Still working through scenarios. |
| Not configured | No FBR credentials yet. Your onboarding backlog. |
| Tax lawyers | How many, and their combined pending review count. |
| Submissions today | Filings made, split by accepted and rejected. |
| Acceptance rate | Rolling health of the integration. |
| Unresolved unknowns | Submissions still awaiting reconciliation. |
| Reference data age | When the FBR lists were last synced. |
Below the figures: submissions over time, acceptance rate by business, the most frequent error codes, and the businesses with the largest review backlogs.
Onboarding a business
Platform โ Businesses โ New business.
Identity
Legal name, NTN or CNIC, STRN, province, address. These are sent to FBR on every invoice, so they must match the registration exactly.
Activity and sector
Business nature and sector. Together they decide which sandbox scenarios apply, so a wrong answer here produces the wrong scenario list.
The login
One account per business: name, email, password. There are no sub-users by design.
Assign a tax lawyer
Without one, the business can issue invoices but nothing can ever be filed.
FBR credentials
Enter the sandbox token. Production comes later.
Save
The chart of accounts, expense categories and the applicable scenario list are all created automatically.
What gets created for them: thirty-four ledger accounts covering assets, liabilities, equity, income and expenses, including the sales tax payable and input tax accounts the FBR flow needs; eleven expense categories; and the sandbox scenario checklist for their activity and sector.
NTN, STRN and province flow onto every filed invoice. Correcting them after filing means credit notes, not edits.
FBR credentials
Each business holds two tokens, sandbox and production, issued from the FBR IRIS portal by the business itself. They are encrypted at rest with the application key and decrypted only inside the service that makes the call.
Rules the system enforces
- A token is never rendered in any screen, in either panel, including yours. You can replace one; you cannot read one back.
- Tokens are never logged. Request payloads are stored for the audit trail with the authorisation header stripped.
- The environment on the business decides which token is used. There is no per-submission override.
Rotating a token
Open the business, paste the new token, save. The change is recorded in the audit log as an event without its value. Rotate whenever a token expires, when someone with access leaves, or on any suspicion of exposure.
Have the business reissue it in IRIS, paste the new one here, and check the submissions list for filings you cannot account for.
Sandbox scenarios
FBR โ Sandbox scenarios. FBR requires a business to submit successfully against each scenario that matches its activity and sector before it may go live. The platform holds all twenty-eight scenarios, SN001 through SN028, and selects the applicable subset automatically. IRIS issues fewer than the published table (it has dropped SN015, SN021 and SN022); if the list on this page differs from the "Eligible Scenarios" tile in IRIS, correct the business nature or sector and use Refresh applicable scenarios. IRIS treats a business ticked as Importer, Exporter and Distributor as a Distributor.
| Code | Covers |
|---|---|
SN001 | Goods at standard rate to a registered buyer. |
SN002 | Goods at standard rate to an unregistered buyer. |
SN005 | Goods at a reduced rate. |
SN006 | Exempt goods. |
SN007 | Zero-rated goods. |
SN008 | Third schedule goods, taxed on retail price. |
SN016 | Processing or conversion of goods. |
SN017 | Goods with federal excise charged in sales tax mode (petroleum products, a per-litre duty on top of 18%). |
SN024 | Goods listed in SRO 297(I)/2023 at 25%. |
SN026, SN027, SN028 | Sales to an end consumer by a retailer: standard rate, third schedule and reduced rate respectively. |
Each row shows its status โ pending, passed or failed โ with the FBR number that last exercised it and, under Last result, FBR's own words where it failed. The Runner column says Automatic when the platform knows how to file that scenario itself and Manual when it does not; hover a Manual badge for the reason. A steel manufacturer gets the steel-sector scenarios; a services business gets the service ones. You do not pick the list.
Running them
You pass the scenarios yourself from this page; the business does not need to be involved. Before the first run the business needs its NTN or CNIC, its province and its sandbox token. Then:
- Run on a row files one test invoice for that scenario: the platform creates a sandbox test buyer (registered or unregistered as the scenario needs) and a sandbox test product with the right HS code, rate, SRO reference, unit and retail price, raises a ten-unit invoice, checks it with FBR's validation service, approves it in your name and posts it. Accepted means passed; the FBR number is shown on the row.
- Run all pending does the same for every pending or failed automatic scenario, in code order, and stops at the first one FBR rejects so you can read the reason.
- A rejection cancels the test invoice and records FBR's message on the row. Fix the cause and run it again; a failed scenario can be re-run any number of times.
Test invoices are real documents: they post to the business's ledger and appear in its lists marked Sandbox test, as do the test buyer and products. That is deliberate โ FBR must see a genuine filing โ and it is why the next step exists.
Purging the test data
Purge sandbox test data removes every sandbox test invoice with its journal entries, stock movements, reviews and FBR records, then the test products and customers. The scenario results are kept. It refuses while a test invoice is still in flight to FBR or has a credit note or receipt against it. Run it once every scenario has passed and before the business trades for real; Promote to production will not proceed while test invoices remain.
Scenarios marked Manual have no recipe yet because they have not been verified against FBR's sandbox. They still have to be passed: create the invoice by hand in the business's panel, have the lawyer approve and file it, and the row updates on acceptance.
Moving a business to production
Confirm all five before you switch:
- Every applicable scenario shows as passed.
- The sandbox test data has been purged (see the previous section).
- The business has obtained its production token from IRIS โ it appears there automatically once the last scenario passes โ and you have entered it with Set FBR tokens.
- Identity details โ NTN, STRN, province โ match the registration exactly, and the IP whitelisted in IRIS is the server's outbound address.
- A tax lawyer is assigned and actively reviewing.
Then use Promote to production on the Sandbox scenarios page. It is disabled until the scenarios are complete and a production token exists, and it refuses while sandbox test invoices remain. From that moment every approved document is filed live. The date of the change is recorded on the business and in the audit log.
A production filing is a legal record. It cannot be deleted, only corrected by credit note. Do not switch a business over to clear a backlog.
Tax lawyers
Platform โ Tax Lawyers. Create the account with a name, email and password, then assign businesses to it. A lawyer sees exactly the businesses assigned to them and nothing else.
The list shows each lawyer's assigned client count, pending review count and oldest waiting document, which is how you spot an overloaded reviewer before clients start complaining.
Reassigning
Assignment can be changed at any time and takes effect immediately. Work already reviewed keeps the name of the lawyer who reviewed it, permanently. When a lawyer leaves, reassign their clients first, then deactivate the account; deactivating leaves their historical decisions intact and attributed.
Reference data
FBR โ Reference data holds FBR's own lists, cached locally so forms and validation never guess: provinces, document types, HS codes, SRO items, transaction types and units of measure.
Nothing in the interface hardcodes these values. A province dropdown, a unit of measure, an HS code lookup โ all read from this cache.
Syncing
A scheduled sync runs daily at 02:30 and pulls every list using the first available business token. Sync now on this screen runs it immediately, which is what you want after FBR publishes changes. Each type reports how many rows it wrote, or the error if it failed; one list failing does not stop the others.
The data is considered stale after twenty-four hours and the dashboard says so. Stale reference data is not an outage โ the lists change rarely โ but it is worth clearing before a busy filing period.
Reference data is fetched with a real business's credentials. Until at least one business has a working token, the sync reports that it has nothing to authenticate with.
Submissions
FBR โ Submissions is the record of every filing attempt the platform has made, for every business. Filter by business, environment, status, date range or error code.
Each submission holds the document it belongs to, the environment, the idempotency key, the attempt count, the FBR invoice number and filing date where accepted, the error code and message where rejected, the full request and response payloads, and the timestamps of the last attempt and completion.
Statuses
| Status | Meaning | Action |
|---|---|---|
| Accepted | FBR issued an invoice number. | None. |
| Rejected | FBR refused it with a code. | The lawyer handles it. |
| Unknown | Timeout or unreadable response. | Reconciled hourly. Investigate only if it persists. |
| Pending | In flight. | None. |
Every attempt also writes a call log holding the endpoint, HTTP status, duration in milliseconds, payloads and any exception. When someone disputes what was sent, this is the answer.
An idempotency key is built from the document and environment, so a retried submission can never produce a second filing of the same invoice.
FBR monitoring
FBR โ FBR monitoring is the health view of the integration as a whole: submission volume over time, acceptance rate, average response time, the most frequent error codes across all businesses, unresolved unknowns, and the reference data age.
Reading it
- Acceptance rate falling across every business points at FBR or at stale reference data, not at one client.
- Acceptance rate falling for one business points at their product tax profiles. Tell their lawyer.
- Response times climbing usually means FBR is under load. The retry policy absorbs it.
- Unknowns accumulating means reconciliation is not running. Check the scheduler.
Reconciliation runs hourly, asking FBR about every submission left unknown and settling each one against FBR's own record.
The audit log
Monitoring โ Audit Log records every meaningful change made anywhere on the platform: who, what, when, and the before and after values.
What is logged: document creation and every state change, review decisions with their comments, all FBR submissions and their outcomes, period locks and unlocks with reasons, master record changes, credential changes as events without values, business and lawyer administration, and logins.
Filter by business, user, action, model or date range. Each entry links to the record it changed.
The log cannot be edited or deleted through the application by anybody, including you. That is what makes it worth having.
Suspending a business
Open the business and clear Active. The login stops working immediately. All data, filings and audit history remain untouched, and reactivating restores access exactly as it was.
Suspension is the right tool for non-payment or a dispute. There is no delete, deliberately: filed invoices are legal records and the audit trail has to survive the commercial relationship.
How the system is built
A single Laravel application, organised as a modular monolith. Business logic lives in domain services; the Filament resources that draw the screens stay thin and call into them.
| Area | Responsibility |
|---|---|
| Accounting | Posting, period locking, document numbering, the document services, opening balances, bank reconciliation, reports. |
| Inventory | Receiving, issuing, reversal and valuation at weighted average cost. |
| Fbr | The client, payload builder, submission service, credential resolver, scenario resolver, reference sync, lifecycle transitions, PDF rendering. |
| Reviews | Request changes, reject, approve, approve and submit. |
| Imports / Exports | Spreadsheet reading, validation, templates, report exports. |
| Analytics | Dashboard figures and notifications. |
Invariants worth knowing
- One writer for the journal. Every entry goes through the posting service, which validates the lines, proves debits equal credits, asserts the period is open and numbers the entry sequentially. Nothing else writes journal entries.
- Money is never a float. Amounts are decimal strings handled with arbitrary-precision arithmetic, two places for money and four for unit costs, rounded half away from zero.
- Tenancy is a global scope. Every business-owned model carries a trait that scopes reads and fills the business on write. A missing filter yields no rows rather than someone else's rows.
- Document numbering is gapless. Numbers are issued under a row lock, so two concurrent invoices cannot take the same one.
- Lifecycle transitions are guarded. A document can only move along its defined path, and the check is in the service, not the interface.
Roughly five hundred automated tests cover the accounting engine, inventory costing, the FBR flow, the review workflow, imports and tenant isolation.
Scheduled work
| Task | When | Purpose |
|---|---|---|
| Reference data sync | Daily, 02:30 | Refresh FBR's lists. |
| Submission reconciliation | Hourly | Settle unknown submissions against FBR. |
Both need the scheduler running. On a server that means one cron entry calling the scheduler every minute:
* * * * * cd /path/to/app && php artisan schedule:run >> /dev/null 2>&1
A queue worker is also required, because spreadsheet checking, PDF rendering and notifications are queued. If imports sit at pending forever, the worker is not running.
Installing and running it
Requirements: PHP 8.4 or newer with the bcmath, gd and intl extensions, Composer, Node and npm, and MySQL 8 or newer.
composer install
npm install
cp .env.example .env
php artisan key:generate
Set the database connection in .env, then:
php artisan migrate --seed
composer dev
The seeders create an administrator, two tax lawyers and five demo businesses with about ninety days of trade, several hundred invoices and a filled FBR submission history, so every screen has something real on it.
Useful commands:
| Command | Does |
|---|---|
composer dev | Serve the app, run the queue worker and build assets together. |
php artisan test | Run the full test suite. |
vendor/bin/pint | Apply the code style. |
php artisan fbr:sync-reference-data | Sync FBR's lists immediately. |
php artisan fbr:reconcile | Reconcile unknown submissions immediately. |
php artisan migrate:fresh --seed | Rebuild the database from scratch. Destroys all data. |
Before you deploy
Work through this list. The first item is not optional.
- Delete the development login route. A local-only sign-in shortcut exists at /dev-login/{email} in the web routes. It is double-guarded by the local environment and an off-by-default flag, and it must be removed from the codebase before the application is deployed anywhere.
- Set
APP_ENV=productionandAPP_DEBUG=false. - Generate a fresh application key and keep it safe. Losing it makes every stored FBR token unreadable.
- Give the database its own user, not root, with only the privileges it needs.
- Serve over HTTPS only.
- Install the scheduler cron entry and a supervised queue worker.
- Cache configuration, routes and views.
- Arrange database backups and test a restore before you need one.
- Confirm every business starts in sandbox.
FBR tokens are encrypted with it. If it is rotated or lost, every stored token must be re-entered by hand.
Troubleshooting
| Symptom | Likely cause and fix |
|---|---|
| Submissions failing for every business. | FBR is down, or reference data is stale. Check monitoring, then sync. |
| Submissions failing for one business. | Token expired, or the environment was switched without a matching token. Re-enter it. |
| Unknowns accumulating. | The scheduler is not running. Check the cron entry. |
| Imports never leave pending. | No queue worker. Start one. |
| Reference sync reports no credentials. | No business has a working token yet. Add one. |
| A lawyer cannot see a client. | Not assigned. Assign them under Tax Lawyers. |
| A business cannot post to last month. | The period is locked. Their lawyer can unlock it with a reason. |
| Scenario list looks wrong. | Business nature or sector is wrong. Correct it and use Refresh applicable scenarios. If IRIS ticks several natures, use Distributor when Distributor is among them. |
| Run says "Scenario not run". | The business is missing its NTN/CNIC, province or sandbox token, is already on production, or the scenario has no recipe (Manual). The message says which. |
| A scenario keeps failing with the same FBR message. | Read the Last result column. "HS Code does not match" means FBR does not accept that HS code for that sale type; SRO errors mean the schedule or serial is not in FBR's current list. These are recipe problems for the developer, not business problems. |
| Promote to production is refused. | Sandbox test invoices are still in the books. Purge them first. |
| PDF is missing its QR code. | FBR has not accepted the invoice yet. The QR only exists once it has. |
Reference
Who can do what
| Capability | Admin | Lawyer | Business |
|---|---|---|---|
| Create businesses and logins | Yes | No | No |
| Hold FBR credentials | Yes | No | No |
| Assign lawyers | Yes | No | No |
| Switch to production | Yes | No | No |
| Record trade | No | No | Yes |
| Approve and file invoices | No | Yes | No |
| Post journal vouchers | No | Yes | Yes |
| Post opening balances | Yes | Yes | No |
| Lock and unlock periods | No | Yes | No |
| Read the platform audit log | Yes | No | No |
| Read FBR request and response payloads | Yes | Outcome only | Outcome only |
FBR terms
| Term | Meaning |
|---|---|
| NTN | National Tax Number. Seven digits. |
| STRN | Sales Tax Registration Number. |
| IRIS | FBR's portal, where businesses obtain their tokens. |
| HS code | Harmonised System code identifying the goods, such as 8471.3000. |
| SRO | Statutory Regulatory Order. Cited by schedule and serial to justify a non-standard rate. |
| Scenario | A sandbox test case, SN001 to SN028. |
| Sandbox test data | The buyer, products and invoices the scenario runner creates for a business. Marked in every list and removed by "Purge sandbox test data". |
| FED in ST mode | Federal excise duty collected through the sales tax return, as a fixed amount per unit on top of the percentage rate. |
| FBR invoice number | The identifier issued on acceptance, such as 7000007DI1747119701593. |
| Third schedule | Goods taxed on retail price rather than sale price. |