# Worked Example: Browse Articles By Curriculum Topic

This example builds the exact student-facing browsing workflow from the customer eval: find Articles for a curriculum topic, show what is available, read a selected article's student-safe view, and display surface-returned evidence. It also proves the current Content contract: the public Content kinds are Content-owned, every row carries `expected_xp`, catalog discovery uses named sidecar facets, Content embeds are relationship rows, Results-owned effectiveness is exposed read-only, and Common Cartridge is generated as transport by the surface. It uses only Content Alpha API calls. It does not parse QTI XML, infer content type, compute scores, compute effectiveness, rewrite media URLs, generate Common Cartridge packages, nest referenced Content payloads, or store Content rows outside the API. Legacy `customerWebsite` links shown in API response examples point to a withdrawn status surface; use the generated data dictionary and architecture for current documentation.

Prerequisites: `curl` and `jq`.

## 1. Set Base URL And Mint Demo Token

```bash
export CONTENT_BASE_URL="${CONTENT_BASE_URL:-https://platform3-andymontgomery-9773s-projects.vercel.app/content/alpha/implementation/api}"
export CONTENT_TENANT="${CONTENT_TENANT:-demo}"

if [ -z "${CONTENT_TOKEN:-}" ]; then
  export CONTENT_TOKEN="$(curl -fsS -X POST "$CONTENT_BASE_URL/dev/mint?tenantId=demo" | jq -r '.token')"
fi
```

For a real tenant, set `CONTENT_BASE_URL`, `CONTENT_TENANT`, and `CONTENT_TOKEN` from operator-supplied credentials. Do not call `/dev/mint` for non-demo tenants.

## 2. Read The Descriptor

```bash
curl -fsS "$CONTENT_BASE_URL" \
  -H "Authorization: Bearer $CONTENT_TOKEN" \
  > /tmp/content-skill-descriptor.json

jq '{module, surface, status, allowedFilters, allowedValues: (.allowedValues | {content_kind, alpha_subject_id, alpha_grade_id, assessment_component_role, expected_xp_band, content_reference_relationship_kind, catalog_facet_key})}' \
  /tmp/content-skill-descriptor.json
```

Expected response shape:

```json
{
  "module": "content",
  "surface": "alpha",
  "status": "ok",
  "allowedFilters": [
    "content_kind",
    "subtype",
    "dok",
    "assessment_role",
    "subject_id",
    "alpha_subject_id",
    "target_grade_id",
    "curriculum_node_id",
    "usage_scope",
    "test_type",
    "is_mastery_gate",
    "source_system",
    "source_record_id",
    "source_activity_ref",
    "facet",
    "modifiedSince",
    "cursor",
    "limit",
    "subject",
    "grade_band",
    "language",
    "accessibility_profile",
    "rights_license",
    "provenance",
    "quality_rating",
    "kc_refs",
    "standards_framework_refs",
    "expected_xp_band",
    "modality"
  ],
  "allowedValues": {
    "content_kind": ["question", "test", "test_spec", "test_bank", "article", "video", "audio", "image", "interactive", "media_asset", "external_link", "script"],
    "alpha_subject_registry_ref": "references/authoring-subject-registry.json#canonicalValues",
    "alpha_grade_id": ["pre_k", "k", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "postsecondary", "ungraded"],
    "assessment_component_role": ["test", "section", "part", "question", "question_ref", "distractor", "rubric", "preauthored_feedback"],
    "expected_xp_band": ["0_to_10", "10_to_20", "20_to_45", "45_plus"],
    "content_reference_relationship_kind": ["embeds", "depends_on", "transcript_for", "caption_for", "thumbnail_for", "source_file_for", "source_script_for", "alternative_form_of", "bank_member", "related_material"],
    "catalog_facet_key": ["subject", "grade_band", "language", "accessibility_profile", "rights_license", "provenance", "quality_rating", "kc_refs", "standards_framework_refs", "content_kind", "expected_xp_band", "modality"]
  }
}
```

Use these values as live descriptor output. Do not copy them into a downstream app as a private enum table. Lessons, lesson sections, playlists, and question banks are Curriculum containers. Video, audio, image, and script are first-class Content kinds when they are the primary student-touchable asset; captions, transcripts, thumbnails, source files, and supporting files are `media_asset` rows or media/reference sidecars.

## 3. List Articles For A KC Discovery Facet

The demo tenant currently has Article rows with the `kc_refs` discovery facet `case:math:7.ee.b.4`. This compatibility catalog value is not registry proof; read `/kc-tags` for the canonical weighted registry relationship.

