Mobile Admin Portal — Integration Guide

Last Updated: 2026-08-26  | 
Portal developers integrate via the MobileContent.ApiClient.Admin NuGet package (IAdminApiClient). The Admin API provides full CRUD for sites, pages (including containers and items), and themes. All write operations target draft state. Separate workflow actions (publish, schedule, revert, archive) control the content lifecycle. A config endpoint provides enum values for all dropdown fields so nothing is hardcoded client-side.
Auth: All Admin endpoints are currently [AllowAnonymous] for development. Tighten to [Authorize] before production.

NuGet Setup

// Program.cs
services.AddHttpClient<IAdminApiClient, AdminApiClient>(client =>
{
    client.BaseAddress = new Uri("https://gatewayapi.alpa.org");
    // Add auth headers as needed
});

Sites

Sites are the top-level container. A site has a unique lowercase key (semantic identifier), a description, and an optional active theme. Deleting a site performs a soft delete (IsActive = false); it can be restored.

Endpoints

GET     /api/mobilecontent/admin/site                            → IEnumerable<SiteDto>
GET     /api/mobilecontent/admin/site/{siteKey}                  → SiteDto
GET     /api/mobilecontent/admin/site/{siteKey}/pages/inactive   → IEnumerable<PageDto>  (soft-deleted pages only)
POST    /api/mobilecontent/admin/site                            → SiteDto  (201 Created)
PUT     /api/mobilecontent/admin/site/{siteKey}                  → SiteDto
PATCH   /api/mobilecontent/admin/site/{siteKey}                  → SiteDto  (partial update)
DELETE  /api/mobilecontent/admin/site/{siteKey}                  → 204 No Content  (soft delete)
POST    /api/mobilecontent/admin/site/{siteKey}/restore          → 204 No Content

GET query parameters

ParameterTypeDefaultDescription
includeInactiveboolfalseWhen true, includes soft-deleted sites in the results. Active pages within each site are still filtered to active-only.
The GET {siteKey} and GET (list) endpoints only include active pages in the pages collection. To see soft-deleted pages for a site use GET {siteKey}/pages/inactive.

AdminSiteRequest (POST / PUT body)

FieldTypeRequiredNotes
siteKeystringYesUnique lowercase identifier. Normalized on save.
descriptionstringYesHuman-readable label for the site.
activeThemestring?NoKey of the theme (from DynamicThemes). Resolved to a GUID on save. Sets the default theme for all pages in this site.

AdminSitePatchRequest (PATCH body)

Send only the fields you want to change. Omitted fields are left unchanged.

FieldTypeNotes
descriptionstring?Updates the human-readable label. Omit to leave unchanged.
activeThemestring?Sets a new active theme by key (e.g. brand-dark). Omit to leave unchanged.
clearActiveThemeboolWhen true, removes the active theme assignment regardless of activeTheme.

SiteDto (response)

FieldTypeNotes
idguidDatabase PK.
keystringUnique lowercase identifier.
descriptionstringHuman-readable label for the site.
activeThemeIdguid?GUID of the active theme, if assigned.
activeThemeKeystring?Key of the active theme, if assigned.
pagesPageDto[]Active pages only, ordered by sortOrder. Use /pages/inactive to retrieve soft-deleted pages.

Pages

A page belongs to a site and carries its own workflow state, sort order, and optional theme override. The page shell (key, title, sort order, theme) is managed via CRUD endpoints. Containers and items are managed via sub-resource endpoints on the same page controller. Content is editable only in Draft state.

Page Shell Endpoints

GET     /api/mobilecontent/admin/site/{siteKey}/page/{pageKey}                  → PageDto
GET     /api/mobilecontent/admin/site/{siteKey}/page/{pageKey}?includeInactive=true
POST    /api/mobilecontent/admin/site/{siteKey}/page                            → PageDto  (201 Created)
PUT     /api/mobilecontent/admin/site/{siteKey}/page/{pageKey}                  → PageDto
PATCH   /api/mobilecontent/admin/site/{siteKey}/page/{pageKey}                  → PageDto  (partial update)
DELETE  /api/mobilecontent/admin/site/{siteKey}/page/{pageKey}                  → 204 No Content  (soft delete)
POST    /api/mobilecontent/admin/site/{siteKey}/page/{pageKey}/reactivate       → 204 No Content

