Supersedes the earlier composite-key draft. A prior version of this contract described a
composite content-item id (ItemTypeId + prefixed ItemId, e.g. menu-123 /
doc-456 / msg-789) resolved through an /api/content/resolve endpoint. That
premise was wrong and is retracted — there is no composite key, no msg- / message content type, and
no resolve endpoint.
Also corrected 2026-08-28 (AB#2609). This doc previously claimed the dynamic-feed's
ItemDto (see the dynamic-feed contract) is
derived from a MenuItem/DocumentItem pair served by a "MobileContent" API. Per Vlad Oprica's
current Mobile Content API docs, that's not how the backend works:
dynamic-feed content (cards, carousels, hero content) is authored directly as ItemDto rows in the
Site → Page → Container → Item model, through the
Admin Portal API or the
Ingestion API, and read back via the
Dynamic API — there is no DocumentItem model in that
system, and no transform step from MenuItem/DocumentItem into ItemDto.
MenuItem itself is real, but it's the legacy, unrelated app-navigation menu (see the
Menu Items & Favorites API) — it does not feed the dynamic
feed. See §1–§3 below for the corrected model.
The dynamic-UI response (see the dynamic-feed contract) describes which components render and how — but each component still needs its data source. This contract is that last piece: it maps every dynamic-feed component to the real content that populates it. There are two separate content systems in play, and they do not feed each other:
ItemDto rows inside a Page's Containers, via the Site → Page → Container →
Item model. See §2.MenuItem — the legacy, unrelated app-navigation menu (Mobile Menu rows: titled,
iconed tiles that route somewhere), served by the
Menu Items & Favorites API. It does not populate
dynamic-feed components. See §3.There is no DocumentItem model and no MobileContent API with a
MenuItem/DocumentItem duality feeding the dynamic feed — that premise is retracted (see the
warning above). Authoritative source for §2: Vlad Oprica's Mobile Content
API docs.
Dynamic-feed content is not served by a separate "MobileContent" API — it's the same Site → Page → Container →
Item hierarchy documented in the Mobile Content API overview. Each
Item row becomes one ItemDto in the page payload the mobile app reads via the
Dynamic API (GET
/api/mobilecontent/dynamic/site/{siteKey}/page/{pageKey}). Content is authored/edited two ways:
/api/mobilecontent/admin/...) — full CRUD on sites, pages,
containers, and items, with a draft/publish workflow./api/mobilecontent/ingest/...) — atomic upsert of a full page or site
tree, used for bulk seeding and CI/CD.A dynamic-feed component in the response binds to whichever ItemDto its Container holds —
see §4 for the mapping from component to itemType. The exact itemType string used for
document/article-style content (analogous to what this doc previously, incorrectly, called DocumentItem)
is not yet confirmed against Vlad's docs — flagged as an open item in §7 rather than guessed here.
Favorites uses a separate discriminator, unrelated to dynamic-feed content. Favorites references an
item by Favorite.ItemTypeId (int, resolved via the open itemtypes lookup catalog — see §7) +
Favorite.ItemId (string). That pattern is real and shipped, but it's Favorites' own bookmarking model —
it is not how dynamic-feed content is sourced (above), and it does not imply a
MenuItem/DocumentItem duality feeding ItemDto. See §7 for the
current Favorites shape.
MenuItem — legacy navigationMenuItem is the Mobile Menu's navigation model, served by
GET /api/mobilecontent/menuitems/getallforuser (see the
Menu Items & Favorites API). It is unrelated to
dynamic-feed content (§2) — it's covered here only because §4 maps the nav-tile components it backs.
| Type | Role | Fields | Source |
|---|---|---|---|
MenuItem |
Navigation | id (int) · parentId (int?, tree nesting) · sortOrder (int) ·
title (string) · description (string?) · path (string?) ·
imageSource (string?) · glyph (string?) · glyphFontFamily (string?) ·
enabled (bool) · visible (bool) · specialCode (string?) ·
showOnHomeScreen (bool) · showToAdmin (bool) · showOnTabBar (bool) ·
showOnTabBarSort (int) |
GET /api/mobilecontent/menuitems/getallforuser ([AllowAnonymous]) |
Flat list, not nested — corrected 2026-08-28. MenuItem is a flat collection;
parent/child relationships are expressed via parentId, not a nested Items collection.
description is already a live, shipped field — not a pending "Change 2" addition as this doc previously
stated.
Which content type backs which dynamic-UI component (from backend-api-mapping §2):
| Component | Backed by | Key fields / note |
|---|---|---|
card-sm nav tiles (DART, Directories, …) | MenuItem | Title · Path · ImageSource · ShowOnHomeScreen · SortOrder |
ButtonCard (Emergency Hotline) | MenuItem | Glyph · Title · Path; identified by SpecialCode |
TextCard (Eyebrows / Simple) | MenuItem + Description | Navigation tile with richer display — needs Description (backend Change 2) |
card-lg / HeroCard (feeds and standalone) | ItemDto (dynamic feed) | Image · eyebrowToken · title; which items appear is determined by the enclosing Container/Page, not a Scope/Category discriminator (that concept belonged to the retracted DocumentItem model — see §7 open item). One CardHeroViewModel whether standalone (e.g. Hotel Request) or inside a carousel. |
| Carousel (of hero cards) | ContainerDto + ItemDto[] (dynamic feed) | CarouselViewModel.Items is a List<CardHeroViewModel>, each mapped from one ItemDto in the container; order follows each item's sortOrder |
| Welcome / Pilot Card (domain control → Card) | IAuthentication + ItemDto (dynamic feed) | Greeting from UserInfo (add Rank — Change 1), contract link from an ItemDto's contractLinkToken/ctaLink |
The three rows above were corrected 2026-08-28 (AB#2609) — they previously named the retracted DocumentItem model as the source.
ItemDto) are selected by which Page/Container
they were authored into via the Admin Portal or Ingestion API (§2) — a feed slot resolves to whatever items its
container holds, ordered by each item's sortOrder (isSortable controls
whether the mobile client allows drag-reorder). There is no Scope/Category discriminator
on the wire — that was specific to the retracted DocumentItem model (corrected 2026-08-28).MenuItem) tiles are selected by visible +
showOnHomeScreen and ordered by sortOrder; special
actions (e.g. Emergency Hotline) are matched by specialCode.Do not conflate MenuItem (legacy navigation) with dynamic-feed ItemDto
content. They are different entities served by different APIs — Menu Items & Favorites vs.
Admin/Ingest/Dynamic — and merging them would couple two independently-evolving systems. (Corrected 2026-08-28: this
section previously argued against merging MenuItem with the retracted DocumentItem model;
the same "don't conflate" verdict applies to the real dynamic-feed ItemDto model instead.) The one
MenuItem-side change this doc still calls out — a nullable UserInfo.Rank addition
(Change 1) for the Welcome/Pilot Card greeting — remains a client-side model question, unrelated to this
correction.
Favorites relationship — resolved, already shipped; corrected 2026-08-28 (AB#2609).
Favorite.ItemTypeId (int) is not a fixed 3-value enum as this doc previously stated — it
resolves against the open, DB-backed lookup catalog at GET /api/mobilecontent/favorites/itemtypes
(anonymous) → IEnumerable<ItemType> { id: int, description: string } (e.g. "Flight", "News",
"Event" — see the Menu Items & Favorites API). Full
shape: POST /api/mobilecontent/favorites (body { itemTypeId, itemId, sortOrder?, title? })
→ Favorite { id, userId, itemTypeId, itemTypeName, itemId, sortOrder, title? } (201, or 200 idempotent
if the itemTypeId+itemId pair already exists for the user); DELETE
/api/mobilecontent/favorites/{itemTypeId}/{itemId} → 204, or 404 if not found.
FavoritesViewModel.HydrateAsync resolves a favorite back to real content and hand-builds the matching
Presentation ViewModel (CardSmallViewModel for MenuItem-backed favorites) — working, in production,
today.
Feed binding for dynamic-feed content — resolved, not a client concern. The dynamic-feed response (dynamic-feed contract) delivers ItemDto — a server-flat bag, not a union type, discriminated by ItemDto.itemType (a string, e.g. "TextBlock", "Image", "Button" — see the Dynamic API) — not a raw MenuItem/DocumentItem passthrough. ItemComponentFactory.Create(ItemDto) already maps this to the correct Presentation ViewModel for every component type in §4's table. Corrected 2026-08-28: there is no server-side "MenuItem/DocumentItem → ItemDto" transform step — ItemDto is authored directly as Item rows via the Admin Portal or Ingestion API (§2), not derived from another model.
Adapter layer — built (WI-2262, D58, 2026-07-15). The reusable factories this item called for now exist in ALPAMobile/Components/: MenuItemCardFactory (MenuItem → CardSmallViewModel, with the web-servable-image check and icon-mask fallback) and DocumentHeroFactory (DocumentItem → CardHeroViewModel, plus the static OpenRoute(fileId) that owns the /document-open never-a-raw-Path-href policy). FavoritesViewModel and DocumentsListPage.razor consume them. Scope note added 2026-08-28 (AB#2609): the DocumentItem referenced here is the separate, pre-existing document-library feature (Documents page, /document-open route) — it is unrelated to the dynamic-feed / Site-Page-Container-Item content model described in §1–§2, and it does not feed ItemDto. What remains open is only whether that document-library feature ever grows a mixed-type response envelope of its own — no dynamic-feed scenario requires one (Favorites discriminates via ItemTypeId; the dynamic feed sources content directly as ItemDto).
Full rationale, code citations, and the D11/DocumentHeroFactory naming clarification: D57 · implementation record: D58.