Skip to content

Get Generated Story Count#

Returns a deterministic count of generated stories in a date window, with explicit include/exclude filtering and optional audit samples.

Endpoint#

POST /api/app/analytics/generatedStoryCount

Authentication#

This endpoint requires:

  • bearer authentication (Authorization: Bearer <token>)
  • an analytics-enabled CMS role (editor, admin, developer, superadmin, or systemadmin)
  • tenant context (for example x-storyteller-api-key: <tenant-guid>)

See Authentication for details.

Request Body#

Field Type Required Description
startDate string (ISO 8601) Yes Range start timestamp (UTC-normalized)
endDate string (ISO 8601) Yes Range end timestamp, exclusive (UTC-normalized)
profile string No Optional caller-provided profile label echoed in response metadata
includeExternalIdPatterns string[] No SQL-LIKE include patterns over Stories.ExternalId (%, _)
excludeExternalIdPatterns string[] No SQL-LIKE exclude patterns over Stories.ExternalId (%, _)
categoryIds guid[] No Match stories with at least one matching category id
categoryExternalIds string[] No Match stories with at least one matching category external id
publishedAtMode string No earliest (default, durable firstPublishedAt) or current (computed earliestCurrentPublishAt)
auditSampleSize int No Number of deterministic sample rows to return (0..100)

categoryIds and categoryExternalIds are mutually exclusive.

Request#

Example Request#

POST /api/app/analytics/generatedStoryCount
Host: cmsapi.localhost
Authorization: Bearer <jwt>
x-storyteller-api-key: 11111111-2222-3333-4444-555555555555
Content-Type: application/json
{
  "startDate": "2026-02-01T00:00:00Z",
  "endDate": "2026-02-08T00:00:00Z",
  "profile": "nba-automated-v1",
  "excludeExternalIdPatterns": ["00224%-%", "localized-recap%", "top-headlines%"],
  "categoryExternalIds": ["nba"],
  "publishedAtMode": "earliest",
  "auditSampleSize": 5
}

Response#

Success Response#

Status Code: 200 OK

{
  "kind": "analytics.generated-story-count",
  "version": "1",
  "env": "Production",
  "tenant": "11111111-2222-3333-4444-555555555555",
  "range": {
    "startDate": "2026-02-01T00:00:00Z",
    "endDate": "2026-02-08T00:00:00Z",
    "endDateSemantics": "exclusive",
    "timestampField": "firstPublishedAt"
  },
  "filters": {
    "profile": "nba-automated-v1",
    "publishedAtMode": "earliest",
    "categoryMatch": "any",
    "patternSyntax": "sql-like",
    "patternPrecedence": "exclude-wins",
    "includeExternalIdPatterns": [],
    "excludeExternalIdPatterns": ["00224%-%", "localized-recap%", "top-headlines%"],
    "categoryIds": [],
    "categoryExternalIds": ["nba"]
  },
  "metrics": {
    "generatedStoryCount": 123
  },
  "audit": {
    "sampleStoryIds": [
      "8f3c6d9e-40ef-4c61-b340-2134de95ec4a"
    ],
    "sampleExternalIds": [
      "nba-recap-2026-02-07"
    ]
  }
}

Error Responses#

Status Code: 400 Bad Request

Common cases:

  • missing tenant context
  • startDate is not earlier than endDate
  • range is larger than the maximum supported window (120 days)
  • both categoryIds and categoryExternalIds were provided
  • invalid filter values (empty pattern, too many values, oversized values)

Semantics#

  • Date window contract: startDate <= timestamp < endDate (end-exclusive).
  • Timezone behavior: incoming timestamps are normalized to UTC.
  • publishedAtMode = earliest uses the story's first recorded publish timestamp (firstPublishedAt), not a live-delivery transition.
  • In earliest mode, a story can remain countable after it was later unscheduled or moved out of Published.
  • Generated story filter baseline:
  • Stories.Source == IntegrationsApi
  • story is not soft-deleted (current state)
  • story is not hidden from CMS (current state)
  • Pattern precedence: exclude wins over include.
  • Category filter behavior: any match.