PageDto (response)

FieldTypeNotes
idguidDatabase PK.
keystringUnique slug within the site.
titlestring?Display title.
sortOrderintPosition within the site's page list.
parentPageIdguid?GUID of the parent page for nested hierarchies. Null for root-level pages.
langFallbackPageKeystring?Key of the fallback page to serve when no locale-specific content exists.
activeThemeIdguid?GUID of the per-page theme override. When null, the site-level theme applies.
activeThemeKeystring?Key of the per-page theme override.
workflowStatestringDraft / Published / Unpublished / Archived.
activeSnapshotIdguid?GUID of the currently published snapshot. Null if never published.
previewMessagestringNon-empty when the response is a draft preview rather than a published snapshot.
isLockedboolTrue while another admin user has the page open for editing.
lockedByUserstring?Identity of the user holding the edit lock. Null when not locked.
hasDraftChangesboolTrue when the draft content has been modified since the last publish. Use to display an “unpublished changes” badge. Cleared automatically on publish.
createdAtDateTimeUTC timestamp when the page record was first created.
updatedAtDateTimeUTC timestamp of the most recent change to the page record.
containersContainerDto[]Ordered by sortOrder.

AdminPageRequest (POST / PUT body)

The site is identified via the route ({siteKey}) and must not be repeated in the request body.

FieldTypeRequiredNotes
pageKeystringNoOptional. When omitted the server slugifies title and resolves collisions by appending -1, -2, etc. When supplied it is used as-is; a 409 is returned if it already exists within the site.
titlestring?NoDisplay title shown in the admin UI.
sortOrderintNoOrdering hint within the site. Default 0.
activeThemestring?NoKey of the theme to override the site-level theme for this page (e.g. brand-dark). Resolved to a GUID on save.
scheduledPublishDateDateTime?NoUTC. Sets an auto-publish schedule at creation/update time.

AdminPagePatchRequest (PATCH body)

Send only the fields you want to change. Omitted fields are left unchanged.

FieldTypeNotes
titlestring?Updates the display title. Omit to leave unchanged.
sortOrderint?Updates the position within the site's page list. Omit to leave unchanged.
activeThemestring?Sets a per-page theme override by key (e.g. brand-dark). Omit to leave unchanged.
clearActiveThemeboolWhen true, removes the page-level theme override so the site theme applies.

Container Endpoints

Containers are ordered layout blocks on a page. pageKey is included in container routes for clarity, but only containerId (a GUID) is used for get/update/delete.

GET     /api/mobilecontent/admin/site/{siteKey}/page/{pageKey}/container/{containerId}            → ContainerDto  (includes items, sortOrder)
POST    /api/mobilecontent/admin/site/{siteKey}/page/{pageKey}/container                          → ContainerDto
PUT     /api/mobilecontent/admin/site/{siteKey}/page/{pageKey}/container/{containerId}            → ContainerDto
DELETE  /api/mobilecontent/admin/site/{siteKey}/page/{pageKey}/container/{containerId}            → 204 No Content

AdminContainerRequest (POST / PUT body)

FieldTypeRequiredNotes
containerTypestringYesMust match an active entry in the DynamicContainerTypes table. See Config endpoint.
titlestring?NoOptional display title shown above the container.
viewAllTextstring?NoLabel for the "view all" action link.
viewAllLinkstring?NoURL or deep-link for the "view all" action.
columnsint?NoColumn count hint for grid layouts.
backgroundTokenstring?NoAny string token identifier for the container background. Passed through to the mobile client as-is.
cornerRadiusTokenstring?NoAny string token identifier for the container corner radius.
paddingTokenstring?NoAny string token identifier for the container padding.
titleTokenstring?NoTheme token path applied to the container title text color/style.
viewAllLinkTokenstring?NoTheme token path applied to the “view all” link element.
isSortableboolNoWhether the mobile app should allow item drag-reorder. Default true.
sortOrderintNoDisplay order of this container within the page.

