6. API Reference — Preview / Apply / Internal Endpoint
6.1 Root-admin controller (MultiTenancyApi)
POST api/Saas/TenantOperationalDefaults/Preview { "tenantId": "...", "correlationId": "..." }
POST api/Saas/TenantOperationalDefaults/Apply { "tenantId": "...", "correlationId": "..." }
GET api/Saas/TenantOperationalDefaults/Tenants/{tenantId}/DefaultPlan
- Controller:
TenantOperationalDefaultsController(ApiExplorerSettings(GroupName = "MultiTenancy")). - Every action requires a permission —
TenancyPermissions.TenantOperationalDefaults.Apply(Preview and Apply) or.View(theDefaultPlanread-only alias for Preview). Previewnever writes anything (no tenant DB write, no apply log).Applywrites tenant records through the bridge and always writes aTenantOperationalDefaultApplyLogrow (Saas schema), identical convention to every sibling apply flow.
6.2 Response shape
{
"succeeded": true,
"dryRun": false,
"tenantId": "247360",
"correlationId": "3b3f3243-74ea-4cb1-81aa-5c296a89b10c",
"totalRequestedKinds": 3,
"createdCount": 2,
"skippedExistingCount": 1,
"entitlementSkippedCount": 1,
"failedCount": 0,
"items": [
{ "kind": "Warehouse", "code": "DefaultWarehouse", "status": "SkippedExisting", "resolvedId": "..." },
{ "kind": "CashAccount", "code": "DefaultCashAccount", "status": "Created", "resolvedId": "..." },
{ "kind": "SalesInvoiceTemplate", "code": "DefaultSalesInvoiceTemplate", "status": "Created", "resolvedId": "..." }
],
"warnings": ["BankAccount default was skipped — tenant does not have any of the required subscription feature entitlements (Features.AccountManagement)."],
"error": null
}
status is one of WouldCreate/Created/SkippedExisting/Failed (plain string, not a shared enum —
same forward-compatibility reasoning as every sibling apply contract).
6.3 Internal bridge (SaaS → BackEnd)
POST {BackEndBaseUrl}/api/internal/tenant-operational-defaults/apply
Header: X-Shumoul-Internal-Key: {shared secret}
Header: X-Correlation-Id: {guid}
Header: TenantId: {tenant identifier}
- MultiTenancyApi side:
TenantOperationalDefaultsApplyBridgeSettings(Enabled,BackEndBaseUrl,ApplyPath,InternalApiKey,TimeoutSeconds) — sibling ofOnboardingApplyBridgeSettings, own dedicated namedHttpClient("tenant-operational-defaults-apply-bridge"), since this is a genuinely new trust boundary (spans both POS and Finance concerns, not one accounting-setup capability). - BackEnd side:
TenantOperationalDefaultsApplyInternalController([AllowAnonymous][ApiExplorerSettings(IgnoreApi = true)], hidden from Swagger) — validatesX-Shumoul-Internal-KeyagainstInternalServiceAuthSettings.TenantOperationalDefaultsApplyApiKey(a dedicated key, not reused from any sibling endpoint): missing key →401; wrong key →403; missingTenantId/emptyItems→400; success →200; service-reported failure →502. Identical sequence to every sibling internal endpoint (OnboardingInternalController,CostCenterTemplateApplyInternalController). - BackEnd service:
TenantOperationalDefaultsProvisioningService— trusts the ambientITenantInfo(never resolves/switches a tenant database itself), resolves current state for every requested kind before writing anything (the same plan a DryRun reports is the plan a real apply executes), wraps all writes in one transaction, then wiresCashierAppsSettingsandUserCashierSettings(see Chapter 7).
6.4 Entitlement resolution
ITenantOperationalDefaultsService (MultiTenancyApi) resolves entitlement per kind via
ISubscriptionFeatureEntitlementService.IsFeatureAllowedAsync — the same real, seeded SubscriptionFeature
mechanism every sibling apply flow already uses (see Chapter 4 §4.2
for the exact keys per kind). A kind the tenant is not entitled to is skipped with a clear warning
(never invented, never blocks the other entitled kinds, never fails the whole apply) — see
Chapter 9 for the live-verified example.
