<!-- source-hash: 4c8bb965154478fda34b9e6e30abb929 -->
Fetches a fully-mapped content item for an inline chat card by reusing the same list-API endpoint as public-facing pages (`/blog`, `/podcasts`, `/roadmap`, etc.), ensuring a single source of truth for item shape and joins.

## Key Components

### `useChatCardItem<T>(type, id)`
Primary hook. Builds the list URL via `runtime.endpoints.buildListUrl`, fires a TanStack Query fetch, and returns the matched item. Deduplicated by `['chat-card-item', type, id]` query key — multiple references to the same `(type, id)` within one chat message produce a single network request. `refetchOnWindowFocus` is disabled to avoid wasteful re-fetches as the chat panel is toggled.

### `extractItems(data)`
Normalizes heterogeneous API response shapes (`{ items }`, `{ posts }`, `{ campaigns }`, `{ completed, inProgress }`, `{ data }`, or bare arrays) into a flat `unknown[]`. Single normalization point for all content types.

### `extractItemId(type, item)`
Resolves the primary key from a fetched item, handling both `id` and `external_id` fields for ClickUp-backed types (`roadmap_item`, `delivery_item`, `internal_task`).

### `UseChatCardItemResult<T>`
Return type interface exposing `item`, `isLoading`, and `isError`.

## Usage Example

```typescript
import { useChatCardItem } from './use-chat-card-item'

interface BlogPost {
  id: string
  title: string
  author: string
}

function ChatBlogCard({ id }: { id: string }) {
  const { item, isLoading, isError } = useChatCardItem<BlogPost>('post', id)

  if (isLoading) return <Spinner />
  if (isError || !item) return <ErrorCard />

  return <BlogCardPreview title={item.title} author={item.author} />
}
```

> **Runtime dependency:** The hook reads `runtime.endpoints.buildListUrl` from `ChatRuntimeContext`. Hub environments wire the registry-driven builder directly; embedded apps supply a per-type URL builder pointing at the reverse proxy.