Item Endpoints

Items are atomic content elements nested inside a container.

GET     /api/mobilecontent/admin/site/{siteKey}/page/{pageKey}/container/{containerId}/item/{itemId}          → ItemDto
POST    /api/mobilecontent/admin/site/{siteKey}/page/{pageKey}/container/{containerId}/item                   → ItemDto
PUT     /api/mobilecontent/admin/site/{siteKey}/page/{pageKey}/container/{containerId}/item/{itemId}          → ItemDto
DELETE  /api/mobilecontent/admin/site/{siteKey}/page/{pageKey}/container/{containerId}/item/{itemId}          → 204 No Content

AdminItemRequest (POST / PUT body)

FieldTypeRequiredNotes
itemTypestringYesMust match an active entry in the DynamicItemTypes table. See Config.
tokenOverridesstring?NoJSON object of theme token overrides scoped to this item (e.g., {"Surface/Brand":"#ff0000"}). Stored as-is; applied by the mobile client on top of the active theme.
isSortableboolNoDefault true.
sortOrderintNoDisplay order within the container.
titlestring?NoPrimary title / heading text.
headerTextstring?NoSecondary header text.
descriptionstring?NoBody text (up to 2000 chars).
eyebrowstring?NoSmall label above the title.
labelstring?NoSupplemental label text.
imagestring?NoImage URL or asset path.
linkstring?NoPrimary navigation URL or deep-link.
linkTextstring?NoDisplay text for the primary link.
ctaTextstring?NoCall-to-action button label.
ctaLinkstring?NoCall-to-action destination URL.
backgroundTokenstring?NoAny string token identifier for the item background. Passed through to the mobile client as-is.
eyebrowTokenstring?NoTheme token path applied to the eyebrow text color/style.
titleTokenstring?NoTheme token path applied to the title text color/style.
descriptionTokenstring?NoTheme token path applied to the description text color/style.
headerTextTokenstring?NoTheme token path applied to the header text color/style.
identityTokenstring?NoTheme token path used to resolve user/member identity styling.
contractLinkTokenstring?NoTheme token path applied to the contract link element.
ctaTextTokenstring?NoTheme token path applied to the call-to-action button text.
rosterTitleTokenstring?NoTheme token path applied to the roster section title.
rosterNameTokenstring?NoTheme token path applied to roster member name text.
rosterEmailTokenstring?NoTheme token path applied to roster member email text.
rosterTelTokenstring?NoTheme token path applied to roster member telephone text.
viewAllLinkTokenstring?NoTheme token path applied to the “view all” link within an item.
iconSizeTokenstring?NoTheme token path controlling the size of an icon element within the item.
iconColorTokenstring?NoTheme token path controlling the color of an icon element within the item.
templatestring?NoFree-form JSON string carrying item-type-specific layout or data payload.

Page Workflow

Every page has a WorkflowState. The workflow endpoints transition pages between states and manage scheduling. All workflow actions accept a callerUser identity from the bearer token and stamp it on the snapshot.

State Transitions

FromActionToEndpoint
DraftPublish immediatelyPublishedPOST …/{pageKey}/publish (no future date)
DraftSchedule publishDraft (pending)POST …/{pageKey}/publish with future scheduledPublishDate
PublishedUnpublish immediatelyUnpublishedPOST …/{pageKey}/unpublish (no future date)
PublishedSchedule unpublishPublished (pending)POST …/{pageKey}/unpublish with future scheduledUnpublishDate
UnpublishedRe-publish from existing snapshotPublishedPOST …/{pageKey}/republish
AnyArchive (retire)ArchivedPOST …/{pageKey}/archive
ArchivedRestore to draftDraftPOST …/{pageKey}/restore
DraftRollback draft to past snapshotDraftPOST …/{pageKey}/revert

Workflow Endpoints

