5. Controllers & Endpoints
All three controllers live in Shumoul.Framework.EInvoicing.Api (the variant consumed by Shumoul.Saas.Api),
inherit VersionNeutralApiController ([ApiController], [ApiVersionNeutral]), and are mounted into the
host process via the IncludeApi() MVC Application Part mechanism — no controller code exists inside
Shumoul.Api itself. [ApiVersionNeutral] also means these routes live outside the platform's
api/v{version}/... scheme entirely, at bare /api/EInvoicing/....
Convention deviation: none of these 12 endpoints use the platform's [MustHavePermission] attribute —
the only authorization present is class-level [Authorize] (authentication only, no permission gate). This
is a deviation from every other Shumoul controller convention.
5.1 EInvoicingController — [Authorize], api/EInvoicing
| Method | Route | Auth | Request | Response | Delegates to |
|---|---|---|---|---|---|
| POST | api/EInvoicing/Generate | [Authorize] only | UblInvoiceDto | Result<SubmittedInvoiceResult> (200) / InvalidModelResult<List<ValidationRule>> (400) / ErrorResult (500) | IInvoiceProcessFlowService.GenerateAndSubmit(invoice) |
| GET | api/EInvoicing/download-file?fileName= | [Authorize] only | query fileName | raw byte[] (application/xml) or 400 | Reads ./Files/Invoices/Signed/{fileName}.xml |
The download-file endpoint's fileName handling was hardened in Phase 0.2 — see
Chapter 7 — Security § Path Traversal.
5.2 EInvoicingSettingsController — [Authorize], api/EInvoicing/Settings
| Method | Route | Auth | Request | Response | Delegates to |
|---|---|---|---|---|---|
| GET | .../VatRegistration | [Authorize] only | none | Result<VatRegistrationSettings> | GetAppSetting<VatRegistrationSettings>() |
| POST | .../VatRegistration | [Authorize] only | VatRegistrationSettings | Result | Validated, then SetAppSetting<T> |
| GET | .../Integration | [Authorize] only | none | Result<IntegrationSettings> (OTP field redacted, Phase 0.2) | GetAppSetting<IntegrationSettings>() |
| POST | .../Integration | [Authorize] only | IntegrationSettings | Result | SetAppSetting<IntegrationSettings>() — no validator call, unlike the other setters |
| GET | .../ConfigrationMode (sic) | [Authorize] only | none | Result<ConfigModeSettings> | GetAppSetting<ConfigModeSettings>() |
| POST | .../ConfigrationMode (sic) | [Authorize] only | ConfigModeSettings | Result | IMediator.Publish(new SwitchToProductionModeEvent(request)) |
| GET | .../Address-Details | [Authorize] only | none | Result<AddressDetailsSettings> | GetAppSetting<AddressDetailsSettings>() |
| POST | .../Address-Details | [Authorize] only | AddressDetailsSettings | Result | Validated, then SetAppSetting<T> |
| GET | .../Additional-Info | [Authorize] only | none | Result<AdditionalInfoSettings> | GetAppSetting<AdditionalInfoSettings>() |
| POST | .../Additional-Info | [Authorize] only | AdditionalInfoSettings | Result | Validated, then SetAppSetting<T> |
ConfigrationMode (missing the "u" in "Configuration") is the actual, live route string — not a
documentation typo. See Chapter 11 — Technical Debt.
The six secret-bearing/state-bearing settings classes (ComplianceChecksSettings, ComplianceCsidSettings,
ConfigModeSettings (read-write above but not validated), FatooraSecretSettings, ProductionCsidSettings,
X509CertifcateSettings) have no controller endpoint at all — read/written only internally by SDK
services.
5.3 EInvoicingOnboardingController — [Authorize], api/EInvoicing/Onboarding
| Method | Route | Auth | Request | Response | Delegates to |
|---|---|---|---|---|---|
| POST | api/EInvoicing/Onboarding | [Authorize] only | Headers: type (OnboardingRequestType), Otp (string) — no body DTO | Result (200) / ErrorResult (406) | IOnboardingProcessFlowService.Initialize(type, Otp) |
See Chapter 9 — Onboarding for the full onboarding sequence this endpoint drives.
5.4 Mounting Mechanism (Recap)
Startup.cs in .Api: AddEInvoicingControllers() calls
services.AddMvc().AddApplicationPart(Assembly.Load(new AssemblyName("Shumoul.Framework.EInvoicing.Api"))),
invoked from the .IncludeApi() fluent-chain step (see Chapter 2 — Architecture). No
route prefix, area, or API versioning is applied beyond each controller's own hardcoded [Route] attribute.
5.5 TenancyApi Variant — Near-Identical Duplication, Not Parameterized Multi-Tenant Admin
Shumoul.Framework.EInvoicing.TenancyApi exposes the same three controllers under api/Saas/EInvoicing/*
(SaasEInvoicingController, SaasEInvoicingSettingsController, SaasEInvoicingOnboardingController) — line
-for-line identical business logic, differing only in route prefix, ApiExplorerSettings GroupName,
namespace, and which Tenancy-suffixed types are imported. Neither variant accepts a tenant identifier as
a route or body parameter — the real difference is entirely in DI wiring (which
IEInvoicingAppSettingService implementation is resolved in which host process), not in the controller
surface itself.
5.6 SignalR
None. No Hub class or IHubContext usage exists anywhere in the EInvoicing projects, and none of the
platform's three existing Hubs (WhatsAppHub, NotificationHub, BackgroundJobsHub) is connected to
invoices or e-invoicing in any way.
5.7 Confirmation — No ERP Controller Calls Into the SDK
A repository-wide search of every Shumoul.Api controller and every Shumoul.Application service for
ZATCA-related types returned zero matches. SalesInvoiceService.cs contains no reference to
IInvoiceProcessFlowService, GenerateAndSubmit, or any EInvoicing type. The only way to reach the SDK today
is the standalone /api/EInvoicing/Generate endpoint.
