# Get item lineage

```http
GET /api/public/v1/contributor-network/items/{itemId}/graph
```

**Scope**: `contributor-network.read`.

Returns a lineage graph centered on one active public item. The graph contains the complete ancestor chain to the lineage root and active descendants within the requested distance. Reading lineage requires neither Competition enrollment nor account setup.

## Path parameter

| Parameter | Requirement |
| :--- | :--- |
| `itemId` | **Required string.** Opaque Contributor Network item ID. |

## Query parameter

| Parameter | Type, requirement, default, and meaning |
| :--- | :--- |
| `descendantDepth` | **Optional integer from `0` through `10`; defaults to `1`.** Maximum number of parent-child edges to traverse below the anchor. `0` returns only the anchor and its ancestors. |

Unknown query parameters, repeated parameters, blank values, and values outside the documented range return `422 RESOURCE_FIELD_INVALID`.

## Graph membership

The anchor and its owning Competition must be publicly visible, and the Competition must expose Contributor Network items. The graph then contains:

- the anchor;
- every ancestor from the anchor to its lineage root, regardless of current availability;
- every active descendant no more than `descendantDepth` parent-child edges below the anchor; and
- an unavailable intermediate node only when it is required to connect an included active descendant.

Every edge stays within one Competition and one item `kind`. Kind-specific publication rules still apply: for example, a Lean Kernel package lineage also stays within one Problem, while a Stage 2 solver-template lineage may cross tracks or models.

The response never silently truncates one node's children. It contains at most 2,000 nodes; if the selected traversal would exceed that bound, the endpoint returns `413 GRAPH_TOO_LARGE` without a partial graph. Reduce `descendantDepth`, then continue from active boundary-item IDs returned by the smaller graph. `directChildCount` lets a client tell whether an included node has active children beyond the returned depth.

## Response

Returns `200 OK` with:

| Field | Type | Description |
| :--- | :--- | :--- |
| `anchorId` | string | The requested item ID. |
| `nodes` | [`GraphNode[]`](#graph-node) | The anchor, selected active relatives, and required redacted placeholders. |
| `edges` | [`GraphEdge[]`](#graph-edge) | Directed parent-to-child relationships between returned nodes. |

### Graph node

Every `GraphNode` contains:

| Field | Type | Description |
| :--- | :--- | :--- |
| `id` | string | Stable, opaque Contributor Network item ID. |
| `publicCode` | string | Immutable human-shareable code. |
| `competitionId` | string | Competition that owns the lineage. |
| `kind` | string | `cheatsheet`, `solver-template`, `model-reference`, or `lean-kernel-package`. |
| `lineageDepth` | integer | Number of parent edges from the lineage root; `0` identifies the root. |
| `available` | boolean | Whether the item is active and can be fetched through the item-detail endpoint. |
| `directChildCount` | integer | Active items that directly reference this node, including children outside the requested descendant depth. |

This item-centered response adds one field to that common graph-node shape:

| Field | Type | Description |
| :--- | :--- | :--- |
| `distanceFromAnchor` | integer | Signed edge distance from the requested item: ancestors are negative, the anchor is `0`, and descendants are positive. |

An available node also contains:

| Field | Type | Description |
| :--- | :--- | :--- |
| `title` | string | Immutable publication title. |
| `author.sairId` | string | Public SAIR account ID snapshotted at publication. |
| `author.displayName` | string | Public display name snapshotted at publication. |
| `author.avatarUrl` | string \| null | Public avatar URL, or `null` when absent. |
| `publishedAt` | string | ISO 8601 UTC publication time. |

An unavailable node returns `available: false` and omits `title`, `author`, and `publishedAt`. It also never exposes the original remark, kind-specific payload, favorite or comment state, or download metadata. Its structural ID, public code, discriminator, lineage depth, item-relative distance, and active-child count remain so clients can render the surviving public lineage without reconstructing withdrawn content.

### Graph edge

| Field | Type | Description |
| :--- | :--- | :--- |
| `parent` | string | Parent item ID. |
| `child` | string | Direct child item ID. |

Both endpoints of every edge appear in `nodes`. Treat `nodes` and `edges` as graph sets rather than relying on array order.

### Example

```json
{
  "ok": true,
  "data": {
    "anchorId": "cn_01K4D8M2P7A6R9T3V5X1Y0Z8Q",
    "nodes": [
      {
        "id": "cn_01K4CZZP6M1T7A9R2V8X3Y5Q0",
        "publicCode": "MDS1-C000014",
        "competitionId": "mathematics-distillation-challenge-equational-theories-stage1",
        "kind": "cheatsheet",
        "lineageDepth": 0,
        "distanceFromAnchor": -1,
        "available": false,
        "directChildCount": 1
      },
      {
        "id": "cn_01K4D8M2P7A6R9T3V5X1Y0Z8Q",
        "publicCode": "MDS1-C000021",
        "competitionId": "mathematics-distillation-challenge-equational-theories-stage1",
        "kind": "cheatsheet",
        "lineageDepth": 1,
        "distanceFromAnchor": 0,
        "available": true,
        "directChildCount": 1,
        "title": "Compact rewrite strategy",
        "author": {
          "sairId": "U-1042",
          "displayName": "Ada",
          "avatarUrl": null
        },
        "publishedAt": "2026-08-31T09:15:00Z"
      },
      {
        "id": "cn_01K4E1H8S2B6N0R7T9V3X5Y4Q",
        "publicCode": "MDS1-C000024",
        "competitionId": "mathematics-distillation-challenge-equational-theories-stage1",
        "kind": "cheatsheet",
        "lineageDepth": 2,
        "distanceFromAnchor": 1,
        "available": true,
        "directChildCount": 0,
        "title": "Rewrite strategy with guard lemmas",
        "author": {
          "sairId": "U-2084",
          "displayName": "Lin",
          "avatarUrl": "https://cdn.sair.foundation/avatars/U-2084.png"
        },
        "publishedAt": "2026-08-31T12:40:00Z"
      }
    ],
    "edges": [
      {
        "parent": "cn_01K4CZZP6M1T7A9R2V8X3Y5Q0",
        "child": "cn_01K4D8M2P7A6R9T3V5X1Y0Z8Q"
      },
      {
        "parent": "cn_01K4D8M2P7A6R9T3V5X1Y0Z8Q",
        "child": "cn_01K4E1H8S2B6N0R7T9V3X5Y4Q"
      }
    ]
  }
}
```

Successful responses use `Content-Type: application/json` and `Cache-Control: private, no-store`.

## Errors

| HTTP | Code | When |
| :---: | :--- | :--- |
| `404` | `NOT_FOUND` | The anchor item or its owning Competition is unavailable, or the Competition does not expose Contributor Network items. |
| `413` | `GRAPH_TOO_LARGE` | The selected traversal would contain more than 2,000 nodes. No partial graph is returned. |
| `422` | `RESOURCE_FIELD_INVALID` | `descendantDepth` or another query parameter is invalid, unknown, or repeated. |

See [Errors](../../errors.md) for shared errors.