GET     /api/mobilecontent/admin/site/{siteKey}/page/{pageKey}/workflow        → PageWorkflowDto
GET     /api/mobilecontent/admin/site/{siteKey}/page/{pageKey}/versions        → IEnumerable<PageVersionSummaryDto>

POST    /api/mobilecontent/admin/site/{siteKey}/page/{pageKey}/publish         → { pageKey, versionNumber, message }
POST    /api/mobilecontent/admin/site/{siteKey}/page/{pageKey}/unpublish       → { pageKey, message }
POST    /api/mobilecontent/admin/site/{siteKey}/page/{pageKey}/republish       → { pageKey, message }
POST    /api/mobilecontent/admin/site/{siteKey}/page/{pageKey}/revert          → { pageKey, message }
POST    /api/mobilecontent/admin/site/{siteKey}/page/{pageKey}/archive         → { pageKey, message }
POST    /api/mobilecontent/admin/site/{siteKey}/page/{pageKey}/restore         → { pageKey, message }

DELETE  /api/mobilecontent/admin/site/{siteKey}/page/{pageKey}/schedule/publish    → { pageKey, message }
DELETE  /api/mobilecontent/admin/site/{siteKey}/page/{pageKey}/schedule/unpublish  → { pageKey, message }

PUT     /api/mobilecontent/admin/site/{siteKey}/page/{pageKey}/preview-users   → { pageKey, previewUsers }

POST    /api/mobilecontent/admin/site/{siteKey}/page/{pageKey}/lock            → { pageKey, lockedBy }
POST    /api/mobilecontent/admin/page/{pageKey}/unlock          → { pageKey, message }
POST    /api/mobilecontent/admin/page/{pageKey}/force-unlock    → { pageKey, message }

AdminPublishPageRequest (POST /publish body)

FieldTypeNotes
notesstring?Optional human-readable note stored with the snapshot (e.g., "Holiday campaign v2").
scheduledPublishDateDateTime?UTC. When set to a future time the page is NOT published immediately — the background scheduler fires the publish when the time is reached. When null or in the past, publishes immediately.
scheduledUnpublishDateDateTime?UTC. Optional auto-expiry. When set, the scheduler automatically unpublishes the page after it goes live. Can be combined with scheduledPublishDate to configure the full lifecycle in one call.

AdminUnpublishPageRequest (POST /unpublish body)

FieldTypeNotes
scheduledUnpublishDateDateTime?UTC. When set to a future time, unpublish is deferred to that time instead of firing immediately.

AdminRevertPageRequest (POST /revert body)

FieldTypeNotes
versionNumberintThe snapshot version to restore as the new draft. Must exist in the page's snapshot history.

PageWorkflowDto (GET /workflow response)

FieldTypeDescription
pageKeystring
workflowStateint0=Draft, 1=Published, 2=Unpublished, 3=Archived
activeVersionNumberint?Version number of the currently active snapshot. Null if never published.
lastPublishedDateTimeDateTime?UTC timestamp of the most recent publish action.
lastPublishedByUserstring?Identity of who last published.
totalVersionsintNumber of snapshots in history.
previewUsersstring[]User/group IDs that can preview the draft on mobile.
scheduledPublishDateDateTime?Non-null when an auto-publish is pending.
scheduledUnpublishDateDateTime?Non-null when an auto-unpublish is pending.

PageVersionSummaryDto (GET /versions list item)

FieldTypeDescription
versionNumberintMonotonically increasing per page.
publishedByUserstringIdentity of the publisher.
publishedDateTimeDateTimeUTC publish timestamp.
notesstring?Optional notes from the publish request.

Preview Users

stored in DynamicPagePreviewUsers

PUT /api/mobilecontent/admin/page/{pageKey}/preview-users accepts a List<string> body and fully replaces the current list.

Page Locking

The lock system prevents two portal users from overwriting each other's edits. The portal should lock a page when a user opens it for editing, and unlock it when they save or cancel. If a user closes their browser without unlocking, an admin can use force-unlock to clear the stale lock.

