---
title: "List Products"
method: GET
path: "/v1/products"
tags: ["Product Catalog"]
---

# List Products

`GET /v1/products`

## Request

- Base URL: `https://api.xdata.kr` (the document declares no server; this is the origin it was published from)
- URL: `https://api.xdata.kr/v1/products`
- Auth: one of:
  - HTTP basic
  - OAuth 2 (authorization code, authorization URL `https://api.xdata.kr/oauth/authorize`, token URL `https://api.xdata.kr/oauth/token`)

## Query parameters

- `query` ProductListQuery, required: `GET /v1/products` 쿼리 파라미터. 페이지네이션 + 필터. 헤더 `X-Env-Scope` 는 서비스 레벨에서 강제 부착(본 DTO 영역 외).
  - `limit` integer: 1~100.
  - `cursor` string, nullable: opaque 커서 (BIGINT after_id 또는 URL-safe base64 인코딩 페이로드 — RFC 4648 §5). 최대 2048자 — RFC 7230 권고 HTTP URI 한도 (~8KB) 아래로 안전 마진. 운영 페이로드는 통상 256 bytes 이내. 클라이언트는 본문을 해석하거나 만들지 않는다.
  - `sort` 'created_at:asc' | 'created_at:desc' | 'updated_at:asc' | 'updated_at:desc' | 'code:asc' | 'code:desc'
  - `from` string, date-time, nullable: created_at 시작 시각 (포함). aware datetime 필수. 쿼리스트링 이름은 `?from=` 하나다 — OpenAPI 에 선언되는 이름이자 공개 계약이다. Python 예약어 회피용 필드명 `from_` 는 모델 생성(`ProductListQuery(from_=...)`)과 `model_validate` 에서만 쓴다(`populate_by_name=True`).
  - `to` string, date-time, nullable: created_at 종료 시각 (포함). aware datetime 필수.
  - `status` string[], nullable: 상태 필터. 다중 콤마 (`?status=ACTIVE,DEPRECATED`) 또는 반복형 (`?status=ACTIVE&status=DEPRECATED`) 양쪽 허용. 최대 20 원소.
  - `visibility` string[], nullable: 가시성 필터. status 와 동일 - 콤마 · 반복형 양쪽 허용 + 최대 20 원소.
  - `provider` string, nullable: 등록된 기관 식별자로 걸러낸다. 형식 `^[a-z][a-z0-9_]{0,63}$`.
  - `category` 'tax' | 'gov' | 'finance' | 'marketing', nullable
  - `include_deleted` boolean: true + 운영자 토큰 시만 deleted_at NOT NULL 행을 노출한다.
  - `q` string, nullable: 이름/code 부분일치 검색(ILIKE 기반).
  - `website_slug` string, nullable: Website URL slug exact match (`api_products.website_slug`). Sprint 2 legacy routing SSOT.
  - `website_legacy_id` string, nullable: Legacy mock id exact match (`metadata.website_legacy_id`, e.g. hometax-1).
  - `expose_mode_filter` 'marketing_only' | 'exclude_marketing_only', nullable: Admin catalog list filter on metadata.expose_mode. `marketing_only` = 소개만; `exclude_marketing_only` = 소개 제외. Omit for all. Server-side WHERE only (no client page-local filter).
- `env_scope` 'production' | 'real_test' | 'sandbox', nullable: 환경을 고릅니다. 값은 production · real_test · sandbox 중 하나입니다. 경로 접두(/v1/sandbox/... · /v1/test/... · /v1/production/...) · X-Env-Scope 헤더 · env_scope 쿼리 가운데 한 통로로 반드시 보내야 합니다. 빼면 자동으로 채우지 않고 거절하며, 두 통로에 서로 다른 값을 담아도 거절합니다. 거절은 HTTP 422 이고 오류 코드는 VALIDATION_ENV_SCOPE_MISMATCH 입니다.

## Headers