```bash
export KC_REF="${KC_REF:-case:math:7.ee.b.4}"

curl -fsS "$CONTENT_BASE_URL/tenants/$CONTENT_TENANT/alpha/content/items?content_kind=article&facet=kc_refs:$KC_REF&limit=10" \
  -H "Authorization: Bearer $CONTENT_TOKEN" \
  > /tmp/content-skill-articles.json

jq -r '.data[] | "\(.content_id)\t\(.title)\t\(.subject_id)\tgrade \(.target_grade_id)\t\(.status)"' \
  /tmp/content-skill-articles.json
```

Expected response shape:

```json
{
  "data": [
    {
      "content_id": "f71f3b0a-e271-40ea-b990-89940f371012",
      "workspace_id": "0d4ce2f4-1c42-4f3c-9f0d-03fb7f5271d3",
      "content_kind": "article",
      "subtype": null,
      "dok": null,
      "title": "Integration article 1d0b6d39 revised",
      "language": "en-US",
      "source_identifier": null,
      "subject_id": "math",
      "target_grade_id": "7",
      "expected_xp": 12,
      "usage_scope": "practice_eligible",
      "assessment_role": null,
      "test_type": null,
      "is_mastery_gate": null,
      "test_family_id": null,
      "phonics_constraint": null,
      "constraint_set_id": null,
      "policy_version": null,
      "latest_version_id": "5a9c6b6d-c955-4fec-8b73-a3ced5fe22c6",
      "source_system": "alpha_content",
      "source_record_id": null,
      "source_activity_ref": null,
      "status": "draft",
      "created_at": "2026-06-04T09:44:25.292Z",
      "updated_at": "2026-06-04T09:44:26.072Z",
      "retired_at": null
    }
  ],
  "nextCursor": "Mw",
  "hasMore": true,
  "links": {
    "dataDictionary": "https://platform3-andymontgomery-9773s-projects.vercel.app/content/alpha/data_dictionary",
    "customerWebsite": "https://platform3-andymontgomery-9773s-projects.vercel.app/content/alpha/customer_website#endpoint-list-content-items"
  }
}
```

If the list is empty, do not fuzzy-search titles or parse source paths. Report that no Content rows match the typed filters.

For a library or LOR-style browse, use the same list endpoint with named facet filters:

```bash
curl -fsS "$CONTENT_BASE_URL/tenants/$CONTENT_TENANT/alpha/content/items?facet=subject:math&facet=grade_band:middle_school&facet=kc_refs:$CURRICULUM_NODE_ID&facet=expected_xp_band:10_to_20&limit=10" \
  -H "Authorization: Bearer $CONTENT_TOKEN" \
  > /tmp/content-skill-facet-browse.json
```

This is still a surface call. Do not build a local catalog index.

## 4. Read Detail And Student View For The Selected Article

```bash
export CONTENT_ID="$(jq -r '.data[0].content_id // empty' /tmp/content-skill-articles.json)"

if [ -z "$CONTENT_ID" ]; then
  echo "No articles returned for $CURRICULUM_NODE_ID" >&2
  exit 1
fi

curl -fsS "$CONTENT_BASE_URL/tenants/$CONTENT_TENANT/alpha/content/items/$CONTENT_ID" \
  -H "Authorization: Bearer $CONTENT_TOKEN" \
  > /tmp/content-skill-detail.json

curl -fsS "$CONTENT_BASE_URL/tenants/$CONTENT_TENANT/alpha/content/items/$CONTENT_ID/student-view" \
  -H "Authorization: Bearer $CONTENT_TOKEN" \
  > /tmp/content-skill-student-view.json
```

Expected detail response shape:

```json
{
  "content_id": "f71f3b0a-e271-40ea-b990-89940f371012",
  "workspace_id": "0d4ce2f4-1c42-4f3c-9f0d-03fb7f5271d3",
  "content_kind": "article",
  "title": "Integration article 1d0b6d39 revised",
  "language": "en-US",
  "source_identifier": null,
  "subject_id": "math",
  "target_grade_id": "7",
  "expected_xp": 12,
  "test_type": null,
  "is_mastery_gate": null,
  "test_family_id": null,
  "latest_version_id": "5a9c6b6d-c955-4fec-8b73-a3ced5fe22c6",
  "source_system": "alpha_content",
  "status": "draft",
  "created_at": "2026-06-04T09:44:25.292Z",
  "updated_at": "2026-06-04T09:44:26.072Z",
  "retired_at": null
}
```

