Multi-Company
Arkan ERP supports running multiple companies within a single tenant. Each company has fully isolated data while sharing the same user accounts and platform infrastructure.
Architecture
Tenant (Organization)
-> Company A (e.g., Holding Co.)
-> Company B (e.g., Subsidiary 1)
-> Company C (e.g., Subsidiary 2)- Users belong to the tenant and can be granted access to one or more companies
- All data (transactions, records, settings) is scoped to a specific company
- Users switch between companies without logging out
Company Switching
The active company is set via the X-Company-Id HTTP header on every API request. The frontend provides a company switcher in the top navigation bar.
# Request scoped to Company A
curl -H "Authorization: Bearer TOKEN" \
-H "X-Company-Id: company-a-uuid" \
https://api.arkanerp.com/api/v1/projects
# Switch to Company B -- just change the header
curl -H "Authorization: Bearer TOKEN" \
-H "X-Company-Id: company-b-uuid" \
https://api.arkanerp.com/api/v1/projects7 Layers of Isolation
Company data isolation is enforced at every level of the stack:
| Layer | Mechanism | Purpose |
|---|---|---|
| 1. HTTP Header | X-Company-Id extraction | Identify target company |
| 2. Access Guard | CompanyGuard | Verify user has access to the company |
| 3. Application Scope | CompanyScope Prisma middleware | Auto-inject company_id in all queries |
| 4. Event Context | DomainEvent carries companyId | Ensure events are processed in correct context |
| 5. CHECK Constraint | CHECK (company_id IS NOT NULL) | Prevent null company references (27 tables) |
| 6. Foreign Keys | ON DELETE RESTRICT | Prevent cross-company cascade deletes (23 tables) |
| 7. Row-Level Security | PostgreSQL RLS policies | Database-level enforcement (8 tables) |
Even if application code has a bug, layers 5-7 prevent data leakage at the database level.
Consolidated Reporting
View aggregated data across all companies you have access to:
- Consolidated P&L — Combined revenue and expenses across companies
- Consolidated Balance Sheet — Aggregated assets, liabilities, and equity
- Cross-company dashboard — KPIs summed or averaged across companies
- Intercompany eliminations — Automatically exclude intercompany transactions from consolidated views
Intercompany Transactions
Record transactions between companies within the same tenant:
- Company A creates an intercompany invoice to Company B
- The system creates matching records in both companies:
- Company A: Accounts Receivable entry
- Company B: Accounts Payable entry
- On payment, both sides are settled automatically
- Consolidated reports eliminate these transactions to avoid double-counting
Company Settings
Each company has independent configuration:
| Setting | Description |
|---|---|
| Company name | Legal entity name |
| Currency | Base currency for transactions |
| Fiscal year | Start month for financial periods |
| Tax configuration | VAT rates and tax codes |
| Chart of accounts | Independent account structure |
| Number sequences | Invoice, PO, and document numbering |
| Logo and branding | Per-company branding on documents |
| Active modules | Each company can enable different modules |
User Access
Users are granted company access with specific roles:
User: ahmed@company.com
-> Company A: Admin
-> Company B: Member
-> Company C: (no access)A user’s role can differ per company. See Permissions for role details.
API Endpoints
| Method | Path | Description |
|---|---|---|
| GET | /companies | List companies the user can access |
| POST | /companies | Create a new company |
| GET | /companies/:id | Get company details |
| PATCH | /companies/:id | Update company settings |
| POST | /companies/:id/switch | Set active company context |