Shop It Docs
Developer Resourcescontent

Content Module API & Integration Guide

API contracts for content admin and customer/mobile newsletter flows.

Content Module - API & Integration Guide

1. How to Read / Quick Metadata

  • Module: Content
  • Primary base URLs:
    • Admin: /api/admin/content-series, /api/admin/content-items, /api/admin/content-featured
    • Customer: /api/content/...
    • Mobile-composed customer: /api/mobile/content/...
  • Auth model:
    • Admin routes: JwtAuthGuard + RoleGuard + @Permissions(...)
    • Customer full/library routes: JwtAuthGuard
    • Customer preview/list routes: public
  • Response envelope: ResponseDto<T> with paginated responses via ApiPaginatedResponseDto

2. High-Level Overview

Content API is split into three resource families:

  • Series: grouping, metadata, and optional tier rules
  • Items: newsletter publishing lifecycle, preview/full retrieval, send actions
  • Featured: curated placement with date windows and explicit ordering

Entitlement for full body responses is subscription-tier based, not product-purchase based.

3. Core Concepts and Terminology

  • content series: parent grouping (content_series)
  • content item: newsletter entity (content_item)
  • newsletter tier rule: access requirement (newsletter_content_tier)
  • featured content: curated surfaced entries (featured_content)
  • slug history: backward-compatible route resolution for renamed slugs
  • recipient filter: newsletter send/list filter (all, basic, premium)

4. Route Summary

4.1 Admin routes

MethodPathPermission
GET/api/admin/content-seriesContentSeries_READ
GET/api/admin/content-series/:idContentSeries_READ
POST/api/admin/content-seriesContentSeries_CREATE
PATCH/api/admin/content-series/:idContentSeries_UPDATE
POST/api/admin/content-series/:id/tierContentSeries_UPDATE
DELETE/api/admin/content-series/:idContentSeries_DELETE
GET/api/admin/content-itemsContent_READ
GET/api/admin/content-items/subscribersContent_READ
GET/api/admin/content-items/:idContent_READ
POST/api/admin/content-itemsContent_CREATE
PATCH/api/admin/content-items/:idContent_UPDATE
DELETE/api/admin/content-items/:idContent_DELETE
POST/api/admin/content-items/:id/publishContent_UPDATE
POST/api/admin/content-items/:id/unpublishContent_UPDATE
POST/api/admin/content-items/:id/tierContent_UPDATE
POST/api/admin/content-items/:id/send-emailContent_UPDATE
POST/api/admin/content-items/:id/send-pushContent_UPDATE
GET/api/admin/content-items/:id/previewContent_READ
GET/api/admin/content-items/:id/fullContent_READ
GET/api/admin/content-featuredContent_READ
GET/api/admin/content-featured/:idContent_READ
POST/api/admin/content-featuredContent_CREATE
PATCH/api/admin/content-featured/:idContent_UPDATE
DELETE/api/admin/content-featured/:idContent_DELETE
POST/api/admin/content-featured/reorderContent_UPDATE

4.2 Customer / Mobile routes

MethodPathNotes
GET/api/content/serieslist active series
GET/api/content/series/:slugseries detail
GET/api/content/itemslist published items
GET/api/content/items/:slugpreview
GET/api/content/items/:slug/fullJWT + entitlement
GET/api/content/my-libraryJWT
GET/api/content/items/featuredactive featured list
GET/api/content/items/featured/allpaginated featured list

Mobile-composed equivalents are available under /api/mobile/content/....

5. Query Parameters

Common pagination fields (from QueryDto):

  • pagination (default true)
  • page (default 1)
  • size (default 20)
  • search (optional trimmed string)
  • order (asc/desc, default desc unless endpoint overrides)

Resource-specific filters:

  • customer item list: seriesId, seriesType, sort (title|publishedAt|createdAt)
  • customer series list: type, sort (name|createdAt|updatedAt)
  • customer featured list (/featured/all): isActive, sort (order|title|createdAt|updatedAt), order (default asc)
  • admin item list: seriesId, status, previewEnabled, sort
  • admin series list: type, isActive, sort
  • admin featured list: isActive, sort, order
  • subscribers list: tier (all|basic|premium)

6. Response Shape Examples

Subscriber filter example:

"
  "tier": "premium",
  "search": "reader@domain.com",
  "pagination": true,
  "page": 1,
  "size": 20
"

Full-content success envelope example:

"
  "message": "Content full body fetched successfully",
  "data": "
    "id": 101,
    "seriesId": 12,
    "seriesName": "Weekly Market Newsletter",
    "slug": "market-outlook-apr-2026",
    "title": "Market Outlook",
    "contentBody": "...full body...",
    "previewEnabled": true,
    "previewMode": "paragraphs",
    "previewWatermarkEnabled": false,
    "isLocked": false,
    "isEntitled": true,
    "requiredTier": "premium",
    "publishedAt": "2026-04-01T08:00:00.000Z"
  "",
  "errorCode": null
"

7. Enums

Core enums used by content APIs:

  • content_type: newsletter
  • content_status: draft, published, hidden, archived
  • preview_mode: paragraphs, words, sections
  • newsletter_tier: basic, premium
  • newsletter recipient filter: all, basic, premium

8. Integration Diagram

9. Caching

Primary content cache key prefixes:

  • content:customer:items:list:
  • content:customer:item:slug:
  • content:customer:library:
  • content:customer:subscriptions:
  • content:customer:series:list:
  • content:customer:series:slug:
  • content:featured:admin:list:
  • content:featured:customer:active:
  • content:featured:customer:all:

TTL envs:

  • CONTENT_CUSTOMER_SERIES_CACHE_TTL_SECONDS
  • CONTENT_CUSTOMER_ITEM_CACHE_TTL_SECONDS
  • CONTENT_CUSTOMER_MY_LIBRARY_CACHE_TTL_SECONDS
  • CONTENT_CUSTOMER_PURCHASES_CACHE_TTL_SECONDS
  • CONTENT_CACHE_TTL_SECONDS

10. Error Handling

All content API errors return the standard envelope with errorCode.

"
  "statusCode": 404,
  "errorCode": "CONTENT_NOT_FOUND",
  "message": "Content item not found.",
  "timestamp": "2026-03-20T10:00:00.000Z",
  "path": "/api/content/items/some-slug"
"
HTTPerrorCodeCondition
400CONTENT_STATUS_INVALIDscheduledAt is in the future but status=published
400CONTENT_ARCHIVED_NOT_PUBLISHABLEattempted to publish archived content
400CONTENT_SCHEDULED_AT_INVALIDinvalid scheduledAt value
400CONTENT_SERIES_CREATE_FAILEDcontent series creation failed
400CONTENT_NOT_PUBLISHEDattempted send for unpublished newsletter
400FEATURED_CONTENT_DUPLICATE_IDSduplicate IDs in featured reorder payload
400FEATURED_CONTENT_INVALID_DATE_RANGEfeatured startDate is after endDate
400FEATURED_CONTENT_INVALID_SORTunsupported sort field for featured listing
403CONTENT_ACCESS_DENIEDuser lacks entitlement for full content
404CONTENT_NOT_FOUNDcontent item does not exist
404CONTENT_SERIES_NOT_FOUNDcontent series does not exist
404FEATURED_CONTENT_NOT_FOUNDfeatured row does not exist
404CONTENT_CATEGORY_NOT_FOUNDreferenced category does not exist
404TAG_NOT_FOUNDone or more tag IDs do not exist
429RATE_LIMIT_EXCEEDEDendpoint rate limit exceeded
503ERR_CONFIG_INVALIDrequired config invalid/missing

11. Endpoint Reference + Payload Cheatsheet

11.1 Payload Cheatsheet Table (Every Endpoint)