Expected student-view response shape:

```json
{
  "version_id": "5a9c6b6d-c955-4fec-8b73-a3ced5fe22c6",
  "student_view": {
    "kind": "article",
    "title": "Integration article 1d0b6d39",
    "blocks": [
      {"text": "Two-step equations", "type": "heading"},
      {"text": "Solve by balancing both sides. Content stores the blank material only.", "type": "paragraph"}
    ]
  },
  "etag": "W/\"content:f71f3b0a:v1:98c2461145df\"",
  "answerSlots": [],
  "answerKeysIncluded": false,
  "links": {
    "dataDictionary": "https://platform3-andymontgomery-9773s-projects.vercel.app/content/alpha/data_dictionary#object-content-version",
    "customerWebsite": "https://platform3-andymontgomery-9773s-projects.vercel.app/content/alpha/customer_website#endpoint-get-student-view"
  }
}
```

For a student-facing app, render `student_view` and keep answer keys out of the UI. Do not submit student answers through Content.

## 5. Read Media, References, KC Tags, Trust, And Effectiveness

```bash
for suffix in media references kc-tags catalog-facets effectiveness trust versions; do
  curl -fsS "$CONTENT_BASE_URL/tenants/$CONTENT_TENANT/alpha/content/items/$CONTENT_ID/$suffix" \
    -H "Authorization: Bearer $CONTENT_TOKEN" \
    > "/tmp/content-skill-$suffix.json"
done

curl -fsS "$CONTENT_BASE_URL/tenants/$CONTENT_TENANT/alpha/content/policies?policy_key=alpha.policy.content_effectiveness" \
  -H "Authorization: Bearer $CONTENT_TOKEN" \
  > /tmp/content-skill-policies.json
```

Expected media response shape:

```json
{
  "data": [
    {
      "media_id": "4a343d51-d65b-4425-bfa2-293211c45251",
      "content_id": "f71f3b0a-e271-40ea-b990-89940f371012",
      "version_id": "5a9c6b6d-c955-4fec-8b73-a3ced5fe22c6",
      "media_kind": "transcript",
      "media_type": "text/plain",
      "byte_length": 104,
      "delivery_url": "/content/alpha/implementation/api/tenants/demo/alpha/content/items/f71f3b0a/media/4a343d51",
      "metadata": {"title": "Worked solution transcript", "language": "en-US"}
    }
  ],
  "nextCursor": null,
  "hasMore": false
}
```

Expected references response shape:

```json
{
  "data": [
    {
      "content_reference_id": "67b4ddfb-5369-4fd8-a0ef-e139e3638b01",
      "parent_content_id": "f71f3b0a-e271-40ea-b990-89940f371012",
      "parent_content_kind": "article",
      "referenced_content_id": "9a49cb4d-7840-42d4-a8a6-512f19dbe98ca",
      "referenced_content_kind": "image",
      "relationship_kind": "embeds",
      "display_order": 1
    }
  ],
  "nextCursor": null,
  "hasMore": false
}
```

The list may be empty. If rows are present, treat them as relationship facts. Do not copy the referenced content payload into the parent row; call the referenced content's own detail or student-view endpoint when needed.

Expected weighted KC-tag response shape (the list may be empty until an authorized tagging workflow writes a registry KC):

```json
{
  "data": [
    {
      "tenant_id": "demo",
      "content_id": "f71f3b0a-e271-40ea-b990-89940f371012",
      "kc_id": "001c6d5d-b370-559a-93d4-3483845ad2f2",
      "weight": 0.8,
      "is_primary": true,
      "provenance": "teacher-review",
      "confidence": 0.95,
      "created_at": "2026-07-16T13:30:00.000Z",
      "updated_at": "2026-07-16T13:30:00.000Z",
      "retired_at": null
    }
  ],
  "nextCursor": null,
  "hasMore": false
}
```

Expected catalog-facets response shape:

```json
{
  "data": [
    {
      "facet_assignment_id": "1fd04ddf-f5e7-4fd5-b170-cf0345711f39",
      "content_id": "f71f3b0a-e271-40ea-b990-89940f371012",
      "version_id": null,
      "media_id": null,
      "facet_key": "expected_xp_band",
      "facet_value": "10_to_20",
      "provenance": {"policy": "alpha.policy.content.expected_xp_band.v1"}
    }
  ],
  "nextCursor": null,
  "hasMore": false
}
```

