Skip to main content

Tenant Service — API Contracts

Status: populated Owner: TBD Last updated: 2026-04-18 Companion: Service Template · 05 API Design · API_PATH_CONVENTIONS

Base URL: https://api.ghasi-ehealth.{tld} Auth: Bearer JWT (RS256) except internal routes. Error envelope: { error: ERROR_CODE, message, requestId, timestamp }.

1. Tenant lifecycle (Super Admin)

MethodPathScopeSummary
GET/api/v1/admin/tenantsSUPER_ADMINList all tenants (paginated)
POST/api/v1/admin/tenantsSUPER_ADMINCreate tenant
GET/api/v1/admin/tenants/:idSUPER_ADMINGet tenant details
POST/api/v1/admin/tenants/:id/activateSUPER_ADMINActivate tenant (saga)
POST/api/v1/admin/tenants/:id/suspendSUPER_ADMINSuspend tenant
POST/api/v1/admin/tenants/:id/reactivateSUPER_ADMINReactivate suspended tenant
POST/api/v1/admin/tenants/:id/terminateSUPER_ADMINTerminate tenant (irreversible)
PATCH/api/v1/admin/tenants/:id/subscriptionSUPER_ADMINUpdate subscription tier/dates

POST /api/v1/admin/tenants — request body:

{
"slug": "kabul-central-hospital",
"legalName": "Kabul Central Hospital",
"displayName": "KCH",
"countryCode": "AF",
"contactEmail": "admin@kch.af",
"timezone": "Asia/Kabul",
"locale": "ps",
"subscriptionTier": "PROFESSIONAL",
"subscriptionStart": "2026-01-01"
}

201 Created:

{
"id": "ten_01H...",
"slug": "kabul-central-hospital",
"status": "pending",
"createdAt": "2026-04-18T00:00:00Z"
}

2. Tenant profile (Tenant Admin)

MethodPathScopeSummary
GET/api/v1/tenants/:idTENANT_ADMIN (own) or SUPER_ADMINGet tenant
PATCH/api/v1/tenants/:idTENANT_ADMINUpdate displayName, contactEmail, locale, timezone
GET/api/v1/tenants/:id/configTENANT_ADMINList all config KV
PUT/api/v1/tenants/:id/config/:keyTENANT_ADMINSet config value
DELETE/api/v1/tenants/:id/config/:keyTENANT_ADMINRemove config value

Allowed config keys: mfa_required, session_timeout_minutes, branding.primary_color, branding.logo_url, max_failed_login_attempts.

Errors: 400 TENANT_CONFIG_KEY_UNKNOWN, 403 TENANT_CROSS_TENANT.

3. Hierarchy (Tenant Admin)

MethodPathScopeSummary
GET/api/v1/tenants/:id/nodesTENANT_ADMINList all nodes (flat)
GET/api/v1/tenants/:id/nodes/:nodeId/treeTENANT_ADMINSubtree from node
GET/api/v1/tenants/:id/nodes/:nodeId/ancestorsJWTAncestor chain (used by license resolver)
POST/api/v1/tenants/:id/nodesTENANT_ADMINCreate node
PATCH/api/v1/tenants/:id/nodes/:nodeIdTENANT_ADMINUpdate node name/attributes
POST/api/v1/tenants/:id/nodes/:nodeId/archiveTENANT_ADMINArchive node

POST node request:

{
"parentNodeId": "nod_01H...",
"nodeType": "facility",
"name": "Emergency Department",
"code": "ED-01",
"attributes": { "phone": "+93799000001" }
}

Errors: 422 TENANT_NODE_CROSS_TENANT, 422 TENANT_NODE_INVALID_TYPE.

4. User profiles and membership

MethodPathScopeSummary
GET/api/v1/tenants/:id/usersTENANT_ADMINList user profiles
POST/api/v1/tenants/:id/usersTENANT_ADMINInvite user (create profile + send invitation event)
GET/api/v1/tenants/:id/users/:userIdTENANT_ADMINGet user profile
PATCH/api/v1/tenants/:id/users/:userIdTENANT_ADMINUpdate profile (name, specialty)
POST/api/v1/tenants/:id/users/:userId/membershipsTENANT_ADMINAssign user to node
DELETE/api/v1/tenants/:id/users/:userId/memberships/:nodeIdTENANT_ADMINRemove membership

5. RBAC (Tenant Admin)

MethodPathScopeSummary
GET/api/v1/tenants/:id/rolesTENANT_ADMINList roles
POST/api/v1/tenants/:id/rolesTENANT_ADMINCreate custom role
POST/api/v1/tenants/:id/users/:userId/rolesTENANT_ADMINAssign role at node
DELETE/api/v1/tenants/:id/users/:userId/roles/:roleIdTENANT_ADMINRevoke role
POST/api/v1/tenants/:id/access/evaluateJWTRBAC/ABAC evaluate — { decision, reasons }

POST /evaluate request:

{
"subjectId": "usr_01H...",
"nodeId": "nod_01H...",
"resource": "patient_chart",
"action": "read"
}

200 response:

{ "decision": "allow", "reasons": ["role:CLINICIAN grants patient_chart:read at nod_01H..."] }

6. Internal (cluster-only)

MethodPathSummary
GET/internal/tenant/tenants/:idResolve tenant by ID
GET/internal/tenant/tenants/by-slug/:slugResolve tenant by slug
GET/internal/tenant/tenants/:id/statusActive status probe
GET/internal/tenant/nodes/:id/ancestorsAncestor chain for license resolver
GET/internal/tenant/users/:userId/access-contextRoles + memberships for identity access-context
POST/internal/tenant/evaluateAuthorization evaluate for service-to-service

7. Pagination and idempotency

  • Pagination: ?page=1&pageSize=50 (max 100).
  • Idempotency: Idempotency-Key required on POST /admin/tenants/:id/activate (24h window). 409 TENANT_IDEMPOTENCY_MISMATCH on mismatch.
  • X-Request-Id echoed; Traceparent propagated.

8. Rate limits (Kong)

SurfaceLimit
POST /admin/tenants/:id/activate5/min/IP
POST /api/v1/tenants/* (Tenant Admin writes)120/min/tenant
GET /api/v1/tenants/*600/min/tenant