MethodPathAuth / PermissionRequest DTO / ParamsSuccess DTONotes
GET/api/admin/content-seriesAdmin + ContentSeries_READQueryContentSeriesAdminDto "" page?, size?, pagination?, type?, isActive?, sort?, order? ""ContentSeriesAdminListItemDto[] paginatedList all content series with filters
GET/api/admin/content-series/:idAdmin + ContentSeries_READpath: id (int)ContentSeriesAdminDetailDtoGet single series with items
POST/api/admin/content-seriesAdmin + ContentSeries_CREATECreateContentSeriesDto "" name: string, type: "newsletter", slug?: string, description?: string, isActive?: boolean ""ContentSeriesResponseDtoCreates new content series
PATCH/api/admin/content-series/:idAdmin + ContentSeries_UPDATEpath: id (int), body: UpdateContentSeriesDto "" name?: string, description?: string, isActive?: boolean ""ContentSeriesResponseDtoUpdate series metadata
POST/api/admin/content-series/:id/tierAdmin + ContentSeries_UPDATEpath: id (int), body: SetContentSeriesTierDto "" tier: "basic"|"premium" ""ContentSeriesTierResponseDtoSet required tier for series
DELETE/api/admin/content-series/:idAdmin + ContentSeries_DELETEpath: id (int)DeleteResponseDtoSoft delete series (archived)
GET/api/admin/content-itemsAdmin + Content_READQueryContentItemsAdminDto "" page?, size?, pagination?, seriesId?, status?, previewEnabled?, sort?, order? ""ContentItemAdminListItemDto[] paginatedList all content items
GET/api/admin/content-items/subscribersAdmin + Content_READQuerySubscribersDto "" tier?: "all"|"basic"|"premium", search?, page?, size?, pagination? ""SubscriberListDto[] paginatedList newsletter subscribers
GET/api/admin/content-items/:idAdmin + Content_READpath: id (int)ContentItemAdminDetailDtoGet item detail with series info
POST/api/admin/content-itemsAdmin + Content_CREATECreateContentItemDto "" seriesId: number, title: string, slug?: string, contentBody: string, previewEnabled?: boolean, previewMode?: "paragraphs"|"words"|"sections", status?: "draft" ""ContentItemResponseDtoCreate new content item
PATCH/api/admin/content-items/:idAdmin + Content_UPDATEpath: id (int), body: UpdateContentItemDto "" title?: string, contentBody?: string, previewEnabled?, previewMode?, status? ""ContentItemResponseDtoUpdate content item
DELETE/api/admin/content-items/:idAdmin + Content_DELETEpath: id (int)DeleteResponseDtoDelete content item
POST/api/admin/content-items/:id/publishAdmin + Content_UPDATEpath: id (int), body: PublishContentItemDto "" scheduledAt?: string ""ContentItemResponseDtoPublish content item (optionally schedule)
POST/api/admin/content-items/:id/unpublishAdmin + Content_UPDATEpath: id (int)ContentItemResponseDtoUnpublish/hide content item
POST/api/admin/content-items/:id/tierAdmin + Content_UPDATEpath: id (int), body: SetContentItemTierDto "" tier: "basic"|"premium"|null ""ContentItemTierResponseDtoSet required tier for item
POST/api/admin/content-items/:id/send-emailAdmin + Content_UPDATEpath: id (int), body: SendContentEmailDto "" recipientFilter: "all"|"basic"|"premium" ""SendEmailResponseDtoSend newsletter via email
POST/api/admin/content-items/:id/send-pushAdmin + Content_UPDATEpath: id (int)SendPushResponseDtoSend push notification
GET/api/admin/content-items/:id/previewAdmin + Content_READpath: id (int)ContentItemPreviewDtoPreview content (unpublished items)
GET/api/admin/content-items/:id/fullAdmin + Content_READpath: id (int)ContentItemFullDtoFull content for admin review
GET/api/admin/content-featuredAdmin + Content_READQueryFeaturedContentAdminDto "" page?, size?, pagination?, isActive?, sort?, order? ""FeaturedContentAdminListDto[] paginatedList featured content
GET/api/admin/content-featured/:idAdmin + Content_READpath: id (int)FeaturedContentAdminDetailDtoGet featured content detail
POST/api/admin/content-featuredAdmin + Content_CREATECreateFeaturedContentDto "" contentItemId: number, startDate: string, endDate: string, order?: number ""FeaturedContentResponseDtoCreate featured content entry
PATCH/api/admin/content-featured/:idAdmin + Content_UPDATEpath: id (int), body: UpdateFeaturedContentDto "" startDate?: string, endDate?: string, order?: number, isActive?: boolean ""FeaturedContentResponseDtoUpdate featured content
DELETE/api/admin/content-featured/:idAdmin + Content_DELETEpath: id (int)DeleteResponseDtoRemove featured content
POST/api/admin/content-featured/reorderAdmin + Content_UPDATEbody: ReorderFeaturedContentDto "" ids: number[] ""ReorderResponseDtoReorder featured items
GET/api/content/seriesPublicQueryContentSeriesCustomerDto "" page?, size?, pagination?, type?, sort?, order? ""ContentSeriesListItemDto[] paginatedList active series
GET/api/content/series/:slugPublicpath: slug (string)ContentSeriesDetailDtoGet series by slug with item count
GET/api/content/itemsPublicQueryContentItemsCustomerDto "" page?, size?, pagination?, seriesId?, seriesType?, search?, sort?, order? ""ContentItemListItemDto[] paginatedList published items
GET/api/content/items/:slugPublicpath: slug (string)ContentItemPreviewDtoPreview mode (entitlement not required)
GET/api/content/items/:slug/fullUser JWTpath: slug (string)ContentItemFullDtoFull content (requires entitlement)
GET/api/content/my-libraryUser JWTQueryMyLibraryDto "" page?, size?, pagination?, sort?, order? ""MyLibraryItemDto[] paginatedUser's subscribed content
GET/api/content/items/featuredPublicFeaturedContentListDto[]Active featured items (ordered)
GET/api/content/items/featured/allPublicQueryFeaturedContentCustomerDto "" page?, size?, pagination?, isActive?, sort?, order? ""FeaturedContentListDto[] paginatedAll featured items paginated
POST/api/content/items/:id/bookmarkUser JWTpath: id (int), body: BookmarkContentDto "" isBookmarked: boolean ""BookmarkResponseDtoToggle bookmark on content
GET/api/content/bookmarksUser JWTQueryBookmarksDto "" page?, size?, pagination? ""BookmarkListDto[] paginatedUser's bookmarked items
POST/api/content/items/:id/track-progressUser JWTpath: id (int), body: TrackProgressDto "" progress: number (0-100) ""ProgressResponseDtoTrack reading progress
GET/api/content/progress/:contentIdUser JWTpath: contentId (int)ProgressDetailDtoGet user's progress for content

