Skip to content

Admin, Approvals & System API

Staff users, roles & permissions, audit, communications, internal settings, finance config, storage upload, and approvals routes.


Users (staff)

Handlers: handleUsers at src/lib/api.ts:12968, handleUsersById at src/lib/api.ts:13056, handleUserPassword at src/lib/api.ts:13075, handleUserRoles at src/lib/api.ts:13103.

Route Method Permission Purpose
/users GET (read-only) List staff users + their roles
/users POST admin.users.create Provision staff user (email must end in @alhuda.co.in or @alhudatravels.in)
/users/:id DELETE admin.users.delete Soft-delete user
/users/:id/password POST Admin resets user password via Supabase admin API
/users/:id/roles POST Replace user's role assignments

POST /users

Cite: src/lib/api.ts:13021.

Calls the admin-users Supabase edge function (requires the service role key server-side). Enforces domain restriction on staff email: only @alhuda.co.in and @alhudatravels.in are accepted.

Returns{ id, tempPassword }.

POST /users/:id/roles

Cite: src/lib/api.ts:13103. Accepts body shapes:

  • { roleId } — single UUID
  • { role } — single name or UUID
  • { roles: [...] } — array of names or UUIDs

Resolves names to IDs against the Role table. Deletes existing UserRole rows and inserts the new ones in one transaction.


Permissions admin

Handler: handlePermissions at src/lib/api.ts:12519. The first line is await requireAdminUser() — every sub-route is additionally behind admin gating.

Route Method Permission Purpose
/permissions GET requireAdminUser List the permission catalog
/permissions/roles GET requireAdminUser Roles with their permission grants
/permissions/roles/:roleId PUT admin.permissions.edit Replace a role's RolePermission rows
/permissions/users GET requireAdminUser Users with roles + overrides
/permissions/users/:userId PUT admin.permissions.edit Replace a user's role assignments and UserPermission overrides

Agents (partner admin)

See CustomersGET/POST/PATCH/DELETE /agents are documented there. Admin-only uses of /agents (bulk status changes, partner approval on pending signups) flow through PATCH /agents/:id with status='active'|'inactive'|'suspended'.


Audit log

Handler: handleAudit at src/lib/api.ts:12457.

Route Method Permission Purpose
/audit GET (read-only) Audit log with optional ?entityType=&entityId=; resolves user names and booking numbers
/audit POST finance.create Append an external audit entry
/admin/audit-log GET, POST same as /audit Alias

Permission is finance.create — historical

Writing to the audit log requires finance.create by convention; a future refactor may introduce a dedicated audit.write permission.


Communications

Handlers: handleOperationsCommunications at src/lib/api.ts:13180, handleCommunicationQueue at src/lib/api.ts:3181.

Route Method Permission Purpose
/operations/communications GET (read-only) Filter by ?entityType=&entityId=&limit=
/operations/communications POST admin.users.edit Log a communication; optionally dispatch via WhatsApp/email now or schedule for later
/communications/queue GET (read-only) Inspect the scheduled communications queue

Dispatch semantics

Cite: src/lib/api.ts:13200-13360. When channel is whatsapp or email:

  • If sendNow === false, requires a future scheduledFor datetime. Inserts into CommunicationQueue with status='pending'.
  • If sendNow !== false, dispatches via sendMailer({ type, channel, phone|to, templateData }) and logs into CommunicationsLog with the provider name tagged in notes.
  • Phone / email are resolved via resolveCommunicationTarget(entityType, entityId) from the related booking / agent / customer, or taken from the body directly.

Daily stats

Inline handler at src/lib/api.ts:28065. GET /admin/daily-stats with optional ?date=YYYY-MM-DD (defaults to today).

Returns — bookings created/approved/rejected/totalAmount, payments count/totalReceived/ verified/pending, customers created/total, groups active/departing7d, finance journal count + totals (debit/credit), visa applied/issued/pending. Read-only; no permission check ().


Currencies

Inline handler at src/lib/api.ts:28124. GET /admin/currencies returns active rows from the currencies table (code, name, symbol).


Finance config

Inline handler at src/lib/api.ts:28166.

Route Method Permission Purpose
/admin/finance-config GET (read-only) Read FinanceConfig singleton (sanitized for client)
/admin/finance-config PUT finance.edit Update allowed fields (prefixes, default currency, GST rate, GL account IDs); invalidates cache

The allowlist of mutable fields is fixed (see src/lib/api.ts:28173-28190). Fields not in the list are ignored. After update, assertFinanceConfigAccountTypes validates that every GL account ID maps to the right account type (ASSET/LIABILITY/etc).


Communication settings

Inline handler at src/lib/api.ts:28205.

Route Method Permission Purpose
/admin/communication-settings GET (read-only) List all settings keys
/admin/communication-settings PUT admin.edit Upsert { settings: [{key, value}] }

Stores sender-side credentials and template defaults (SMTP, WhatsApp API keys, etc) in the CommunicationSetting table keyed by key.


Storage upload

Inline handler handleStorageUpload at src/lib/api.ts:27724.

Route Method Permission Purpose
/storage/upload POST authenticated (no named permission) Upload a file; returns { storageKey, provider, fileName }

Returns — reference tokens used by ticket/visa/customer document handlers. The file is stored in the configured provider (Supabase Storage or Google Drive) and ownership is enforced by the downstream handler that associates the upload with an entity.


Requests (service requests)

See Bookings/requests, /requests/:id, /requests/:id/notes are documented there.


Portals

Partner (/portals/agent/*) and customer (/portals/customer/*) portal routes are documented in Bookings. They are JWT-authenticated and row-scoped by resolving the caller's Agent or Customer row — no permission checks.


Internal / maintenance endpoints

Route Method Permission Purpose
/finance/ledger POST admin.edit Rebuild derived ledger rows (mode: 'rebuild_quota_blocks')
/finance/stock-adjustment POST finance.create Manual inventory write-up / write-down
/finance/supplier-transactions GET finance.view Dump of all SupplierTransaction rows

Deprecated endpoints

These routes throw immediately — do not use.

Route Method Error
/accounts POST 400 — "Create GL accounts from Finance → Accounts..."
/accounts/:id PATCH 400 — same

Use /finance/accounts instead (see Finance).


Unmigrated endpoints

When the dispatch cascade falls through without a match, the router throws ApiError(501, { message: 'Unmigrated endpoint: <METHOD> <path>. This UI route still expects the old /api backend. Migrate it to supabase.from(...).' }). Cite: src/lib/api.ts:28235.

Typical cause — a frontend service calling an old REST path before someone adds the dispatch entry. The fix is to add the handler in _apiFetchInternal per the checklist in the Overview.