Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/CodexaCP/DG_BACK/llms.txt

Use this file to discover all available pages before exploring further.

Dragon Guard is a multi-tenant warehouse management API where every operational entity — items, bins, stock, receipts, shipments, movements, and RFID tags — is strictly scoped to a CompanyId. Each authenticated request must carry a resolvable company context, and the server validates that the caller actually has access to that tenant before processing any operation.

Data Isolation

All operational tables carry a CompanyId column. No cross-tenant query can succeed through normal controller paths; the company context is resolved and enforced at the base-controller level before any business logic runs.

Company context resolution

ApiControllerBase.ResolveCompanyId determines the company context for each request using the following priority order:
1

Query parameter

If a companyId query parameter is present, it is parsed as a GUID and validated against the caller’s access list.
2

X-Company-Id header

If no query parameter is found, the X-Company-Id request header is checked and parsed.
3

JWT default claim

Finally, the companyId claim embedded in the JWT is used as the fallback. If none of these sources yield a valid GUID, the request is rejected with 401 Unauthorized.
Whichever source provides the CompanyId, it must also appear in the caller’s company_access JWT claims. HasCompanyAccess enforces this check:
protected bool HasCompanyAccess(Guid companyId) =>
    IsSupremeAdmin || AccessibleCompanyIds.Contains(companyId);
SUPERADMIN_SUPREMO bypasses the company_access list entirely because IsSupremeAdmin is true when the JWT contains isSuperAdmin = true.
Regular WMS controllers are strictly tenant-scoped and are not opened to arbitrary cross-company reads, even for SUPERADMIN_SUPREMO. Supreme-only operations are exclusively exposed under /api/supreme/*.

User–Tenant Assignment Rules

  • A non-supreme user belongs to exactly one tenant. Attempting to assign a user to a second tenant returns USER_ALREADY_ASSIGNED_TO_OTHER_TENANT.
  • SUPERADMIN_SUPREMO has no operational tenant. Attempting to assign one back returns SUPERADMIN_CANNOT_HAVE_TENANT.
  • If historical data has incorrectly assigned a user to multiple tenants, login is blocked until the conflict is resolved by a supreme administrator.
If a user appears associated with more than one tenant due to corrupted or legacy data, all logins for that user will be rejected until a Supreme administrator corrects the assignment.

Tenant Lifecycle Enforcement

Dragon Guard enforces tenant status both at login time and on every subsequent request.

Login checks

Non-supreme users are rejected at login if:
  • company.IsActive = false, or
  • company.Status != "Active"
This prevents users from obtaining new tokens for suspended or deactivated tenants.

Per-request middleware

A middleware registered in Program.cs re-validates the tenant’s active status on every /api/* request. This means tokens issued before a suspension do not remain valid — the next call will be refused.
Tenant inactive  →  403 Forbidden   (messageKey: auth.companyInactive)
Tenant suspended →  402 Payment Required
Write on read-only plan  →  402 Payment Required
The following paths are exempt from the suspension check and remain accessible:
Exempt prefixReason
/api/authLogin and token refresh must still work
/api/plansTenant must be able to view plan information
/api/supremeSupreme admin operations are always permitted
GET, HEAD, and OPTIONS requests are always allowed for read-only tenants. Only mutating methods (POST, PUT, PATCH, DELETE) are blocked when a plan is in read-only status.

Company Status Model

StatusIsActiveBehavior
ActivetrueFull access; all operations permitted
InactivefalseLogin blocked; all API requests rejected with 403
SuspendedfalseLogin blocked; write operations return 402; reads may be restricted by plan
Company status is managed exclusively by a SUPERADMIN_SUPREMO through the /api/supreme/* endpoints.

Operational vs. Supreme Isolation

Path prefixWho can access
/api/* (non-supreme)Authenticated tenant users scoped to their CompanyId
/api/supreme/*SUPERADMIN_SUPREMO only — all other callers receive 403
EnsureSupremeAccess() is called at the start of every supreme controller action:
protected void EnsureSupremeAccess()
{
    if (!IsSupremeAdmin)
        throw new UnauthorizedAccessException("Supreme access is required for this resource.");
}
Never rely on frontend role visibility for supreme gate-keeping. The backend enforces isSuperAdmin = true independently of any role string presented by the client.

Build docs developers (and LLMs) love