11.1.1 Customer List Endpoint Query Variations

MethodPathQuery ParametersNotes
GET/api/content/items(no params)Default: page=1, size=20, sort=publishedAt, order=desc
GET/api/content/items?page=2&size=10Custom page and size
GET/api/content/items?seriesId=5Filter by series
GET/api/content/items?seriesType=newsletterFilter by series type
GET/api/content/items?search=marketSearch title/content
GET/api/content/items?sort=title&order=ascSort by title ascending
GET/api/content/items?pagination=falseReturns all records

11.1.2 Admin List Endpoint Query Variations

MethodPathQuery ParametersNotes
GET/api/admin/content-items(no params)Default pagination
GET/api/admin/content-items?seriesId=5&status=publishedFilter by series + status
GET/api/admin/content-items?status=draftFilter by draft status
GET/api/admin/content-items?previewEnabled=trueFilter by preview enabled
GET/api/admin/content-items?search=quarterlySearch content items
GET/api/admin/content-series?type=newsletter&isActive=trueFilter series

11.1.3 Content Item Create/Update Variations

MethodPathRequest BodyNotes
POST/api/admin/content-items"" "seriesId": 1, "title": "Q1 Update", "contentBody": "...", "status": "draft" ""Create draft item
POST/api/admin/content-items"" "seriesId": 1, "title": "Q1 Update", "contentBody": "...", "status": "published" ""Create and publish
POST/api/admin/content-items/:id/publish""Publish immediately
POST/api/admin/content-items/:id/publish"" "scheduledAt": "2026-04-15T09:00:00.000Z" ""Schedule publish
POST/api/admin/content-items/:id/unpublish""Hide published item
PATCH/api/admin/content-items/:id"" "title": "Updated Title" ""Partial update

11.1.4 Tier and Access Variations

MethodPathBodyNotes
POST/api/admin/content-series/1/tier"" "tier": "premium" ""Require premium for series
POST/api/admin/content-items/1/tier"" "tier": "basic" ""Override series tier for item
POST/api/admin/content-items/1/tier"" "tier": null ""Use series default tier

11.1.5 Newsletter Send Variations

MethodPathBodyNotes
POST/api/admin/content-items/1/send-email"" "recipientFilter": "all" ""Send to all subscribers
POST/api/admin/content-items/1/send-email"" "recipientFilter": "premium" ""Send to premium only
POST/api/admin/content-items/1/send-push""Send push to all users
MethodPathBodyNotes
POST/api/admin/content-featured"" "contentItemId": 5, "startDate": "2026-04-01", "endDate": "2026-04-30", "order": 1 ""Create featured
POST/api/admin/content-featured/reorder"" "ids": [3, 1, 5, 2] ""Reorder featured items
PATCH/api/admin/content-featured/1"" "isActive": false ""Deactivate featured

11.1.7 Error Response Variations

HTTPerrorCodeMessageScenario
400CONTENT_STATUS_INVALIDInvalid status transition.Publish archived item
400CONTENT_ARCHIVED_NOT_PUBLISHABLECannot publish archived content.Attempt publish on archived
400CONTENT_SCHEDULED_AT_INVALIDInvalid scheduled date.Invalid timestamp format
400CONTENT_SERIES_CREATE_FAILEDFailed to create content series.DB constraint failure
400CONTENT_NOT_PUBLISHEDContent is not published.Send unpublished newsletter
400FEATURED_CONTENT_DUPLICATE_IDSDuplicate IDs provided.Reorder with duplicates
400FEATURED_CONTENT_INVALID_DATE_RANGEStart date after end date.Invalid date window
403CONTENT_ACCESS_DENIEDAccess denied.Full content without entitlement
404CONTENT_NOT_FOUNDContent item not found.Invalid content ID
404CONTENT_SERIES_NOT_FOUNDContent series not found.Invalid series ID

