Skip to content

API

Stack: Laravel 12, MySQL, Redis (queue + cache), Reverb (WebSockets), Stancl Tenancy, Sanctum, Spatie Laravel Permission.

The API is the hub: the admin panel, POS frontend, and storefront all talk to it, and it owns both the central database and every tenant’s database. See Architecture for the multitenancy model this is all built around.

File Scope Notes
routes/api.php Central, no tenant header Tenant resolution, Tuma callback, loads routes/admin.php
routes/admin.php Central, admin-only Everything the admin panel calls
routes/tenant.php Tenant, requires X-Tenant-ID Everything the POS frontend calls; middleware api, InitializeTenancyByRequestData, CheckSubscriptionStatus

Business logic lives in modules/{ModuleName}/ rather than in one flat app/. See Modules for the per-module business-logic docs (in progress).

  • Registered in config/modules.php’s enabled array.
  • App\Providers\ModulesServiceProvider merges each module’s config, registers its service provider, loads its migrations, views, and lang files.
  • A module in tenantRouteModules (currently: Inventory, StockTransfer, ReceiptSettings, Analytics, Tuma, Etims, CreditNotes) has its routes required directly from routes/tenant.php rather than auto-loaded. Everything else loads its own Modules/.../Routes/* with standard api middleware.

Full module list today: Accounting, Analytics, Approvals, CreditNotes, Ecommerce, Etims, HR, Integrations, Inventory, Jobs, Pdfs, Production, Promotions, Purchasing, ReceiptSettings, Repair, SaleCustomers, Sales, StockTransfer, Tax, Tuma.

Sanctum, token-based, shared shape across Admin and POS. Inside a tenant, authorization is Spatie Laravel Permission, seeded per tenant on creation.

Queue workers you’ll typically need running locally, depending on what you’re touching:

Terminal window
php artisan queue:work --tries=3 --timeout=90 # default
php artisan queue:work central --queue=backups --tries=3 --timeout=90 # DB backups (central queue connection)
php artisan queue:work redis --queue=inventory-import --tries=2 --timeout=200 # stock CSV import chunks
php artisan queue:work --queue=platform-migrations --tries=1 --timeout=3600 # platform architecture migration backfills
php artisan reverb:start --debug # WebSocket server

Central DB and every tenant DB back up to Cloudflare R2, scheduled at 07:00, 12:00, 14:00, 23:00 EAT. Backup jobs run on the central queue connection. Manual triggers live in Admin → Backups. Only super_admin can restore; central restores go only to a staging database. See docs/PRODUCTION_BACKUPS_AND_SCHEDULER.md in the API repo for production Supervisor/cron setup.

Read Conventions → Database safety first. There’s a real incident behind that rule, not a hypothetical one.