4. Frontend Prerequisite APIs
Every one of these endpoints is [AllowAnonymous] — the signup form calls them before the user has any
account or JWT. All are hosted on Shumoul.Api under VersionedApiController's
api/v{version:apiVersion}/[controller] convention unless noted otherwise.
4.1 Countries
GET api/v1/Country/GetRegistrationList
- Input: none.
- Output:
Result<List<CountryViewDto>>whereCountryViewDto { Guid Value, string Text, string Code }. - Source:
RegistrationReferenceService→SharedDbContext.Countriesfiltered toIsActive && !Is_Deleted. - Purpose: populates the Country dropdown.
Valueis theCountryIdto submit at registration.
4.2 Regions (by country)
GET api/v1/Region/GetRegistrationListByCountryId/{countryId}
- Input:
countryId(route, GUID) — theValueselected from 4.1. - Output:
Result<List<RegionRegistrationViewDto>>whereRegionRegistrationViewDto { Guid Value, string Text, Guid CountryId }. - Purpose: populates the Region dropdown, filtered by the already-selected country.
Valueis theRegionIdto submit.
4.3 Cities (by region, or by country)
GET api/v1/City/GetRegistrationListByRegionId/{regionId}
GET api/v1/City/GetRegistrationListByCountryId/{countryId}
- Input:
regionIdorcountryId(route, GUID). - Output:
Result<List<SelectListItem>>whereSelectListItem { string Value, string Text }— note:Valuehere is a string, unlike Country/Region whereValueis aGuid. Frontend code must parse it to a GUID before submittingCityId. - Purpose: populates the City dropdown. Prefer
GetRegistrationListByRegionIdonce a region is selected — it is the tighter filter and matches what registration itself validates against (region → city relationship, see Chapter 6).
4.4 Subscription packages / pricing (recommended — single call)
GET api/Subscription/Packages/pricing
- Input: none (returns all active packages across all active plans/billing cycles).
- Output:
Result<List<SubscriptionPricingDto>>— each entry nestsSubscriptionPackagePricingDetailsDto.Id, which is the exact value to submit asRegisterNewTenantRequest.PlanPackageId. ThisIdis theSubscriptionPlanPackagejoin-table row's ownId— notPackageIdand notPlanId. SubmittingPackageIdorPlanIdinstead will fail package resolution at registration. - Purpose: single call that already carries the plan/billing-cycle/package combination (e.g. "باقة
البداية" / Starter, Monthly, 99 SAR) needed to pick the right
PlanPackageId.
4.5 Subscription plans and package list (alternative, 2-step)
GET api/Subscription/Plans/GetActive
GET api/Subscription/PlanPackages/GetActive?planId={id}
Use only if the UI needs to present Plan (billing cycle) and Package as two separate pickers instead of
one combined pricing grid. SubscriptionPlanPackage rows (from either path) carry the same Id that
becomes PlanPackageId.
4.6 Billing cycle
There is no separate billing-cycle enumeration endpoint. BillingCycleEnum (Monthly/Quarterly/
Annually) exists in the domain but is dead code — it is not read anywhere in the registration or pricing
path. Billing cadence is represented by separate SubscriptionPlan rows (e.g. a "Monthly"/"شهريا" plan
and an "Annually"/"سنويا" plan), each producing its own SubscriptionPlanPackage rows and therefore its
own PlanPackageId per package. Selecting "Monthly" vs "Annually" in the UI means selecting a different
PlanPackageId, not a separate billing-cycle field.
4.7 Language / culture
There is no language/culture enumeration endpoint. RegisterNewTenantRequest.Language is a free-text
string ("ar" / "en") that the frontend hardcodes based on its own UI locale, normalized server-side
(NormalizeLanguageCode in TenantService). Document any additional supported codes with the frontend
team directly if they are needed — the backend does not currently publish a canonical list.
4.8 Public config (captcha / terms / registration toggle)
No dedicated "public signup config" endpoint was found. Whether self-registration is even open is
controlled server-side by SubscriptionSettings.Enable_Tenant_Registration — checked inside
TenantService.RegisterAsync itself, returned as a registration-time error if disabled, not exposed as
a separate pre-flight flag today. There is no captcha or terms-acceptance field on
RegisterNewTenantRequest in the current contract (see Chapter 5) — if the
production frontend collects a terms checkbox, it is currently a frontend-only concern with no backend
field to receive it.
4.9 Sample values used in the verified dev scenario
| Field | Prerequisite call | Resolved value used |
|---|---|---|
| Country | GetRegistrationList | Saudi Arabia (CountryId resolved from this dev environment's own dataset — see Chapter 6 §6.8 for why the literal Riyadh/Riyadh region/city pair could not be used in this environment) |
| Region | GetRegistrationListByCountryId | Eastern Province (substituted for Riyadh — see Chapter 6 §6.8) |
| City | GetRegistrationListByRegionId | Dammam |
| Package/Plan | Packages/pricing | Monthly + باقة البداية (Starter), 99 SAR — PlanPackageId matched exactly between direct SQL inspection and the live pricing API |