11.1.8 Cache Key Patterns

Cache KeyPatternTTL (seconds)Invalidation
Customer series listcontent:customer:series:list:pagination:"page"-size:"size"-type:"type"-sort:"sort"-order:"order"300Series write
Customer series detailcontent:customer:series:slug:"slug"300Series update
Customer items listcontent:customer:items:list:pagination:"page"-size:"size"-seriesId:"seriesId"-sort:"sort"120Item publish/unpublish
Customer item previewcontent:customer:item:slug:"slug":preview300Item update
Customer my librarycontent:customer:library:user:"userId":pagination:"page"-size:"size"60User subscribes/access changes
Featured activecontent:featured:customer:active300Featured reorder/change
Featured all paginatedcontent:featured:customer:all:pagination:"page"-size:"size"300Featured change

11.1.9 Rate Limit Patterns

Route FamilyKey PatternLimit
Customer series listrl:content:customer:series:list-key:"userId"60 req/min
Customer items listrl:content:customer:items:list-key:"userId"60 req/min
Customer full contentrl:content:customer:full-key:"userId"30 req/min
Customer my-libraryrl:content:customer:library-key:"userId"30 req/min
Admin content mutationsrl:content:admin:action:mutate-key:"adminId"20 req/min

11.1.10 DTO Field Reference

CreateContentSeriesDto

FieldTypeRequiredValidationNotes
namestringYes1-200 charsSeries name
typeenumYes"newsletter"Series type
slugstringNoauto-generated if nullURL slug
descriptionstringNomax 2000 charsSeries description
isActivebooleanNodefault trueActive status

CreateContentItemDto

FieldTypeRequiredValidationNotes
seriesIdnumberYespositive integerParent series
titlestringYes1-500 charsItem title
slugstringNoauto-generated if nullURL slug
contentBodystringYesmin 1 charFull content HTML/markdown
previewEnabledbooleanNodefault trueEnable preview
previewModeenumNodefault "paragraphs"Preview generation mode
statusenumNodefault "draft"Initial status

PublishContentItemDto

FieldTypeRequiredValidationNotes
scheduledAtstringNoISO-8601 datetimeSchedule future publish

SetContentItemTierDto

FieldTypeRequiredValidationNotes
tierenumYes"basic"|"premium"|nullRequired tier (null = use series)

SendContentEmailDto

FieldTypeRequiredValidationNotes
recipientFilterenumYes"all"|"basic"|"premium"Target recipient segment

CreateFeaturedContentDto

FieldTypeRequiredValidationNotes
contentItemIdnumberYespositive integerContent to feature
startDatestringYesISO-8601 dateFeature start
endDatestringYesISO-8601 dateFeature end (after start)
ordernumberNopositive integerDisplay order

QueryContentSeriesCustomerDto

FieldTypeRequiredValidationNotes
pagenumberNodefault 1Page number
sizenumberNodefault 20, max 100Page size
paginationbooleanNodefault trueEnable pagination
typeenumNo"newsletter"Filter by type
sortenumNodefault "createdAt"Sort field
orderenumNodefault "desc"Sort direction

QueryContentItemsCustomerDto

FieldTypeRequiredValidationNotes
pagenumberNodefault 1Page number
sizenumberNodefault 20, max 100Page size
paginationbooleanNodefault trueEnable pagination
seriesIdnumberNopositive integerFilter by series
seriesTypeenumNo"newsletter"Filter by series type
searchstringNotrimmed stringSearch with similarity
sortenumNodefault "publishedAt"title|publishedAt|createdAt
orderenumNodefault "desc"asc|desc

12. Testing Scenarios

12.1 Suggested Test Scenarios

  • Admin creates content series with tier rule (premium), publishes item, and premium user accesses full content via /items/:slug/full.
  • Basic tier user accesses premium-series item and receives 403 CONTENT_ACCESS_DENIED.
  • Public user accesses preview (GET /api/content/items/:slug) without authentication, content unlocks.
  • Admin publishes item with scheduledAt in future, system publishes automatically at scheduled time.
  • Admin sends newsletter to premium recipients only and email delivers to premium subscribers.
  • Featured content appears in ordered list (GET /api/content/items/featured) within date window.
  • Admin reorders featured items via POST /featured/reorder, new order reflects in customer list.
  • Full content endpoint returns 429 rate limit exceeded after sliding window threshold.

See Also

Time fields in this module are stored as timezone-aware values and should be handled as ISO-8601 instants by API consumers.