---
title: "Get Plan Catalog Offer"
method: GET
path: "/v1/plans/{plan_id}/catalog-offer"
tags: ["Billing Plans"]
---

# Get Plan Catalog Offer

`GET /v1/plans/{plan_id}/catalog-offer`

## 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/plans/{plan_id}/catalog-offer`
- 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`)

Hybrid subscribe catalog snapshot.

## Path parameters

- `plan_id` integer, required

## Query parameters

- `exclude_owned_subscription_id` integer, nullable: When set, omit ACTIVE subscription_items product_ids from addon_offers (midlife Path B catalog; custom-plan W0 G2). Subscription must be ACTIVE and plan_id must match this catalog plan_id.
- `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

- PlanCatalogOfferResponse: `GET /v1/plans/{plan_id}/catalog-offer` Console subscribe catalog snapshot.
  - `plan` PlanResponse, required: 요금제 단일 객체 응답. SCHEMA → DTO 매핑: - `plans.id` → `plan_id` (외부 노출 식별자명 — 표준 `<resource>_id` 패턴). - `plans.code/name/description/tier/billing_cycle/status/env_scope/created_at` → 직접 매핑. - `plans.price_amount_minor + currency + tax_included` → nested `MoneyMinor` 객체 `price`. - `plans.metadata`/`updated_at`/`deleted_at`는 외부 노출 X(BFF/콘솔 미사용 + 정책 평가 키 보호). SCHEMA가 글로벌 카탈로그이므로 `organization_id` 필드 없음.
    - `plan_id` integer, required: plans.id (BIGSERIAL PK).
    - `code` string, required: 비즈 식별 코드. 환경 내 유니크 (SCHEMA uq_plans_code_env_active).
    - `name` string, required: 사용자 노출 명칭.
    - `description` string, nullable: 요금제 설명 (마크다운 허용).
    - `tier` 'FREE' | 'PAID', required: 요금제 등급. FREE/PAID.
    - `billing_cycle` 'ONCE' | 'MONTHLY' | 'YEARLY', required: 청구 주기. ONCE/MONTHLY/YEARLY.
    - `price` MoneyMinor, required: minor unit 정수 기반 금액 객체. SCHEMA 컬럼 `price_amount_minor BIGINT NOT NULL CHECK (>= 0)` + `currency CHAR(3) DEFAULT 'KRW'` + `tax_included BOOLEAN DEFAULT TRUE`를 API 응답의 nested `price` 객체로 묶는다. - `amount_minor`: 1회 청구/결제 금액의 minor unit 정수(KRW는 원 단위, JPY는 엔 단위). 절대 float/Decimal 사용 금지(부동소수 오차 + JSON 직렬화 일관성). - `currency`: v1 단일 KRW. 통화 확장은 별도 ADR. - `tax_included`: 부가세 포함 여부. NULL 금지(SCHEMA NOT NULL DEFAULT TRUE 정합).
      - `amount_minor` integer, required: minor unit 정수 금액. KRW는 원 단위. 음수 금지 (SCHEMA CHECK 정합).
      - `currency` 'KRW', required: 통화 코드 (v1=KRW). 세 글자여야 한다.
      - `tax_included` boolean, required: 부가세 포함 여부. SCHEMA NOT NULL.
    - `status` 'DRAFT' | 'ACTIVE' | 'DEPRECATED' | 'RETIRED', required: 요금제 상태.
    - `env_scope` 'sandbox' | 'real_test' | 'production', required: 환경 스코프.
    - `created_at` string, date-time, required: 발행 시각 (TIMESTAMPTZ, ISO 8601 + offset 포함; naive 입력 시 422).
    - `addon_select_min` integer, nullable: W6/G7: min Path B pack count at subscribe (NULL=unlimited).
    - `addon_select_max` integer, nullable: W6/G7: max Path B pack count (NULL=unlimited).
    - `audience` 'standard' | 'integrator': 기본값 standard · 연동사 요금제는 integrator.
    - `max_active_account_links` integer, nullable: 연동사 요금제의 좌석 상한 - 한 desktop_agent 클라이언트가 가질 수 있는 ACTIVE account_links 최대 수. audience='integrator' 일 때만 뜻이 있고 NULL 은 제한 없음(또는 해당 없음)이다.
    - `included_usage_free_units` integer, nullable: 요금제 전체의 종량 무료 허용량(한 청구 기간 기준) - (sum of all data_provider/action usage_records quantity, not per action). NULL or 0 = no free allowance (billed from unit 1, current behavior).
    - `daily_token_limit` integer, nullable: T12: daily usage-token allowance this plan grants, read from the linked quota_policies row (plans.quota_policy_id). The unit is usage tokens, not call count. null means THIS PLAN HAS NO DAILY CAP from a quota policy - do not read it as not-configured-yet. It covers four cases that are deliberately not distinguished: no policy linked, a linked policy whose own daily_limit is null, a linked policy that was soft-deleted, and the admin create/update responses (those are built from RETURNING, which this repository keeps narrow - read the plan again to get the limits).
    - `monthly_token_limit` integer, nullable: T12: monthly usage-token allowance from the same quota_policies row. NULL = no monthly limit for this plan (the user instruction gives a monthly number for the paid plans only). The unit is usage tokens.
    - `contact_sales` boolean: 상담으로 가입하는 요금제(엔터프라이즈)이면 true. 고객이 스스로 가입하는 길(0원 가입 · 결제 가입)에 서지 않는다. 입력값을 받지 않고 `code` 에서 유도한다(`CONTACT_SALES_PLAN_CODES`).
  - `env_scope` 'sandbox' | 'real_test' | 'production', required
  - `included_products` PlanCatalogIncludedProductItem[]
    - `plan_product_mapping_id` integer, required: plan_products.id.
    - `product_id` integer, required
    - `product_code` string, required
    - `product_name` string, required
    - `is_default` boolean, required: Default BASE mapping flag (plan_products.is_default).
  - `addon_offers` PlanCatalogAddonOfferItem[]
    - `plan_addon_offer_id` integer, required
    - `product_id` integer, required
    - `product_code` string, required
    - `product_name` string, required
    - `product_description` string, nullable: api_products.description projection for Console checkout copy. NULL when the catalog product has no description.
    - `is_mcp_pack` boolean, required: True when api_products.metadata marks an MCP package sale product (seed=mcp_package_addon_sale and/or mcp_package_id). Console may show a small MCP-pack label; not an entitlement SSOT.
    - `recurring_amount_minor` integer, required
    - `currency` string, required
    - `billing_cycle` 'MONTHLY' | 'YEARLY', required
  - `usage_unit_prices` PlanCatalogUsageUnitPriceItem[]
    - `data_provider` string, required
    - `action` string, required
    - `unit_amount_minor` integer, required
    - `currency` string, required

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