Skip to main content
Version: Latest

10. Internal Apply Bridge

10.1 Purpose​

Shumoul.MultiTenancyApi generates the settings patch but cannot write it — only Shumoul.BackEnd can resolve a tenant's own database and call IAppSettingService. The internal apply bridge is the one narrow HTTP call that connects them.

Shumoul.MultiTenancyApi (HttpOnboardingTenantSettingsApplier)
→ POST {BackEndBaseUrl}/api/internal/onboarding/apply-settings-patch
Header: X-Shumoul-Internal-Key: {shared secret}
Header: X-Correlation-Id: {guid}
Header: TenantId: {tenant identifier}
→ Shumoul.BackEnd (OnboardingInternalController)
→ InternalOnboardingSettingsPatchService.ApplyAsync(...)
→ IAppSettingService

This endpoint is service-to-service only. It is not part of the public API surface, must never be called by Angular, and is deliberately unreachable by any user JWT — it doesn't accept one at all.

10.2 Request contract​

{
"tenantId": "555001",
"tenantIdentifier": null,
"sessionId": "9f250000-9d35-f628-3eb5-08dedcf802a3",
"settingsPatchJson": "{\"CashierAppsSettings\":{\"Enable_Cash_Sales\":true}}",
"requestSource": "Shumoul.MultiTenancyApi",
"correlationId": "0d4c21c8-5ed5-4c19-9862-89c410bdf2a3"
}

tenantIdentifier is reserved/unused in this codebase — tenant resolution is entirely driven by the TenantId header (see §10.4) and the tenantId field is only used for the hard-fail consistency check.

10.3 Response contract​

{
"succeeded": true,
"sessionId": "9f250000-9d35-f628-3eb5-08dedcf802a3",
"tenantId": "555001",
"beforeSettingsJson": "{...}",
"afterSettingsJson": "{...}",
"appliedPatchJson": "{\"CashierAppsSettings\":{\"Enable_Cash_Sales\":true}}",
"appliedGroups": ["CashierAppsSettings"],
"ignoredGroups": [],
"warnings": [],
"errors": [],
"correlationId": "0d4c21c8-5ed5-4c19-9862-89c410bdf2a3"
}

10.4 Security model​

CheckBehavior
Missing X-Shumoul-Internal-Key header401 Unauthorized
Header present but doesn't match InternalServiceAuthSettings:OnboardingApplyApiKey403 Forbidden
Key valid, but the resolved tenant (from the TenantId header, via Shumoul.BackEnd's own HeaderStrategy) doesn't match the request body's tenantId502 Bad Gateway, "Tenant could not be resolved." — before IAppSettingService is ever touched
sessionId empty or tenantId blank400 Bad Request

The tenant-mismatch check exists specifically so a bad or missing TenantId header can never silently fall through to the wrong tenant's — or the host's own — database. This is enforced by InternalOnboardingSettingsPatchService.ApplyAsync's very first check: _tenantInfo is null || _tenantInfo.Id != request.TenantId.

10.5 Hidden from Swagger​

OnboardingInternalController carries [ApiExplorerSettings(IgnoreApi = true)] — it never appears in Shumoul.Api's generated swagger.json, so it won't show up for API consumers browsing the documented surface. This was independently re-verified (0 matches for apply-settings-patch in the full generated document) as part of the Phase 1.4–1.6 security hardening passes.

10.6 Correlation​

Every call carries an X-Correlation-Id (generated by the caller if not already present); both hosts log it alongside tenant id, session id, applied/ignored group counts, and warning/error counts — use it to trace a single apply attempt across both hosts' logs.