EndpointWho calls itBehaviour
POST …/{pageKey}/lock Editing user Sets isLocked = true and records lockedByUser. Returns 409 Conflict if the page is already locked by a different user. Re-locking by the same user is idempotent.
POST …/{pageKey}/unlock Editing user (on save or cancel) Clears isLocked and lockedByUser. Always succeeds for any caller (no ownership check — the portal should only show this button to the lock owner).
POST …/{pageKey}/force-unlock Admin Clears the lock regardless of who set it. Use when a user forgot to unlock/save a draft they were editing.
The isLocked, lockedByUser, hasDraftChanges, createdAt, and updatedAt fields are returned on every PageDto response (including inside SiteDto.pages), so the portal can show lock badges, “unpublished changes” indicators, and timestamps without a separate API call.

Scheduling & Background Scheduler

The PageSchedulerService runs as a hosted background service. It polls periodically and fires pending scheduled publishes and unpublishes. When ScheduledPublishDate is reached, the page is published exactly as if the publish endpoint was called manually. When ScheduledUnpublishDate is reached, the page is unpublished. Use DELETE …/schedule/publish or DELETE …/schedule/unpublish to cancel a pending schedule before it fires.

Themes

Themes are named sets of design tokens. Creating a theme registers the shell (key, title, and optional description). When key is omitted on create, the server slugifies title to produce one. Tokens are managed separately via the token endpoints. Publishing a theme creates a JSON snapshot of all current tokens.

Theme CRUD Endpoints

GET     /api/mobilecontent/admin/theme                       → IEnumerable<ThemeDto>  (draft / live token values)
GET     /api/mobilecontent/admin/theme/{key}                 → ThemeDto  (draft / live token values)
GET     /api/mobilecontent/admin/theme/{key}/published       → ThemeDto  (last published snapshot, immutable)
POST    /api/mobilecontent/admin/theme                       → ThemeDto  (201 Created)
PUT     /api/mobilecontent/admin/theme/{key}                 → ThemeDto
PATCH   /api/mobilecontent/admin/theme/{key}                 → ThemeDto  (partial update)
DELETE  /api/mobilecontent/admin/theme/{key}                 → 204 No Content  (soft delete)
POST    /api/mobilecontent/admin/theme/{key}/reactivate      → 204 No Content

AdminThemeRequest (POST / PUT body)

FieldTypeRequiredNotes
keystringNoUnique semantic identifier. Lowercase normalized. When omitted, the server slugifies title.
titlestring?NoHuman-readable display name. Used as the slug source when key is omitted.
descriptionstring?NoAdditional notes or description for the theme.

AdminThemePatchRequest (PATCH body)

Send only the fields you want to change. Omitted fields are left unchanged.

FieldTypeNotes
titlestring?Updates the display name. Omit to leave unchanged.
descriptionstring?Updates the description. Omit to leave unchanged.

ThemeDto (response)

FieldTypeNotes
idguidDatabase PK.
keystringUnique lowercase identifier.
titlestring?Human-readable display name.
descriptionstring?Additional notes or description.
tokensDictionary<string, string>Current token values (draft read) or snapshot values (published read).
workflowStateint0=Draft, 1=Published, 2=Unpublished, 3=Archived.
activeSnapshotIdguid?GUID of the last published snapshot. Null if never published.
typographyDictionary<string, TypographyStyle>Named typography bundles. Empty when none have been set.
hasDraftChangesboolTrue when the live token rows have been modified since the last publish. Acts as a “pending changes” badge. Always false on a published-snapshot read (GET {key}/published).
previewMessagestringNon-empty when this response is a draft preview rather than a published snapshot.
isLockedboolTrue while an admin user has this theme open for editing. Check this before rendering edit controls.
lockedByUserstring?Identity of the user currently holding the lock. Null when the theme is not locked.
createdAtDateTimeUTC timestamp when the theme record was first created.
updatedAtDateTimeUTC timestamp of the most recent change to the theme record.

Theme Preview Users

Preview users allow specific portal users or groups to preview a theme in draft state on the mobile app before it is published. The current preview user list is returned in ThemeWorkflowDto.previewUsers.