Expected effectiveness response shape:

```json
{
  "content_id": "f71f3b0a-e271-40ea-b990-89940f371012",
  "validated_by_count": 0,
  "failed_by_count": 0,
  "effectiveness_rate": null,
  "freshness_status": "insufficient_data",
  "last_recomputed_at": null,
  "provenance": {
    "policy": "alpha.policy.content_effectiveness.v1",
    "resultCount": 0,
    "sourceModule": "results"
  }
}
```

Expected trust response shape:

```json
{
  "content_id": "f71f3b0a-e271-40ea-b990-89940f371012",
  "trust_status": "unknown",
  "evidence_url": null,
  "checked_at": null,
  "problem_code": null
}
```

Expected policies response shape:

```json
{
  "data": [
    {
      "policy_key": "alpha.policy.content_effectiveness.freshness_window_days",
      "value_json": {"days": 1},
      "policy_version": "2026-06-03.1"
    },
    {
      "policy_key": "alpha.policy.content_effectiveness.minimum_eligible_results",
      "value_json": {"minimum": 10},
      "policy_version": "2026-06-03.1"
    }
  ],
  "nextCursor": null,
  "hasMore": false
}
```

Display these values. Do not compute freshness, effectiveness rate, or policy thresholds locally.

## 6. Fetch A Returned Media URL Exactly

```bash
export CONTENT_MEDIA_URL="$(jq -r '.data[0].delivery_url // empty' /tmp/content-skill-media.json)"

content_resolve_url() {
  case "$1" in
    http://*|https://*) printf '%s' "$1" ;;
    /content/alpha/implementation/api/*)
      printf '%s%s' "$(printf '%s' "$CONTENT_BASE_URL" | sed 's#/content/alpha/implementation/api$##')" "$1" ;;
    *) printf '%s%s' "$CONTENT_BASE_URL" "$1" ;;
  esac
}

if [ -n "$CONTENT_MEDIA_URL" ]; then
  curl -fsS "$(content_resolve_url "$CONTENT_MEDIA_URL")" \
    -H "Authorization: Bearer $CONTENT_TOKEN" \
    -D /tmp/content-skill-media.headers \
    -o /tmp/content-skill-media.bytes
fi
```

Use the `delivery_url` exactly as returned. Do not rewrite the locator, append credentials, or guess a storage path.

## 7. Produce The Student-Facing Availability Report

```bash
{
  echo "# Article Browser Data"
  echo
  echo "KC discovery facet: $KC_REF"
  echo "Tenant: $CONTENT_TENANT"
  echo
  echo "## Available Articles"
  jq -r '.data[] | "- \(.title) (`\(.content_id)`), \(.subject_id) grade \(.target_grade_id), status \(.status)"' /tmp/content-skill-articles.json
  echo
  echo "## Selected Article"
  jq -r '"- Title: \(.title)\n- Content id: \(.content_id)\n- Kind: \(.content_kind)\n- Source system: \(.source_system)\n- Latest version: \(.latest_version_id)"' /tmp/content-skill-detail.json
  jq -r '"- Expected XP: \(.expected_xp)\n- Test type: \(.test_type // "none")\n- Mastery gate: \(.is_mastery_gate // "none")"' /tmp/content-skill-detail.json
  echo
  echo "## Student View"
  jq -r '"- Answer keys included: \(.answerKeysIncluded)\n- View kind: \(.student_view.kind)\n- Title: \(.student_view.title)"' /tmp/content-skill-student-view.json
  jq -r '.student_view.blocks[]? | "- \(.type): \(.text)"' /tmp/content-skill-student-view.json
  echo
  echo "## Weighted KC Tags"
  jq -r '.data[]? | "- \(.kc_id): weight=\(.weight), primary=\(.is_primary), confidence=\(.confidence)"' /tmp/content-skill-kc-tags.json
  echo
  echo "## Content References"
  jq -r '.data[]? | "- \(.relationship_kind): \(.referenced_content_kind) `\(.referenced_content_id)`"' /tmp/content-skill-references.json
  echo
  echo "## Catalog Facets"
  jq -r '.data[]? | "- \(.facet_key): \(.facet_value)"' /tmp/content-skill-catalog-facets.json
  echo
  echo "## Media"
  jq -r '.data[]? | "- \(.media_kind): \(.media_type) \(.delivery_url)"' /tmp/content-skill-media.json
  echo
  echo "## Trust"
  jq -r '"- Status: \(.trust_status)\n- Evidence: \(.evidence_url // "none returned")"' /tmp/content-skill-trust.json
  echo
  echo "## Effectiveness"
  jq -r '"- Validated by: \(.validated_by_count)\n- Failed by: \(.failed_by_count)\n- Rate: \(.effectiveness_rate // "not enough data")\n- Freshness: \(.freshness_status)"' /tmp/content-skill-effectiveness.json
} > /tmp/content-article-browser-data.md

cat /tmp/content-article-browser-data.md
```

