---
title: "Update Product"
method: PATCH
path: "/v1/products/{product_id}"
tags: ["Product Catalog"]
---

# Update Product

`PATCH /v1/products/{product_id}`

## 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/{product_id}`
- 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`)

## Path parameters

- `product_id` integer, required

## Query parameters

- `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

- `Idempotency-Key` string, nullable
- `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 입니다.

## Request body

- ProductUpdateRequest: `PATCH /v1/products/{product_id}` 요청 본문. 모든 필드 선택. immutable 컬럼은 `extra='forbid'`로 본 DTO에서 차단: - `code` / `organization_id` / `env_scope` / `current_version_id` 는 본 DTO에 미선언. 클라이언트가 본문에 포함하면 `422 VALIDATION_SEMANTIC_ERROR` 다. - 상태 전이는 nested `StatusTransition` 으로 분리(단일 책임).
  - `name` string, nullable: 공백만 입력 금지.
  - `description` string, nullable: 카탈로그 설명. `ProductCreateRequest.description` 과 같은 정책이다 - 화면이 sanitize 를 맡고 최대 10000자.
  - `category` 'tax' | 'gov' | 'finance' | 'marketing', nullable
  - `visibility` 'PUBLIC' | 'PRIVATE' | 'ENTERPRISE', nullable
  - `monitoring_grade` integer, nullable: Ops monitoring grade 1..5 (api_products.monitoring_grade).
  - `website_slug` string, nullable: Public Website slug; null clears the column when explicitly set. 소문자 · 숫자 · 사이 하이픈만.
  - `collection_sites` string[], nullable: When set, replaces active product_collection_site rows for env_scope.
  - `metadata` object, nullable: 정책 키 진입 금지. JSON 직렬화 후 최대 65536 bytes.
  - `status_transition` StatusTransition: 상태 전이 본문 - `status_transition`. `PATCH /v1/products/{product_id}` 요청 본문에 nested로 동봉. 허용 전이는 상태 전이 매트릭스(`ALLOWED_TRANSITIONS`)에 따른다. 비허용 전이 시 라우트/서비스/DB 트리거 단계에서 `409 CATALOG_INVALID_TRANSITION`. 감사 이벤트: `product.catalog.transitioned`(from, to, reason).
    - `to` 'DRAFT' | 'ACTIVE' | 'DEPRECATED' | 'RETIRED', required: 목표 상태. 허용 전이(ALLOWED_TRANSITIONS)만 받는다.
    - `reason` string, required: 전이 사유. 감사로그에 적재된다.

## Response `200`

Successful Response

- ProductResponse: 상품 단일 객체 응답. SCHEMA → DTO 매핑: - `api_products.id` → `id`. - `code`/`name`/`description`/`category`/`status`/`visibility`/`organization_id`/ `env_scope`/`current_version_id`/`metadata`/`created_at`/`updated_at`/`deleted_at` → 직접 매핑. - `current_version` (nested) — `GET /v1/products/{id}` 상세 응답에서만 채운다. `POST /v1/products` 응답은 `initial_version` echo만 동봉한다. - `versions` (List) — `GET /v1/products/{id}?include_versions=true` 시만 채운다. lifecycle timestamp 3종(`activated_at`/`deprecated_at`/`retired_at`)은 본 응답에 미노출 (운영 추적성은 운영 API 영역). UI/콘솔이 필요하면 별도 결정 뒤 노출을 넓힌다.
  - `id` integer, required: api_products.id.
  - `code` string, required
  - `name` string, required
  - `description` string, nullable: 카탈로그 설명. `ProductCreateRequest.description` 과 같은 정책이다 - 화면이 sanitize 를 맡고 최대 10000자.
  - `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: 활성 버전 포인터. DRAFT 상태에서만 NULL 허용 — ACTIVE/DEPRECATED/RETIRED 상태에서는 반드시 있어야 한다.
  - `metadata` object: 자유 메타데이터. 정책을 결정하는 키는 넣을 수 없다. JSON 직렬화 후 최대 65536 bytes.
  - `monitoring_grade` integer, nullable: Ops monitoring grade 1..5.
  - `website_slug` string, nullable: Public Website slug for routing.
  - `collection_sites` string[]: Active collection site codes (product_collection_site SSOT).
  - `created_at` string, date-time, required
  - `updated_at` string, date-time, required
  - `deleted_at` string, date-time, nullable: soft delete 시각. NULL=활성, NOT NULL=삭제 완료.
  - `current_version` 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}$`.
  - `mcp_tool_name` string, nullable: 이 상품의 활성 버전 (provider, action) 에 해당하는 MCP 도구 이름. null 이면 그 쌍에 도구 행이 없다(활성 버전이 없을 때도 null).
  - `mcp_tool_enabled` boolean: 그 도구가 켜져 있고 이 env_scope 에서 팔리는가. false 는 꺼졌거나 이 레인에서 팔지 않는다는 뜻이며 '준비 중' 을 뜻하지 않는다.
  - `facts` ProductFacts: 상품 상세페이지 안내 블록이 읽는 **서버가 아는 사실**. 도구 계약에서 만들어 손으로 적지 않는다. 모르는 값은 null 로 둔다 - 그럴듯한 값으로 채우지 않는다. 특히 `irreversible` 은 조회면 false 이고 쓰기는 상품이 되돌릴 수 없음을 선언하기 전까지 null 이다(쓰기가 「기관 상태를 바꿀 수 있음」 이라는 것과 「되돌릴 수 없음」 은 다른 말이다).
    - `credential` ProductFactsCredential, required: 이 상품을 쓰기 전에 **연동해 둬야 하는 자격**.
      - `required` boolean, required: 연동해 둔 자격이 필요한가. false 면 연동 없이 바로 부를 수 있다.
      - `kind` 'certificate' | 'id_or_key' | 'bank_account', nullable, required: 자격의 종류: `certificate`(공동인증서), `bank_account`(은행 계좌 연동), `id_or_key`(아이디 · 키 · 계정 연결). 연동이 필요 없으면 null. 여러 기관이 섞이면 인증서 > 은행 계좌 > 아이디 · 키 순으로 하나만 말한다.
      - `windows_pc_needed` boolean, required: 인증서 연동에 Windows PC 와 연결 프로그램 설치가 한 번 필요한가.
      - `providers` string[], required: 연동해야 하는 기관의 슬러그 목록(중복 없음, 호출 순서). 연동이 필요 없으면 빈 목록.
    - `prerequisite_login` ProductFactsPrerequisiteLogin, required: 이 상품을 부르기 전에 먼저 불러야 하는 로그인.
      - `provider` string, required: 로그인해야 하는 기관의 슬러그.
      - `provider_label_ko` string, required: 그 기관의 한국어 이름(서버 표에서 유도한 값).
    - `effect` 'read' | 'draft' | 'submit', nullable, required: 기관에 하는 일: `read`(조회만), `draft`(초안을 덧붙일 뿐 기존 상태를 바꾸지 않음), `submit`(기관 상태를 바꿀 수 있음 - 접수 · 발급 · 저장 등). 로그인 · 세션 · 입력 서식 초기화 같은 단계처럼 분류하지 않은 도구는 null.
    - `irreversible` boolean, nullable, required: 되돌릴 수 없는가. 조회는 false. 쓰기는 null(알 수 없음) - `effect` 가 `submit` 이라는 것이 되돌릴 수 없다는 뜻이 아니다.
    - `confirm_required` boolean, required: 쓰기가 먼저 확인(미리보기 · 동의)을 거쳐야 끝나는가. 조회는 false.
    - `limits` ProductFactsLimits, required: 결과 파일에 관한 한도. 파일을 내주지 않는 상품은 둘 다 null.
      - `file_link_minutes` integer, nullable: 결과 파일 내려받기 링크가 열려 있는 분.
      - `file_retention_days` integer, nullable: 결과 파일을 보관하는 일수.
  - `initial_version` InitialVersionEcho: `POST /v1/products` 응답에 nested로 동봉되는 초기 버전 요약. 초기 버전을 동봉했을 때 응답 본문의 `initial_version` nested 가 이 모양이다. `version` · `provider` · `action` 은 상품 등록과 같은 꼴 · 같은 길이(최대 64자)로 잠근다.
    - `id` integer, required: api_product_versions.id.
    - `version` string, required
    - `version_status` 'DRAFT' | 'PUBLISHED' | 'DEPRECATED' | 'RETIRED', required
    - `provider` string, required
    - `action` string, required
    - `published_at` string, date-time, nullable: 발행 시각. publish_immediately=false 면 null.
  - `versions` CurrentVersionSummary[], nullable: GET /v1/products/{id}?include_versions=true 시만 채운다. 그 외 응답은 항상 null.
    - `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}$`.

## 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)