PUT  /api/mobilecontent/admin/theme/{key}/preview-users   → 200 { key, previewUsers[] }

Supply the full desired list — the endpoint performs a full replace. Send an empty array to clear all preview users.

PUT /api/mobilecontent/admin/theme/{key}/preview-users
[
  "user-id-123",
  "PreviewUsers"
]

ThemeWorkflowDto (response)

FieldTypeNotes
keystringTheme key.
workflowStateWorkflowStateCurrent state enum value.
activeVersionNumberint?Version number of the active snapshot. Null if never published.
lastPublishedDateTimeDateTime?UTC timestamp of the last publish.
lastPublishedByUserstring?User who last published.
totalVersionsintTotal number of snapshots created for this theme.
previewUsersstring[]User IDs or group names that can preview this theme in draft state.

Theme Locking

The locking model mirrors page locking. Lock a theme before editing its tokens to prevent concurrent overwrites; unlock on save or cancel. Admins can force-unlock a theme left locked by another user.

EndpointCallerBehaviour
POST …/{key}/lock Editing user (on open) Sets isLocked = true and records lockedByUser. Returns 409 Conflict if the theme is already locked by a different user. Re-locking by the same user is idempotent.
POST …/{key}/unlock Editing user (on save or cancel) Clears isLocked and lockedByUser. Always succeeds for any caller (the portal should only show this button to the lock owner).
POST …/{key}/force-unlock Admin Clears the lock regardless of who set it. Use when a user forgot to unlock/save a theme they were editing.
The isLocked and lockedByUser fields are returned on every ThemeDto response, so the portal can show a lock badge and disable token-editing controls without a separate API call.

Token Endpoints

Known paths and their metadata (display names, validation rules, defaults, categories) are defined in the MobDynThemeTokenRegistry table — 95 tokens across 15 categories: Surface, Text, Border, Icon, Action, Navigation, Status, Typography, Font Size, Font Weight, Text Transform, Letter Spacing, Spacing, Border Radius, and Icon Size.
GET     /api/mobilecontent/admin/theme/{key}/tokens          → Dictionary<string, string>
PUT     /api/mobilecontent/admin/theme/{key}/tokens          → 204 No Content  (full replace)
PATCH   /api/mobilecontent/admin/theme/{key}/tokens          → 204 No Content  (merge patch)

GET     /api/mobilecontent/admin/theme/token-metadata        → grouped token metadata (for UI)
POST    /api/mobilecontent/admin/theme/validate-tokens       → 200 OK or 400 with errors

PUT /tokens — full replace

Replaces all existing tokens for the theme. Any paths not included in the request are removed. Use this when loading a complete theme definition or copying tokens from another theme.

{
  "tokens": {
	"Surface/Brand":   "#007AFF",
	"Text/Primary":    "#1A1A1A"
  }
}

PATCH /tokens — merge patch

Touches only the token paths you send. Absent paths are left exactly as they are. Send a path with a null value to remove that single token.

{
  "Surface/Brand": "#FF5500",
  "Text/Secondary": null
}

The example above updates Surface/Brand, removes Text/Secondary, and leaves every other token unchanged. Supplied (non-null) values are validated the same way as PUT; a 400 is returned if any value is invalid. Both PUT and PATCH set hasDraftChanges = true on a previously-published theme.

Token Metadata & Validation

GET /token-metadata returns grouped metadata for every known token (display name, validation rules, default values, etc.) so the admin UI can build a structured token editor without hardcoding anything. Use POST /validate-tokens to validate a token dictionary before committing it — returns a Dictionary<string, string> of path → error message on 400, or 200 OK if all tokens are valid.

Typography Endpoints

Typography entries are named bundles of font style properties (font family, size, weight, style, line height, transform, letter spacing, color) stored as JSON objects in DynamicThemeTypography.

GET  /api/mobilecontent/admin/theme/{key}/typography   → Dictionary<string, TypographyStyle>
PUT  /api/mobilecontent/admin/theme/{key}/typography   → 204 No Content  (full replace)