This report is safe input for a small student app. Its facts come from Content Alpha responses.

## 8. Optional Common Cartridge Export

Use this when the job is to package a Curriculum subtree for a catalog or LOR flow. The request starts from the Curriculum subtree id and Content references; the surface generates the transport package and reports package-construction inspection metadata. Despite the current `round_trip_check` field name and `passed` status, the runtime does not re-import the package or prove lossless round-trip equivalence.

```bash
curl -fsS -X POST "$CONTENT_BASE_URL/tenants/$CONTENT_TENANT/alpha/content/exports/common-cartridge" \
  -H "Authorization: Bearer $CONTENT_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"course_component_id":"cur_comp_math_7_linear_equations","curriculum_node_id":"case:math:7.ee.b.4","format":"common_cartridge_1_3","round_trip_check":true}' \
  > /tmp/content-skill-common-cartridge.json

jq '{export_id, format, download_url, round_trip_check, content_refs}' \
  /tmp/content-skill-common-cartridge.json
```

Expected response shape:

```json
{
  "export_id": "cc_export_9ef...",
  "format": "common_cartridge_1_3",
  "source_course_component_id": "cur_comp_math_7_linear_equations",
  "download_url": "/content/alpha/implementation/api/tenants/demo/alpha/content/exports/common-cartridge/cc_export_9ef.../download?state=...",
  "round_trip_check": {
    "status": "passed",
    "translation_logic_used": false,
    "manifest_resource_count": 3,
    "zip_entry_count": 8,
    "package_byte_length": 1234
  },
  "content_refs": [
    {"content_id": "f71f3b0a-e271-40ea-b990-89940f371012", "content_kind": "article"}
  ]
}
```

To download the package, use `download_url` exactly as returned with the same bearer token. Do not generate or inspect the package to build a normal app; the surface owns export packaging. Treat `round_trip_check` as export inspection only until an actual reimport-and-compare path exists.

## 9. Optional QTI Question Probe

Use this only when the job involves QTI-backed material. It proves that QTI material is launched through Content without local parsing.

```bash
curl -fsS "$CONTENT_BASE_URL/tenants/$CONTENT_TENANT/alpha/content/items?content_kind=question&subject_id=math&target_grade_id=7&limit=3" \
  -H "Authorization: Bearer $CONTENT_TOKEN" \
  > /tmp/content-skill-questions.json

export QUESTION_CONTENT_ID="$(jq -r '.data[0].content_id // empty' /tmp/content-skill-questions.json)"

curl -fsS "$CONTENT_BASE_URL/tenants/$CONTENT_TENANT/alpha/content/items/$QUESTION_CONTENT_ID/student-view" \
  -H "Authorization: Bearer $CONTENT_TOKEN" \
  > /tmp/content-skill-question-student-view.json
```

Expected QTI-backed `student-view` shape:

```json
{
  "version_id": "49d5c102-923f-4c28-9d28-78f9647bd6cf",
  "student_view": {
    "kind": "question",
    "title": "Imported QTI package",
    "identifier": "sha256:a3bf367bcb99bd57511d8742c406060937059de5b32cc137a342c25f30be92bb"
  },
  "answerSlots": [],
  "answerKeysIncluded": false
}
```

For a QTI package import proof, send package bytes to `POST /tenants/{tenantId}/alpha/content/imports/qti-package` with `Content-Type: application/zip`, an `Idempotency-Key`, and all three producer-governed classification headers: `X-Content-Subject-Id`, `X-Content-Target-Grade-Id`, and calibrated `X-Content-Expected-Xp`. Supply those values from the approved producer artifact and calibration; never infer them from package metadata, titles, paths, or course defaults. Do not unpack or parse the package locally. Referenced assessment-stimulus XML is returned inside each question `student_view.stimuli[]` and prompt, with images rewritten to question media delivery URLs; render that view directly. A stimulus-only package is rejected, and authored article prose/images belong in `student_view.blocks` on `POST /items/{contentId}/versions`.