- `Authorization` string, nullable
- `X-Env-Scope` 'production' | 'real_test' | 'sandbox', nullable: 환경을 고릅니다. 값은 production · real_test · sandbox 중 하나입니다. 경로 접두(/v1/sandbox/... · /v1/test/... · /v1/production/...) · X-Env-Scope 헤더 · env_scope 쿼리 가운데 한 통로로 반드시 보내야 합니다. 빼면 자동으로 채우지 않고 거절하며, 두 통로에 서로 다른 값을 담아도 거절합니다. 거절은 HTTP 422 이고 오류 코드는 VALIDATION_ENV_SCOPE_MISMATCH 입니다.

## Response `200`

Successful Response

- PageEnvelopeProductListItem
  - `items` ProductListItem[]: Page result items. Empty list allowed.
    - `id` integer, required
    - `code` string, required
    - `name` string, required
    - `status` 'DRAFT' | 'ACTIVE' | 'DEPRECATED' | 'RETIRED', required
    - `visibility` 'PUBLIC' | 'PRIVATE' | 'ENTERPRISE', required
    - `category` 'tax' | 'gov' | 'finance' | 'marketing', nullable
    - `organization_id` integer, nullable
    - `env_scope` 'sandbox' | 'real_test' | 'production', required
    - `current_version_id` integer, nullable
    - `current_version_summary` CurrentVersionSummary: 상품 응답에 nested 로 동봉되는 활성 버전 요약. `GET /v1/products`(목록) + `GET /v1/products/{id}`(상세) 응답에 동봉. 전체 `ProductVersionResponse`(11+ 필드)가 아닌 운영자 카탈로그 표시에 필요한 핵심 필드만 추려 응답 크기를 줄인다. version · provider · action 셋은 형식과 길이 64자를 상품 버전 쪽과 같은 선언으로 잠근다 - 두 자리가 갈리면 한쪽만 고쳐진다.
      - `id` integer, required: api_product_versions.id.
      - `version` string, required: 버전 식별자(예: 'v1'/'v2'). 형식 `^[A-Za-z0-9._-]{1,20}$`.
      - `version_status` 'DRAFT' | 'PUBLISHED' | 'DEPRECATED' | 'RETIRED', required: 버전 상태 enum.
      - `provider` string, required: 등록된 기관 식별자. 형식 `^[a-z][a-z0-9_]{0,63}$`.
      - `action` string, required: 등록된 액션 식별자. 기관에 종속한다. 형식 `^[a-z][a-z0-9_.]{0,63}$`.
    - `monitoring_grade` integer, nullable
    - `website_slug` string, nullable
    - `metadata` object
    - `page_content_exists` boolean: True when an active product_page_content row exists for this product+env. Always filled by list enrichment (WebsiteExposable row badge input).
    - `mcp_tool_name` string, nullable: 이 상품의 활성 버전 (provider, action) 에 해당하는 MCP 도구 이름. null 이면 그 쌍에 도구 행이 없다(활성 버전이 없을 때도 null).
    - `mcp_tool_enabled` boolean: 그 도구가 켜져 있고 이 env_scope 에서 팔리는가. false 는 꺼졌거나 이 레인에서 팔지 않는다는 뜻이며 '준비 중' 을 뜻하지 않는다.
    - `website_featured_rank` integer, nullable: 웹사이트 상품 목록 맨 위 대표 카드의 순서(1 이 맨 앞). 어드민 「웹사이트 대표 상품」 이 정하고 이 env_scope 의 website_featured_products 에서 온다. null 이면 대표가 아니다.
    - `created_at` string, date-time, required
    - `updated_at` string, date-time, required
  - `next_cursor` string, nullable: Opaque cursor for next page, or null on last page.
  - `has_more` boolean: Whether another page exists. False when next_cursor is null.

## Other responses

- `422`: Validation Error

---

[API](https://skmtc.dev/xdata/apis/xboss-api.md) · [All operations](https://skmtc.dev/xdata/apis/xboss-api/llms.txt) · [OpenAPI document](https://skmtc.dev/xdata/apis/xboss-api/revisions/816de78950db?raw)