PUT /typography body

{
  "typography": {
    "EyebrowDefault": {
      "Font": "LeagueSpartan",
      "FontSize": "14px",
      "FontWeight": "900",
      "TextTransform": "uppercase",
      "LetterSpacing": "5%",
      "Color": "#5d6471"
    },
    "ButtonCardTitle": {
      "Font": "LeagueSpartan",
      "FontSize": "12px",
      "FontWeight": "800",
      "TextTransform": "uppercase",
      "LetterSpacing": "0px",
      "Color": "#ffffff"
    }
  }
}

TypographyStyle object

FieldTypeNotes
fontstring?Font family name (e.g. LeagueSpartan).
fontSizestring?CSS font size (e.g. 14px).
fontWeightstring?CSS font weight (e.g. 900, bold).
fontStylestring?CSS font-style value (e.g. italic, normal). Null = inherit.
lineHeightstring?CSS line-height (e.g. 1.5, 24px). Null = inherit.
textTransformstring?CSS text-transform value (e.g. uppercase). Null = inherit.
letterSpacingstring?CSS letter-spacing (e.g. .05em). Null = inherit.
colorstring?Hex or rgba color value. Null = inherit from theme tokens.

Theme Workflow Endpoints

Theme workflow follows the same state machine as pages, without scheduling.

GET     /api/mobilecontent/admin/theme/{key}/workflow       → ThemeWorkflowDto
GET     /api/mobilecontent/admin/theme/{key}/versions       → IEnumerable<ThemeVersionSummaryDto>

POST    /api/mobilecontent/admin/theme/{key}/publish        → { key, versionNumber }
POST    /api/mobilecontent/admin/theme/{key}/unpublish      → 204 No Content
POST    /api/mobilecontent/admin/theme/{key}/republish      → 204 No Content
POST    /api/mobilecontent/admin/theme/{key}/archive        → 204 No Content
POST    /api/mobilecontent/admin/theme/{key}/restore        → 204 No Content

AdminPublishThemeRequest (POST /publish body)

FieldTypeNotes
notesstring?Optional note stored with the snapshot.

ThemeWorkflowDto (GET /workflow response)

FieldTypeDescription
keystring
workflowStateint0=Draft, 1=Published, 2=Unpublished, 3=Archived
activeVersionNumberint?Active snapshot version. Null if never published.
lastPublishedDateTimeDateTime?UTC.
lastPublishedByUserstring?
totalVersionsint

Config / Type Endpoints

These endpoints return the valid string values for all constrained fields. containerTypes and itemTypes are sourced from the DynamicContainerTypes and DynamicItemTypes database tables (managed by administrators), while token lists are derived from server-side enums. Call them once on Admin Portal load to populate dropdowns without hardcoding values in the client. All are anonymous and return plain arrays or a combined object.

GET  /api/mobilecontent/admin/config                      → all enum sets in one object
GET  /api/mobilecontent/admin/config/container-types      → string[]
GET  /api/mobilecontent/admin/config/item-types           → string[]
GET  /api/mobilecontent/admin/config/background-tokens    → string[]
GET  /api/mobilecontent/admin/config/padding-tokens       → string[]
GET  /api/mobilecontent/admin/config/corner-radius-tokens → string[]

Combined config response shape

{
  "containerTypes":    ["VerticalStack", "HorizontalRow", ...],
  "itemTypes":         ["TextBlock", "Image", "Button", ...],
  "backgroundTokens":  ["Surface/Brand", "Surface/Primary", ...],
  "paddingTokens":     ["Spacing/None", "Spacing/Small", ...],
  "cornerRadiusTokens":["Radius/None", "Radius/Small", ...]
}
Recommended pattern: Call GET /config once at admin portal initialization and cache the result in state. Use the returned arrays to populate every container type, item type, and token dropdown throughout the UI.

Complete Admin Endpoint Reference

Sites

