← Back to Component Specifications

Content Contract — MobileContent

How dynamic-feed content (ItemDto, via the Site/Page/Container/Item model) is sourced, and how it differs from the legacy MenuItem navigation menu
Work Item: AB#1821  |  Status: Model & component mapping confirmed from backend-api-mapping §2; §7 open items resolved 2026-07-15 (D57) and the adapter layer built the same day (WI-2262, D58). Reconciled against Vlad Oprica's mobile-content-api docs 2026-08-28 (AB#2609)  |  Peers: dynamic-feed contract · theme endpoint contract · navigation & routing contract  |  Updated: 2026-08-29 07:30 ET  |  Audited: 2026-07-01 21:25 ET

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.

Sections 1. Purpose  ·  2. Where dynamic-feed content comes from  ·  3. MenuItem — legacy navigation  ·  4. Component ↔ content-type mapping  ·  5. How the feed selects content  ·  6. Keep the two systems separate  ·  7. Open confirms

1. Purpose

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:

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.

2. Where dynamic-feed content comes from

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:

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.

3. MenuItem — legacy navigation

MenuItem 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.

TypeRoleFieldsSource
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.

4. Component ↔ content-type mapping

Which content type backs which dynamic-UI component (from backend-api-mapping §2):

ComponentBacked byKey fields / note
card-sm nav tiles (DART, Directories, …)MenuItemTitle · Path · ImageSource · ShowOnHomeScreen · SortOrder
ButtonCard (Emergency Hotline)MenuItemGlyph · Title · Path; identified by SpecialCode
TextCard (Eyebrows / Simple)MenuItem + DescriptionNavigation 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.

5. How the feed selects content

6. Keep the two systems separate

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.

7. Open confirms — resolved/narrowed 2026-07-15 (D57)

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/DocumentItemItemDto" 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.