# Using the CardSight AI API - Field Catalog (Flexible Metadata)

## Overview
Every trading card game carries different metadata — Pokémon cards have HP and Rarity, Magic: The Gathering cards have Mana Cost and Artist, One Piece cards have Power and Counter, Yu-Gi-Oh! cards have Attribute and Level. Rather than hard-coding per-game columns, CardSight models this as a flexible **Fields** system. Any card, set, release, or segment can carry key/value metadata, and the catalog exposes that field vocabulary as a first-class browsable entity.

Use these endpoints to discover what fields exist, how widely each one is used, and to look up field IDs for filtering other catalog queries.

## Endpoint Details

### List Fields
- **Method**: GET
- **URL**: `https://api.cardsight.ai/v1/catalog/fields`
- **Authentication**: API Key required (X-Api-Key header)

### Get Field Detail
- **Method**: GET
- **URL**: `https://api.cardsight.ai/v1/catalog/fields/{id}`
- **Authentication**: API Key required (X-Api-Key header)

## Parameters (List)

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| name | string | No | Filter by display name using partial, case-insensitive matching. Example: `points` matches "Hit Points" and "Attack Points". |
| key | string | No | Filter by field key using partial, case-insensitive matching. Example: `hp` matches keys `HP`, `SHP`, etc. |
| sort | string | No | Field to sort by. One of `name`, `key`, `usageCount`. No default — omit to accept server ordering. |
| order | string | No | Sort direction. `asc` (default) or `desc`. |
| take | integer | No | Page size. Min 1, max 100, default 20. |
| skip | integer | No | Pagination offset. Default 0. |

## Using the CardSight AI SDK (Recommended)

### Node.js / TypeScript
```typescript
import { CardSightAI } from 'cardsightai'

const client = new CardSightAI({ apiKey: 'your-api-key' })

// Browse the most-used fields in the catalog
// Note: order defaults to "asc", so set "desc" explicitly for a top-down list
const list = await client.catalog.fields.list({
  sort: 'usageCount',
  order: 'desc',
  take: 20,
})

if (!list.error) {
  for (const field of list.data.fields) {
    console.log(`${field.name} (${field.key}) — ${field.usageCount} entities`)
  }
}

// Filter by display name (partial, case-insensitive)
const artistLike = await client.catalog.fields.list({ name: 'artist' })

// Filter by field key (partial, case-insensitive) — finds HP, SHP, ...
const hpLike = await client.catalog.fields.list({ key: 'hp' })

// Look up a single field by ID
const detail = await client.catalog.fields.get('550e8400-e29b-41d4-a716-446655440000')
if (!detail.error) {
  console.log(detail.data.name, detail.data.key, detail.data.usageCount)
}
```

### Python
```python
from cardsightai import CardSightAI

client = CardSightAI(api_key='your-api-key')

# order defaults to asc, so pass desc explicitly when you want the biggest first
fields = client.catalog.fields.list(sort='usageCount', order='desc', take=20)
for field in fields.fields:
    print(f"{field.name} ({field.key}) — {field.usage_count} entities")

artist_like = client.catalog.fields.list(name='artist')
hp_like = client.catalog.fields.list(key='hp')
```

## Direct API Call (cURL)
```bash
# Top 20 most-used fields
curl -X GET "https://api.cardsight.ai/v1/catalog/fields?sort=usageCount&order=desc&take=20" \
  -H "X-Api-Key: your-api-key"

# Partial, case-insensitive name filter
curl -X GET "https://api.cardsight.ai/v1/catalog/fields?name=artist" \
  -H "X-Api-Key: your-api-key"

# Partial, case-insensitive key filter
curl -X GET "https://api.cardsight.ai/v1/catalog/fields?key=hp" \
  -H "X-Api-Key: your-api-key"

# Field detail by UUID
curl -X GET "https://api.cardsight.ai/v1/catalog/fields/550e8400-e29b-41d4-a716-446655440000" \
  -H "X-Api-Key: your-api-key"
```

## Example Response (List)
```json
{
  "fields": [
    {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "key": "ARTIST",
      "name": "Artist",
      "description": "Illustrator credited on the card",
      "usageCount": 48231
    },
    {
      "id": "550e8400-e29b-41d4-a716-446655440001",
      "key": "RARITY",
      "name": "Rarity",
      "description": "Rarity tier (Common, Uncommon, Rare, Holo Rare, etc.)",
      "usageCount": 39104
    },
    {
      "id": "550e8400-e29b-41d4-a716-446655440002",
      "key": "HP",
      "name": "Hit Points",
      "description": "Base HP value on Pokémon cards",
      "usageCount": 11520
    }
  ],
  "total_count": 58,
  "skip": 0,
  "take": 20
}
```

## Example Response (Detail)
```json
{
  "id": "550e8400-e29b-41d4-a716-446655440002",
  "key": "HP",
  "name": "Hit Points",
  "description": "Base HP value on Pokémon cards",
  "usageCount": 11520
}
```

## Response Fields

### PaginatedFieldsResponse

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| fields | DetailedFieldResponse[] | Yes | Array of field definitions matching the query |
| total_count | number | Yes | Total fields matching the query |
| skip | number | Yes | Offset applied |
| take | number | Yes | Page size applied |

### DetailedFieldResponse
Returned both as the items of `PaginatedFieldsResponse.fields` and as the body of `GET /v1/catalog/fields/{id}`.

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| id | UUID | Yes | Permanent field UUID |
| key | string | Yes | Field key used in values and filters. Typically uppercase, e.g. `HP`, `ARTIST`, `RARITY` |
| name | string | Yes | Human-readable display name, e.g. "Hit Points", "Artist" |
| description | string | No | Optional explanation of what the field represents (omitted when not provided) |
| usageCount | number | Yes | Total catalog entities (cards, sets, releases, segments) that carry a value for this field |

## Relation to Identify Responses
Every identify detection's `card` object now includes a `fields` array carrying the same keys. Combined with these endpoints, you can:
1. Call `/v1/catalog/fields` to discover which metadata is available for the current segment
2. Call `/v1/identify/card` and read the returned `fields` array directly on each detection
3. Use helpers `getFieldValue(detection, 'ARTIST')` and `formatFieldValues(detection)` from the SDK

## Common Use Cases
- Building filter UIs that adapt to whichever fields are relevant for the active segment
- Discovering what metadata exists for non-sports cards (Pokémon, Magic, One Piece, Yu-Gi-Oh!)
- Looking up a field ID to reference in downstream catalog filters
- Educating users about the flexible metadata surfaced on identify results

## Tips for AI Assistants
- Keys are typically UPPERCASE (e.g. `HP`, `ARTIST`, `MANA_COST`). The `name` and `key` query filters are partial and case-insensitive, so casing does not matter when filtering — but when you read a key out of a response or pass it to other API calls, match the casing that the server returns.
- `usageCount` is a combined count across cards, sets, releases, and segments — a single tally, not a per-entity breakdown.
- To surface the most universal fields first, pass `sort=usageCount&order=desc`. The server default for `order` is `asc`, so you must explicitly set `desc` for a descending list.
- There is no server-side default for `sort` — omit it to accept the server's ordering, or set it explicitly.
- `take` is capped at 100. For larger exports, paginate with `skip` and watch `total_count`.
- Use the detail endpoint (`/v1/catalog/fields/{id}`) to confirm a field's ID before passing it to filter queries.
- This vocabulary powers cross-TCG support; one SDK surface works for every game.