GET     /api/mobilecontent/admin/site
GET     /api/mobilecontent/admin/site/{siteKey}
POST    /api/mobilecontent/admin/site
PUT     /api/mobilecontent/admin/site/{siteKey}
DELETE  /api/mobilecontent/admin/site/{siteKey}
POST    /api/mobilecontent/admin/site/{siteKey}/restore

Pages

GET     /api/mobilecontent/admin/page/{pageKey}
POST    /api/mobilecontent/admin/page
PUT     /api/mobilecontent/admin/page/{pageKey}
DELETE  /api/mobilecontent/admin/page/{pageKey}
POST    /api/mobilecontent/admin/page/{pageKey}/reactivate

Containers

GET     /api/mobilecontent/admin/page/{pageKey}/container/{containerId}
POST    /api/mobilecontent/admin/page/{pageKey}/container
PUT     /api/mobilecontent/admin/page/{pageKey}/container/{containerId}
DELETE  /api/mobilecontent/admin/page/{pageKey}/container/{containerId}

Items

GET     /api/mobilecontent/admin/page/{pageKey}/container/{containerId}/item/{itemId}
POST    /api/mobilecontent/admin/page/{pageKey}/container/{containerId}/item
PUT     /api/mobilecontent/admin/page/{pageKey}/container/{containerId}/item/{itemId}
DELETE  /api/mobilecontent/admin/page/{pageKey}/container/{containerId}/item/{itemId}

Page Workflow

GET     /api/mobilecontent/admin/page/{pageKey}/workflow
GET     /api/mobilecontent/admin/page/{pageKey}/versions
POST    /api/mobilecontent/admin/page/{pageKey}/publish
POST    /api/mobilecontent/admin/page/{pageKey}/unpublish
POST    /api/mobilecontent/admin/page/{pageKey}/republish
POST    /api/mobilecontent/admin/page/{pageKey}/revert
POST    /api/mobilecontent/admin/page/{pageKey}/archive
POST    /api/mobilecontent/admin/page/{pageKey}/restore
DELETE  /api/mobilecontent/admin/page/{pageKey}/schedule/publish
DELETE  /api/mobilecontent/admin/page/{pageKey}/schedule/unpublish
PUT     /api/mobilecontent/admin/page/{pageKey}/preview-users

Themes

GET     /api/mobilecontent/admin/theme
GET     /api/mobilecontent/admin/theme/{key}
POST    /api/mobilecontent/admin/theme
PUT     /api/mobilecontent/admin/theme/{key}
DELETE  /api/mobilecontent/admin/theme/{key}
POST    /api/mobilecontent/admin/theme/{key}/reactivate
GET     /api/mobilecontent/admin/theme/{key}/tokens
PUT     /api/mobilecontent/admin/theme/{key}/tokens
GET     /api/mobilecontent/admin/theme/{key}/typography
PUT     /api/mobilecontent/admin/theme/{key}/typography
GET     /api/mobilecontent/admin/theme/{key}/workflow
GET     /api/mobilecontent/admin/theme/{key}/versions
POST    /api/mobilecontent/admin/theme/{key}/publish
POST    /api/mobilecontent/admin/theme/{key}/unpublish
POST    /api/mobilecontent/admin/theme/{key}/republish
POST    /api/mobilecontent/admin/theme/{key}/archive
POST    /api/mobilecontent/admin/theme/{key}/restore
PUT     /api/mobilecontent/admin/theme/{key}/preview-users
POST    /api/mobilecontent/admin/theme/{key}/lock
POST    /api/mobilecontent/admin/theme/{key}/unlock
POST    /api/mobilecontent/admin/theme/{key}/force-unlock
GET     /api/mobilecontent/admin/theme/token-metadata
POST    /api/mobilecontent/admin/theme/validate-tokens

Config

GET     /api/mobilecontent/admin/config
GET     /api/mobilecontent/admin/config/container-types
GET     /api/mobilecontent/admin/config/item-types
GET     /api/mobilecontent/admin/config/background-tokens
GET     /api/mobilecontent/admin/config/padding-tokens
GET     /api/mobilecontent/admin/config/corner-radius-tokens