# API 참조

> 계정 API 키로 부르는 문, 인증 없는 공개 조회, 앱 배포 피드, design 호스트 API 의 경로·자격·능력·입력 칸.

이 쪽의 표는 서버의 라우터와 컨트롤러 선언에서 만든 생성 문서를 그대로 싣습니다. 손으로 옮겨 적은 값이 없으므로 서버와 어긋나지 않습니다. 설명은 한국어와 영어를 함께 싣습니다.

- 같은 내용의 기계용 사본: [`/openapi.json`](https://developers.gujo.ai/openapi.json)(OpenAPI 3.1)
- 인증과 오류 코드의 뜻: [인증과 API 키](auth.md)
- MCP 도구 이름은 아래 `tool` 칸과 같습니다: [MCP 연결하기](mcp/index.md)

구매자 기기·gujoctl·계정 MCP 가 계정 API 키로 부르는 문과, 인증 없는 공개 조회, app 호스트의 앱 배포 피드, design 호스트의
에이전트 API 를 라우터에서 읽어 만든다. 메서드·경로·호스트·자격·능력·입력 칸은 라우트와 검증 규칙에서, 설명과 응답 Data 는
컨트롤러의 `#[PublicApiSpec]` 에서 온다. 오류 의미·멱등성·버전 규칙처럼 라우트에서 읽을 수 없는 약속은 `docs/contracts.md` 에
있다. 같은 내용의 기계용 사본은 `apps/platform-gujo-core/resources/developers/openapi.json`(OpenAPI 3.1)이다.

- `auth`: `account_api_key` Bearer 계정 API 키, `optional_account_api_key` 키 없이 열고 키가 오면 그 키로 판정,
  `design_agent_token` design 에이전트 Bearer 토큰(괄호 안이 필요한 역할), `design_gallery` design 갤러리 문(지금은 로그인 벽 없음), `none` 인증 없음.
- `ability`: 키에 있어야 하는 능력(`account-api-key.ability:`). 비어 있으면 키만 있으면 된다. 없으면 403 `missing_api_key_ability`.
- `requires`: `product_entitlement` 그 상품의 이용권(없으면 403 `product_entitlement_required`), `product_scoped_key` 그 상품에
  묶인 키(없으면 403 `product_api_key_scope_required`), `product_locale` 상품 언어로 응답.
- `response`: 성공 본문의 Data. `—` 는 컨트롤러가 배열로 직접 만들어 형식을 선언하지 않은 JSON 응답, 괄호는 JSON 이 아닌 응답이다.

## 호스트

| host | URL |
| --- | --- |
| `api` | https://api.gujo.ai |
| `design` | https://design.gujo.ai |
| `app` | https://app.gujo.ai |

## 묶음

| group | 이름 | 문 |
| --- | --- | --- |
| `library` | 라이브러리 | 5 |
| `products` | 상품 | 5 |
| `install` | 설치 | 1 |
| `license` | 라이선스·패스 | 4 |
| `setup` | 설정 진행 | 2 |
| `device` | 기기 | 6 |
| `orders` | 주문·청구 | 4 |
| `subscriptions` | 구독 | 1 |
| `client_config` | 클라이언트 설정 | 1 |
| `support` | 신고 | 2 |
| `mcp` | 계정 MCP | 1 |
| `distribution` | 앱 배포 피드(app 호스트) | 2 |
| `downloads` | 다운로드 공급 현황 | 1 |
| `public` | 공개 조회 | 3 |
| `design` | design 호스트 | 19 |

## 라이브러리 (`library`)

| tool | method path | host | auth | ability | requires | response |
| --- | --- | --- | --- | --- | --- | --- |
| [`library`](#library) | `GET /api/library` | `api` | `account_api_key` | `products.read` |  | `LibraryApiData` |
| [`product_content_css`](#productcontentcss) | `GET /api/product-content.css` | `api` | `account_api_key` | `products.read` |  | (css) |
| [`products_bundle`](#productsbundle) | `GET /api/products/{product}/bundle` | `api` | `account_api_key` | `products.read` | `product_entitlement` | (binary) |
| [`products_content_assets_list`](#productscontentassetslist) | `GET /api/products/{product}/content-assets` | `api` | `account_api_key` | `products.read` | `product_entitlement` | — |
| [`products_content_assets`](#productscontentassets) | `GET /api/products/{product}/content-assets/{path}` | `api` | `account_api_key` | `products.read` | `product_entitlement` | (binary) |

### `library`

`GET https://api.gujo.ai/api/library` · route `api.library`

이 키로 쓸 수 있는 상품 목록. 배달 매니페스트·설정 진행·MCP 서버·번들 정보를 함께 준다. 상품 하나에 묶인 키면 그 상품만 준다

Products this key can use, with delivery manifest, setup progress, MCP server and bundle info. A product-scoped key sees only its product

- auth: `account_api_key`, ability `products.read`
- response: `200 application/json` `LibraryApiData`

### `product_content_css`

`GET https://api.gujo.ai/api/product-content.css` · route `api.product-content.css`

상품 콘텐츠 HTML 에 쓰는 스타일시트

Stylesheet for product content HTML

- auth: `account_api_key`, ability `products.read`
- response: `200 text/css`

### `products_bundle`

`GET https://api.gujo.ai/api/products/{product}/bundle` · route `api.products.bundle`

이용권이 있는 상품의 현재 번들 파일. 업데이트 기간이 끝난 구매자는 403 version_update_window_expired

Current bundle file of an entitled product. A buyer past the update window gets 403 version_update_window_expired

- auth: `account_api_key`, ability `products.read`
- requires: `product_entitlement`
- response: `200 application/octet-stream`

| field | in | type | required |
| --- | --- | --- | --- |
| `product` | path | `integer` | yes |

### `products_content_assets_list`

`GET https://api.gujo.ai/api/products/{product}/content-assets` · route `api.products.content-assets.index`

이용권이 있는 상품의 콘텐츠 자산 목록

Content assets of an entitled product

- auth: `account_api_key`, ability `products.read`
- requires: `product_entitlement`
- response: `200 application/json` —

| field | in | type | required |
| --- | --- | --- | --- |
| `product` | path | `integer` | yes |

### `products_content_assets`

`GET https://api.gujo.ai/api/products/{product}/content-assets/{path}` · route `api.products.content-assets`

이용권이 있는 상품의 콘텐츠 자산 파일 하나

One content asset file of an entitled product

- auth: `account_api_key`, ability `products.read`
- requires: `product_entitlement`
- response: `200 application/octet-stream`

| field | in | type | required |
| --- | --- | --- | --- |
| `product` | path | `integer` | yes |
| `path` | path | `string` | yes |

## 상품 (`products`)

| tool | method path | host | auth | ability | requires | response |
| --- | --- | --- | --- | --- | --- | --- |
| [`products_list`](#productslist) | `GET /api/products` | `api` | `account_api_key` | `products.read` |  | `object{products: array<ProductApiData>}` |
| [`products_show`](#productsshow) | `GET /api/products/{product}` | `api` | `account_api_key` | `products.read` | `product_locale` | `object{product: ProductApiData}` |
| [`products_detail`](#productsdetail) | `GET /api/products/{product}/detail` | `api` | `account_api_key` | `products.read` | `product_locale`, `product_entitlement` | `ProductDetailApiData` |
| [`products_releases`](#productsreleases) | `GET /api/products/{product}/releases` | `api` | `account_api_key` |  |  | `object{releases: array<ProductReleaseData>}` |
| [`products_updates`](#productsupdates) | `GET /api/products/{product}/updates` | `api` | `account_api_key` | `products.read` | `product_entitlement` | `object{update: ProductUpdateApiData}` |

### `products_list`

`GET https://api.gujo.ai/api/products` · route `api.products.index`

판매 중인 상품 목록. 상품마다 이 계정의 이용권 여부(entitled)를 단다

Active products, each marked with whether this account is entitled

- auth: `account_api_key`, ability `products.read`
- response: `200 application/json` `object{products: array<ProductApiData>}`

| field | in | type | required |
| --- | --- | --- | --- |
| `category_id` | query | `integer` |  |
| `tag_id` | query | `integer` |  |
| `q` | query | `string` |  |
| `platform` | query | `string` |  |
| `sort` | query | `string` |  |

입력 칸은 검증 규칙이 없어 선언(`query`·`body`)에서 왔다. 모두 선택 입력이다.

### `products_show`

`GET https://api.gujo.ai/api/products/{product}` · route `api.products.show`

상품 하나. 게시되지 않은 앱 상품은 접근할 수 있는 계정에만 보인다

One product. An unpublished app product is visible only to accounts that can access it

- auth: `account_api_key`, ability `products.read`
- requires: `product_locale`
- response: `200 application/json` `object{product: ProductApiData}`

| field | in | type | required |
| --- | --- | --- | --- |
| `product` | path | `integer` | yes |

### `products_detail`

`GET https://api.gujo.ai/api/products/{product}/detail` · route `api.products.detail`

이용권이 있는 상품의 상세(본문 HTML·매뉴얼·배달 정보·미디어)

Detail of an entitled product (body HTML, manual, delivery, media)

- auth: `account_api_key`, ability `products.read`
- requires: `product_locale`, `product_entitlement`
- response: `200 application/json` `ProductDetailApiData`

| field | in | type | required |
| --- | --- | --- | --- |
| `product` | path | `integer` | yes |

### `products_releases`

`GET https://api.gujo.ai/api/products/{product}/releases` · route `api.products.releases`

상품의 릴리스 목록

Releases of a product

- auth: `account_api_key`
- response: `200 application/json` `object{releases: array<ProductReleaseData>}`

| field | in | type | required |
| --- | --- | --- | --- |
| `product` | path | `integer` | yes |

### `products_updates`

`GET https://api.gujo.ai/api/products/{product}/updates` · route `api.products.updates`

업데이트 확인. 클라이언트 버전(version)과 현재 버전을 비교하고 내려받을 수 있는지 알린다

Update check: compares the client version with the current one and says whether the download is allowed

- auth: `account_api_key`, ability `products.read`
- requires: `product_entitlement`
- response: `200 application/json` `object{update: ProductUpdateApiData}`

| field | in | type | required |
| --- | --- | --- | --- |
| `product` | path | `integer` | yes |
| `version` | query | `string` |  |

입력 칸은 검증 규칙이 없어 선언(`query`·`body`)에서 왔다. 모두 선택 입력이다.

## 설치 (`install`)

| tool | method path | host | auth | ability | requires | response |
| --- | --- | --- | --- | --- | --- | --- |
| [`products_install`](#productsinstall) | `GET /api/products/{product}/install` | `api` | `account_api_key` | `products.read` | `product_entitlement` | `object{data: ProductInstallData}` |

### `products_install`

`GET https://api.gujo.ai/api/products/{product}/install` · route `api.products.install`

설치 계획(서명 다운로드 주소와 sha256). 설치할 것이 없으면 404 install_not_available

Install plan (signed download URL and sha256). 404 install_not_available when there is nothing to install

- auth: `account_api_key`, ability `products.read`
- requires: `product_entitlement`
- response: `200 application/json` `object{data: ProductInstallData}`

| field | in | type | required |
| --- | --- | --- | --- |
| `product` | path | `integer` | yes |
| `channel` | query | `enum(stable, beta, alpha, dev)\|null` |  |

## 라이선스·패스 (`license`)

| tool | method path | host | auth | ability | requires | response |
| --- | --- | --- | --- | --- | --- | --- |
| [`license_check`](#licensecheck) | `POST /api/license/check` | `api` | `account_api_key` | `device.entitlements` |  | — |
| [`products_license`](#productslicense) | `GET /api/products/{product}/license` | `api` | `account_api_key` | `products.read` | `product_entitlement` | — |
| [`products_license_device_revoke`](#productslicensedevicerevoke) | `POST /api/products/{product}/license/device/revoke` | `api` | `account_api_key` | `products.read` | `product_scoped_key` | — |
| [`entitlements_pass_check`](#entitlementspasscheck) | `GET /api/v1/entitlements/pass-check` | `app` | `account_api_key` | `device.entitlements` |  | `PassCheckData` |

### `license_check`

`POST https://api.gujo.ai/api/license/check` · route `api.license.check`

라이선스 판정(product_id 나 bundle_id). 기기 상한을 넘으면 409 와 기기 목록·관리 주소

License decision by product_id or bundle_id. Over the device limit: 409 with the device list and manage URL

- auth: `account_api_key`, ability `device.entitlements`
- response: `200 application/json` —

| field | in | type | required |
| --- | --- | --- | --- |
| `product_id` | body | `integer\|null` |  |
| `bundle_id` | body | `string\|null` |  |
| `device_id` | body | `string\|null` |  |
| `app_version` | body | `string\|null` |  |

### `products_license`

`GET https://api.gujo.ai/api/products/{product}/license` · route `api.products.license`

상품 이용권 확인. 이용권이 없으면 미들웨어가 403 을 낸다

Product entitlement check. Without an entitlement the middleware answers 403

- auth: `account_api_key`, ability `products.read`
- requires: `product_entitlement`
- response: `200 application/json` —

| field | in | type | required |
| --- | --- | --- | --- |
| `product` | path | `integer` | yes |

### `products_license_device_revoke`

`POST https://api.gujo.ai/api/products/{product}/license/device/revoke` · route `api.products.license.device.revoke`

상품 하나에 묶인 이 기기 키를 해제한다

Revokes this product-scoped device key

- auth: `account_api_key`, ability `products.read`
- requires: `product_scoped_key`
- response: `200 application/json` —

| field | in | type | required |
| --- | --- | --- | --- |
| `product` | path | `integer` | yes |

### `entitlements_pass_check`

`GET https://app.gujo.ai/api/v1/entitlements/pass-check` · route `app.api.v1.entitlements.pass-check`

올액세스 패스 확인(데스크톱 앱)

All-access pass check (desktop app)

- auth: `account_api_key`, ability `device.entitlements`
- response: `200 application/json` `PassCheckData`

## 설정 진행 (`setup`)

| tool | method path | host | auth | ability | requires | response |
| --- | --- | --- | --- | --- | --- | --- |
| [`products_setup_progress_show`](#productssetupprogressshow) | `GET /api/products/{product}/setup-progress` | `api` | `account_api_key` | `products.read` | `product_entitlement` | `ProductSetupProgressData` |
| [`products_setup_progress_update`](#productssetupprogressupdate) | `PUT /api/products/{product}/setup-progress` | `api` | `account_api_key` | `setup-progress.write` | `product_entitlement` | `ProductSetupProgressData` |

### `products_setup_progress_show`

`GET https://api.gujo.ai/api/products/{product}/setup-progress` · route `api.products.setup-progress.show`

상품 설정 진행 상태

Setup progress of a product

- auth: `account_api_key`, ability `products.read`
- requires: `product_entitlement`
- response: `200 application/json` `ProductSetupProgressData`

| field | in | type | required |
| --- | --- | --- | --- |
| `product` | path | `integer` | yes |

### `products_setup_progress_update`

`PUT https://api.gujo.ai/api/products/{product}/setup-progress` · route `api.products.setup-progress.update`

상품 설정 진행 상태를 저장한다(끝낸 단계 번호 목록)

Saves setup progress (completed step indexes)

- auth: `account_api_key`, ability `setup-progress.write`
- requires: `product_entitlement`
- response: `200 application/json` `ProductSetupProgressData`

| field | in | type | required |
| --- | --- | --- | --- |
| `product` | path | `integer` | yes |
| `completed_step_indexes` | body | `array<integer>` |  |

## 기기 (`device`)

| tool | method path | host | auth | ability | requires | response |
| --- | --- | --- | --- | --- | --- | --- |
| [`bundles_list`](#bundleslist) | `GET /api/bundles` | `api` | `account_api_key` | `device.entitlements` |  | — |
| [`cli_device_logout`](#clidevicelogout) | `POST /api/cli/device/logout` | `api` | `account_api_key` |  |  | (no_content) |
| [`device_bulk_activate`](#devicebulkactivate) | `POST /api/device/bulk-activate` | `api` | `account_api_key` | `device.activate` |  | — |
| [`device_entitlements`](#deviceentitlements) | `GET /api/device/entitlements` | `api` | `account_api_key` | `device.entitlements` |  | `DeviceEntitlementsData` |
| [`device_installs_create`](#deviceinstallscreate) | `POST /api/device/installs` | `api` | `account_api_key` | `device.install` |  | — |
| [`device_usage_create`](#deviceusagecreate) | `POST /api/device/usage` | `api` | `account_api_key` | `device.usage` |  | — |

### `bundles_list`

`GET https://api.gujo.ai/api/bundles` · route `api.bundles.index`

상품 묶음(번들) 목록

Product bundles

- auth: `account_api_key`, ability `device.entitlements`
- response: `200 application/json` —

### `cli_device_logout`

`POST https://api.gujo.ai/api/cli/device/logout` · route `api.cli.device.logout`

부른 키 자신을 서버에서 폐기한다

Revokes the calling key on the server

- auth: `account_api_key`
- response: `204`

### `device_bulk_activate`

`POST https://api.gujo.ai/api/device/bulk-activate` · route `api.device.bulk-activate`

쓸 수 있는 상품 전부의 기기 키를 한 번에 만든다

Creates device keys for every accessible product at once

- auth: `account_api_key`, ability `device.activate`
- response: `200 application/json` —

### `device_entitlements`

`GET https://api.gujo.ai/api/device/entitlements` · route `api.device.entitlements`

기기 시작 때 부르는 전체 이용권(상품·구독·번들·기기·사용자)

Everything a device needs at launch: products, subscriptions, bundles, device and user

- auth: `account_api_key`, ability `device.entitlements`
- response: `200 application/json` `DeviceEntitlementsData`

### `device_installs_create`

`POST https://api.gujo.ai/api/device/installs` · route `api.device.installs.store`

설치 기록을 남긴다

Records an install

- auth: `account_api_key`, ability `device.install`
- response: `201 application/json` —

| field | in | type | required |
| --- | --- | --- | --- |
| `product_id` | body | `integer` | yes |
| `version` | body | `string` | yes |
| `bundle_id` | body | `string\|null` |  |
| `installed_at` | body | `string\|null` |  |

### `device_usage_create`

`POST https://api.gujo.ai/api/device/usage` · route `api.device.usage.store`

제품 사용량 일별 업로드. (계정, 상품, 기기 키, 날짜) 한 행을 덮어쓴다

Daily product usage upload; overwrites one row per (account, product, device key, date)

- auth: `account_api_key`, ability `device.usage`
- response: `200 application/json` —

| field | in | type | required |
| --- | --- | --- | --- |
| `consent` | body | `boolean` | yes |
| `days` | body | `array<object{product_id: integer, usage_date: string(date), launches: integer, commands: object}>` | yes |

## 주문·청구 (`orders`)

| tool | method path | host | auth | ability | requires | response |
| --- | --- | --- | --- | --- | --- | --- |
| [`invoices_list`](#invoiceslist) | `GET /api/invoices` | `api` | `account_api_key` | `orders.read` |  | — |
| [`orders_list`](#orderslist) | `GET /api/orders` | `api` | `account_api_key` | `orders.read` |  | `OrdersData` |
| [`orders_show`](#ordersshow) | `GET /api/orders/{order}` | `api` | `account_api_key` | `orders.read` |  | `OrderShowData` |
| [`payment_methods_list`](#paymentmethodslist) | `GET /api/payment-methods` | `api` | `account_api_key` | `orders.read` |  | — |

### `invoices_list`

`GET https://api.gujo.ai/api/invoices` · route `api.invoices.index`

내 청구서 목록(커서: limit·after, 상태 필터 status)

My invoices (cursor: limit, after; status filter)

- auth: `account_api_key`, ability `orders.read`
- response: `200 application/json` —

| field | in | type | required |
| --- | --- | --- | --- |
| `status` | query | `string` |  |
| `limit` | query | `integer` |  |
| `after` | query | `integer` |  |

입력 칸은 검증 규칙이 없어 선언(`query`·`body`)에서 왔다. 모두 선택 입력이다.

### `orders_list`

`GET https://api.gujo.ai/api/orders` · route `api.orders.index`

내 주문 목록

My orders

- auth: `account_api_key`, ability `orders.read`
- response: `200 application/json` `OrdersData`

### `orders_show`

`GET https://api.gujo.ai/api/orders/{order}` · route `api.orders.show`

내 주문 하나. 남의 주문은 404 order_not_found

One of my orders. Someone else's order is 404 order_not_found

- auth: `account_api_key`, ability `orders.read`
- response: `200 application/json` `OrderShowData`

| field | in | type | required |
| --- | --- | --- | --- |
| `order` | path | `integer` | yes |

### `payment_methods_list`

`GET https://api.gujo.ai/api/payment-methods` · route `api.payment-methods.index`

내 결제 수단 목록(가린 카드 번호)

My payment methods (masked card numbers)

- auth: `account_api_key`, ability `orders.read`
- response: `200 application/json` —

## 구독 (`subscriptions`)

| tool | method path | host | auth | ability | requires | response |
| --- | --- | --- | --- | --- | --- | --- |
| [`subscriptions_list`](#subscriptionslist) | `GET /api/subscriptions` | `api` | `account_api_key` | `subscriptions.read` |  | `SubscriptionsData` |

### `subscriptions_list`

`GET https://api.gujo.ai/api/subscriptions` · route `api.subscriptions.index`

내 구독 목록

My subscriptions

- auth: `account_api_key`, ability `subscriptions.read`
- response: `200 application/json` `SubscriptionsData`

## 클라이언트 설정 (`client_config`)

| tool | method path | host | auth | ability | requires | response |
| --- | --- | --- | --- | --- | --- | --- |
| [`client_config`](#clientconfig) | `GET /api/client-config` | `api` | `account_api_key` |  |  | `ClientConfigData` |

### `client_config`

`GET https://api.gujo.ai/api/client-config` · route `api.client-config`

클라이언트 정책(최소 버전·재확인 주기·폐기 목록, 서명 포함). 헤더 없는 요청은 401

Signed client policy (minimum version, recheck interval, revocations). Requests without a key are 401

- auth: `account_api_key`
- response: `200 application/json` `ClientConfigData`

## 신고 (`support`)

| tool | method path | host | auth | ability | requires | response |
| --- | --- | --- | --- | --- | --- | --- |
| [`error_reports_create`](#errorreportscreate) | `POST /api/error-reports` | `api` | `none` |  |  | — |
| [`support_reports_create`](#supportreportscreate) | `POST /api/support/reports` | `api` | `account_api_key` | `support.report` |  | — |

### `error_reports_create`

`POST https://api.gujo.ai/api/error-reports` · route `api.error-reports.store`

클라이언트 충돌·예외 보고(인증 없음, 분당 5회)

Client crash or exception report (no auth, 5 per minute)

- auth: `none`
- response: `201 application/json` —

| field | in | type | required |
| --- | --- | --- | --- |
| `app_bundle_id` | body | `string` | yes |
| `app_version` | body | `string` | yes |
| `os_version` | body | `string\|null` |  |
| `device_model` | body | `string\|null` |  |
| `error_type` | body | `enum(crash, exception, warning)` | yes |
| `error_message` | body | `string` | yes |
| `stack_trace` | body | `string\|null` |  |
| `context` | body | `object\|null` |  |

### `support_reports_create`

`POST https://api.gujo.ai/api/support/reports` · route `api.support.reports.store`

앱 문제 신고 접수. 24시간 안의 같은 신고는 같은 티켓에 붙는다(200 dedup)

App problem report. The same report within 24 hours joins the same ticket (200 dedup)

- auth: `account_api_key`, ability `support.report`
- response: `201 application/json` —

| field | in | type | required |
| --- | --- | --- | --- |
| `kind` | body | `enum(general, app_report, refund_request)` | yes |
| `summary` | body | `string` | yes |
| `detail` | body | `string` | yes |
| `app_bundle_id` | body | `string\|null` |  |
| `app_version` | body | `string\|null` |  |
| `macos_version` | body | `string\|null` |  |
| `device_id` | body | `string\|null` |  |
| `bundle` | body | `string(binary)\|null` |  |

## 계정 MCP (`mcp`)

| tool | method path | host | auth | ability | requires | response |
| --- | --- | --- | --- | --- | --- | --- |
| [`mcp`](#mcp) | `POST /api/mcp` | `api` | `account_api_key` | `mcp.read` |  | — |

### `mcp`

`POST https://api.gujo.ai/api/mcp` · route `api.mcp`

계정 MCP(JSON-RPC 2.0). 도구는 이 키로 부를 수 있는 계정 API 다. initialize 응답 머리글 Mcp-Session-Id, 알림이면 202 빈 본문

Account MCP (JSON-RPC 2.0). Tools are the account API this key can call. initialize returns an Mcp-Session-Id header; notifications get 202 with no body

- auth: `account_api_key`, ability `mcp.read`
- response: `200 application/json` —

| field | in | type | required |
| --- | --- | --- | --- |
| `jsonrpc` | body | `string` |  |
| `id` | body | `string` |  |
| `method` | body | `string` |  |
| `params` | body | `object` |  |

입력 칸은 검증 규칙이 없어 선언(`query`·`body`)에서 왔다. 모두 선택 입력이다.

## 앱 배포 피드(app 호스트) (`distribution`)

| tool | method path | host | auth | ability | requires | response |
| --- | --- | --- | --- | --- | --- | --- |
| [`distribution_appcast`](#distributionappcast) | `GET /distribution/v1/products/{id}/appcast.xml` | `app` | `optional_account_api_key` |  |  | (rss) |
| [`distribution_download`](#distributiondownload) | `GET /distribution/v1/releases/{release}/download` | `app` | `optional_account_api_key` |  |  | (redirect) |

### `distribution_appcast`

`GET https://app.gujo.ai/distribution/v1/products/{id}/appcast.xml` · route `app.distribution.appcast`

Sparkle 2 appcast 피드. channel 이 없으면 모든 채널의 통합 피드다. 기기 키가 있으면 그 키로 접근을 판정한다

Sparkle 2 appcast feed. Without channel it is the combined feed of all channels. A device key widens access

- auth: `optional_account_api_key`
- response: `200 application/rss+xml`

| field | in | type | required |
| --- | --- | --- | --- |
| `id` | path | `integer` | yes |
| `channel` | query | `string` |  |

입력 칸은 검증 규칙이 없어 선언(`query`·`body`)에서 왔다. 모두 선택 입력이다.

### `distribution_download`

`GET https://app.gujo.ai/distribution/v1/releases/{release}/download` · route `app.distribution.download`

릴리스 다운로드. 판정을 통과하면 짧은 수명의 서명 주소로 302

Release download: 302 to a short-lived signed URL when access is allowed

- auth: `optional_account_api_key`
- response: `302`

| field | in | type | required |
| --- | --- | --- | --- |
| `release` | path | `integer` | yes |

## 다운로드 공급 현황 (`downloads`)

| tool | method path | host | auth | ability | requires | response |
| --- | --- | --- | --- | --- | --- | --- |
| [`downloads_pipeline`](#downloadspipeline) | `GET /api/downloads/pipeline` | `api` | `none` |  |  | — |

### `downloads_pipeline`

`GET https://api.gujo.ai/api/downloads/pipeline` · route `api.downloads.pipeline`

다운로드 공급 현황(채널별 catalog·platforms·checklist)

Download supply status (catalog, platforms and checklist per channel)

- auth: `none`
- response: `200 application/json` —

## 공개 조회 (`public`)

| tool | method path | host | auth | ability | requires | response |
| --- | --- | --- | --- | --- | --- | --- |
| [`lineages_list`](#lineageslist) | `GET /api/lineages` | `api` | `none` |  |  | — |
| [`lineages_show`](#lineagesshow) | `GET /api/lineages/{slug}` | `api` | `none` |  |  | — |
| [`public_disclosure`](#publicdisclosure) | `GET /api/public/disclosure` | `api` | `none` |  |  | `PublicDisclosureData` |

### `lineages_list`

`GET https://api.gujo.ai/api/lineages` · route `api.lineages.index`

활성 계보 목록

Active lineages

- auth: `none`
- response: `200 application/json` —

### `lineages_show`

`GET https://api.gujo.ai/api/lineages/{slug}` · route `api.lineages.show`

계보 하나와 그 상품

One lineage and its products

- auth: `none`
- response: `200 application/json` —

| field | in | type | required |
| --- | --- | --- | --- |
| `slug` | path | `string` | yes |

### `public_disclosure`

`GET https://api.gujo.ai/api/public/disclosure` · route `api.public.disclosure`

사업자 표기(언어별 회사 이름·연락처)

Business disclosure (company name and contacts per language)

- auth: `none`
- response: `200 application/json` `PublicDisclosureData`

## design 호스트 (`design`)

| tool | method path | host | auth | ability | requires | response |
| --- | --- | --- | --- | --- | --- | --- |
| [`design_agent_collect_runs_open`](#designagentcollectrunsopen) | `POST /api/agent/collect/runs` | `design` | `design_agent_token` (collect) |  |  | — |
| [`design_agent_collect_runs_show`](#designagentcollectrunsshow) | `GET /api/agent/collect/runs/{run}` | `design` | `design_agent_token` (collect) |  |  | — |
| [`design_agent_collect_runs_assets`](#designagentcollectrunsassets) | `POST /api/agent/collect/runs/{run}/assets` | `design` | `design_agent_token` (collect) |  |  | — |
| [`design_agent_collect_runs_captures`](#designagentcollectrunscaptures) | `POST /api/agent/collect/runs/{run}/captures` | `design` | `design_agent_token` (collect) |  |  | — |
| [`design_agent_collect_runs_fail`](#designagentcollectrunsfail) | `POST /api/agent/collect/runs/{run}/fail` | `design` | `design_agent_token` (collect) |  |  | — |
| [`design_agent_collect_runs_submit`](#designagentcollectrunssubmit) | `POST /api/agent/collect/runs/{run}/submit` | `design` | `design_agent_token` (collect) |  |  | — |
| [`design_agent_docs_show`](#designagentdocsshow) | `GET /api/agent/docs/{slug}` | `design` | `design_agent_token` (read) |  |  | — |
| [`design_agent_docs_versions_list`](#designagentdocsversionslist) | `GET /api/agent/docs/{slug}/versions` | `design` | `design_agent_token` (read) |  |  | — |
| [`design_agent_docs_versions_create`](#designagentdocsversionscreate) | `POST /api/agent/docs/{slug}/versions` | `design` | `design_agent_token` (write) |  |  | — |
| [`design_agent_docs_versions_submit`](#designagentdocsversionssubmit) | `POST /api/agent/docs/{slug}/versions/{number}/submit` | `design` | `design_agent_token` (write) |  |  | — |
| [`design_agent_manifest`](#designagentmanifest) | `GET /api/agent/manifest` | `design` | `design_agent_token` (read) |  |  | — |
| [`design_agent_reviews_create`](#designagentreviewscreate) | `POST /api/agent/reviews` | `design` | `design_agent_token` (review) |  |  | — |
| [`design_agent_reviews_pending`](#designagentreviewspending) | `GET /api/agent/reviews/pending` | `design` | `design_agent_token` (review) |  |  | — |
| [`design_apps_show`](#designappsshow) | `GET /api/v1/apps/{id}` | `design` | `design_gallery` |  |  | — |
| [`design_patterns`](#designpatterns) | `GET /api/v1/patterns` | `design` | `design_gallery` |  |  | — |
| [`design_screens`](#designscreens) | `GET /api/v1/screens` | `design` | `design_gallery` |  |  | — |
| [`design_screens_show`](#designscreensshow) | `GET /api/v1/screens/{id}` | `design` | `design_gallery` |  |  | — |
| [`design_taxonomy`](#designtaxonomy) | `GET /api/v1/taxonomy` | `design` | `design_gallery` |  |  | — |
| [`design_mcp_call`](#designmcpcall) | `POST /mcp` | `design` | `design_agent_token` (read) |  |  | — |

### `design_agent_collect_runs_open`

`POST https://design.gujo.ai/api/agent/collect/runs` · route `design.api.agent.collect.runs.open`

원격 수집 실행을 연다. 다른 실행이 수집 중이면 409

Opens a remote collect run. 409 while another run holds the lock

- auth: `design_agent_token` (collect)
- response: `201 application/json` —

| field | in | type | required |
| --- | --- | --- | --- |
| `apps` | body | `array<enum(core, accounts, app, asset, lecture, support, design)>\|null` |  |
| `viewports` | body | `array<enum(desktop, mobile)>` | yes |
| `langs` | body | `array<enum(ko, en)>` | yes |

### `design_agent_collect_runs_show`

`GET https://design.gujo.ai/api/agent/collect/runs/{run}` · route `design.api.agent.collect.runs.show`

내가 연 원격 수집 실행의 상태

Status of a collect run I opened

- auth: `design_agent_token` (collect)
- response: `200 application/json` —

| field | in | type | required |
| --- | --- | --- | --- |
| `run` | path | `integer` | yes |

### `design_agent_collect_runs_assets`

`POST https://design.gujo.ai/api/agent/collect/runs/{run}/assets` · route `design.api.agent.collect.runs.assets`

수집 실행에 자산 파일 하나를 올린다

Uploads one asset file to a collect run

- auth: `design_agent_token` (collect)
- response: `201 application/json` —

| field | in | type | required |
| --- | --- | --- | --- |
| `run` | path | `integer` | yes |
| `sha256` | body | `string` | yes |
| `type` | body | `enum(css, js)` | yes |
| `url` | body | `string` | yes |
| `file` | body | `string(binary)` | yes |

### `design_agent_collect_runs_captures`

`POST https://design.gujo.ai/api/agent/collect/runs/{run}/captures` · route `design.api.agent.collect.runs.captures`

수집 실행에 캡처 하나(이미지·HTML)를 올린다

Uploads one capture (image, HTML) to a collect run

- auth: `design_agent_token` (collect)
- response: `201 application/json` —

| field | in | type | required |
| --- | --- | --- | --- |
| `run` | path | `integer` | yes |
| `key` | body | `string` | yes |
| `url` | body | `string` | yes |
| `viewport` | body | `enum(desktop, mobile)` | yes |
| `lang` | body | `enum(ko, en)` | yes |
| `auth` | body | `boolean` | yes |
| `outcome` | body | `enum(ok, failed, redirected)` | yes |
| `message` | body | `string\|null` |  |
| `login_stage` | body | `boolean\|null` |  |
| `saved_state` | body | `boolean\|null` |  |
| `final_url` | body | `string\|null` |  |
| `http_status` | body | `integer\|null` |  |
| `meta` | body | `string\|null` |  |
| `image` | body | `string(binary)` |  |
| `html` | body | `string(binary)\|null` |  |
| `assets` | body | `string\|null` |  |

### `design_agent_collect_runs_fail`

`POST https://design.gujo.ai/api/agent/collect/runs/{run}/fail` · route `design.api.agent.collect.runs.fail`

제출 전 수집 실행을 실패로 닫는다

Closes a collect run as failed before submission

- auth: `design_agent_token` (collect)
- response: `200 application/json` —

| field | in | type | required |
| --- | --- | --- | --- |
| `run` | path | `integer` | yes |
| `error` | body | `string` |  |

입력 칸은 검증 규칙이 없어 선언(`query`·`body`)에서 왔다. 모두 선택 입력이다.

### `design_agent_collect_runs_submit`

`POST https://design.gujo.ai/api/agent/collect/runs/{run}/submit` · route `design.api.agent.collect.runs.submit`

수집 실행을 제출한다(서버가 재생한다)

Submits a collect run (the server replays it)

- auth: `design_agent_token` (collect)
- response: `200 application/json` —

| field | in | type | required |
| --- | --- | --- | --- |
| `run` | path | `integer` | yes |
| `accounts` | body | `array<object{found: boolean, reason: string\|null}>\|null` |  |
| `build_versions` | body | `array<string>\|null` |  |

### `design_agent_docs_show`

`GET https://design.gujo.ai/api/agent/docs/{slug}` · route `design.api.agent.docs.show`

디자인 문서 하나(version 이 없으면 현재 판)

One design document (current version unless version is given)

- auth: `design_agent_token` (read)
- response: `200 application/json` —

| field | in | type | required |
| --- | --- | --- | --- |
| `slug` | path | `string` | yes |
| `version` | query | `integer` |  |

입력 칸은 검증 규칙이 없어 선언(`query`·`body`)에서 왔다. 모두 선택 입력이다.

### `design_agent_docs_versions_list`

`GET https://design.gujo.ai/api/agent/docs/{slug}/versions` · route `design.api.agent.docs.versions.index`

디자인 문서의 판 목록(쪽 단위)

Versions of a design document (paged)

- auth: `design_agent_token` (read)
- response: `200 application/json` —

| field | in | type | required |
| --- | --- | --- | --- |
| `slug` | path | `string` | yes |

### `design_agent_docs_versions_create`

`POST https://design.gujo.ai/api/agent/docs/{slug}/versions` · route `design.api.agent.docs.versions.store`

디자인 문서의 새 초안 판을 만든다(base_version 위에)

Creates a new draft version on top of base_version

- auth: `design_agent_token` (write)
- response: `201 application/json` —

| field | in | type | required |
| --- | --- | --- | --- |
| `slug` | path | `string` | yes |
| `body` | body | `string` | yes |
| `base_version` | body | `integer` | yes |

### `design_agent_docs_versions_submit`

`POST https://design.gujo.ai/api/agent/docs/{slug}/versions/{number}/submit` · route `design.api.agent.docs.versions.submit`

내가 쓴 초안 판을 검수에 올린다

Submits my draft version for review

- auth: `design_agent_token` (write)
- response: `200 application/json` —

| field | in | type | required |
| --- | --- | --- | --- |
| `slug` | path | `string` | yes |
| `number` | path | `integer` | yes |

### `design_agent_manifest`

`GET https://design.gujo.ai/api/agent/manifest` · route `design.api.agent.manifest`

디자인 문서 목록(쪽 단위)과 에이전트 스킬 패키지 정보

Design documents (paged) and the agent skill package info

- auth: `design_agent_token` (read)
- response: `200 application/json` —

### `design_agent_reviews_create`

`POST https://design.gujo.ai/api/agent/reviews` · route `design.api.agent.reviews.store`

판 하나에 검수 판정을 남긴다

Records a review verdict on a version

- auth: `design_agent_token` (review)
- response: `200 application/json` —

| field | in | type | required |
| --- | --- | --- | --- |
| `slug` | body | `string` | yes |
| `version` | body | `integer` | yes |
| `verdict` | body | `enum(approved, changes_requested, rejected)` | yes |
| `reason` | body | `string` | yes |

### `design_agent_reviews_pending`

`GET https://design.gujo.ai/api/agent/reviews/pending` · route `design.api.agent.reviews.pending`

검수 대기 판 목록(쪽 단위)

Versions waiting for review (paged)

- auth: `design_agent_token` (review)
- response: `200 application/json` —

### `design_apps_show`

`GET https://design.gujo.ai/api/v1/apps/{id}` · route `design.api.apps.show`

앱 하나와 그 흐름·화면

One app with its flows and screens

- auth: `design_gallery`
- response: `200 application/json` —

| field | in | type | required |
| --- | --- | --- | --- |
| `id` | path | `integer` | yes |

### `design_patterns`

`GET https://design.gujo.ai/api/v1/patterns` · route `design.api.patterns`

UX 패턴 목록

UX patterns

- auth: `design_gallery`
- response: `200 application/json` —

### `design_screens`

`GET https://design.gujo.ai/api/v1/screens` · route `design.api.screens`

화면 목록(탐색과 같은 쿼리 필터, 쪽 단위)

Screens (same query filters as explore, paged)

- auth: `design_gallery`
- response: `200 application/json` —

| field | in | type | required |
| --- | --- | --- | --- |
| `platform` | query | `string` |  |
| `q` | query | `string` |  |
| `sort` | query | `string` |  |
| `viewport` | query | `string` |  |
| `lang` | query | `string` |  |
| `mode` | query | `string` |  |
| `kind` | query | `string` |  |
| `patterns` | query | `string` |  |
| `components` | query | `string` |  |
| `cols` | query | `integer` |  |
| `page` | query | `integer` |  |

입력 칸은 검증 규칙이 없어 선언(`query`·`body`)에서 왔다. 모두 선택 입력이다.

### `design_screens_show`

`GET https://design.gujo.ai/api/v1/screens/{id}` · route `design.api.screens.show`

화면 하나

One screen

- auth: `design_gallery`
- response: `200 application/json` —

| field | in | type | required |
| --- | --- | --- | --- |
| `id` | path | `integer` | yes |

### `design_taxonomy`

`GET https://design.gujo.ai/api/v1/taxonomy` · route `design.api.taxonomy`

분류 체계(페이지 유형·패턴·요소·색·글꼴)

Taxonomy catalog (page types, patterns, elements, colors, fonts)

- auth: `design_gallery`
- response: `200 application/json` —

### `design_mcp_call`

`POST https://design.gujo.ai/mcp` · route `design.mcp.call`

design MCP(JSON-RPC 2.0, Streamable HTTP, 요청 하나에 JSON 응답 하나). 모르는 MCP-Protocol-Version 은 400

Design MCP (JSON-RPC 2.0 over Streamable HTTP, one JSON response per request). Unknown MCP-Protocol-Version is 400

- auth: `design_agent_token` (read)
- response: `200 application/json` —

| field | in | type | required |
| --- | --- | --- | --- |
| `jsonrpc` | body | `string` |  |
| `id` | body | `string` |  |
| `method` | body | `string` |  |
| `params` | body | `object` |  |

입력 칸은 검증 규칙이 없어 선언(`query`·`body`)에서 왔다. 모두 선택 입력이다.

## 응답 스키마

응답 Data 의 속성이다. `required` 는 언제나 본문에 있는 키다(값은 `null` 일 수 있다).

### BundleRefData

| field | type | required |
| --- | --- | --- |
| `available` | `boolean` | yes |
| `sha256` | `string\|null` | yes |
| `size` | `integer\|null` | yes |
| `download_url` | `string\|null` | yes |

### ClientConfigData

| field | type | required |
| --- | --- | --- |
| `min_client_version` | `string` | yes |
| `revoked` | `array<ClientConfigRevokedData>` | yes |
| `policy` | `ClientConfigPolicyData` | yes |
| `signed_at` | `string(date-time)` | yes |
| `signature` | `string` |  |
| `version` | `integer` |  |

### ClientConfigPolicyData

| field | type | required |
| --- | --- | --- |
| `ttl_days` | `integer` | yes |
| `recheck_hours` | `integer` | yes |

### ClientConfigRevokedData

| field | type | required |
| --- | --- | --- |
| `product_id` | `integer` | yes |
| `device_id` | `string` | yes |

### DeviceBundleData

| field | type | required |
| --- | --- | --- |
| `id` | `integer` | yes |
| `name` | `string` | yes |
| `product_ids` | `array<integer>` | yes |

### DeviceEntitlementProductData

| field | type | required |
| --- | --- | --- |
| `id` | `integer` | yes |
| `name` | `string` | yes |
| `type` | `string` | yes |
| `access_source` | `string` | yes |
| `has_device_key` | `boolean` | yes |
| `license_key_prefix` | `string\|null` | yes |
| `access_expires_at` | `string(date-time)\|null` | yes |
| `updates_supported_until` | `string(date-time)\|null` | yes |

### DeviceEntitlementsData

| field | type | required |
| --- | --- | --- |
| `products` | `array<DeviceEntitlementProductData>` | yes |
| `subscriptions` | `array` | yes |
| `bundles` | `array<DeviceBundleData>` | yes |
| `device` | `DeviceInfoData` | yes |
| `user` | `DeviceUserData\|null` | yes |

### DeviceInfoData

| field | type | required |
| --- | --- | --- |
| `id` | `integer\|null` | yes |
| `name` | `string\|null` | yes |
| `activated_at` | `string(date-time)\|null` | yes |

### DeviceUserData

| field | type | required |
| --- | --- | --- |
| `id` | `integer` | yes |
| `email` | `string\|null` | yes |
| `name` | `string\|null` | yes |

### DisclosureData

| field | type | required |
| --- | --- | --- |
| `company_name` | `string` | yes |
| `representative_name` | `string` | yes |
| `business_registration_number` | `string` | yes |
| `mail_order_registration_number` | `string` | yes |
| `address` | `string` | yes |
| `phone` | `string` | yes |
| `email` | `string` | yes |
| `website` | `string` | yes |
| `ftc_biz_info_url` | `string` | yes |

### EntitlementData

| field | type | required |
| --- | --- | --- |
| `id` | `integer` | yes |
| `product_id` | `integer` | yes |
| `source_product_id` | `integer\|null` | yes |
| `product_detail_url` | `string` | yes |
| `order_id` | `integer` | yes |
| `order_item_id` | `integer` | yes |
| `grant_type` | `string` | yes |
| `grant_type_label` | `string` | yes |
| `status` | `string` | yes |
| `status_label` | `string` | yes |
| `access_state` | `string` | yes |
| `access_state_label` | `string` | yes |
| `active` | `boolean` | yes |
| `source_quantity` | `integer` | yes |
| `access_expires_at` | `string(date-time)\|null` | yes |
| `updates_supported_until` | `string(date-time)\|null` | yes |
| `granted_at` | `string(date-time)\|null` | yes |
| `revoked_at` | `string(date-time)\|null` | yes |

### LibraryApiData

| field | type | required |
| --- | --- | --- |
| `items` | `array<LibraryApiItemData>` | yes |

### LibraryApiItemData

| field | type | required |
| --- | --- | --- |
| `access_source` | `string` | yes |
| `thumbnail_html` | `string\|null` | yes |
| `hero_path` | `string\|null` | yes |
| `hero_alt` | `string\|null` | yes |
| `product` | `ProductSetupData` | yes |
| `setup_progress` | `ProductSetupProgressData` | yes |
| `mcp_server` | `McpServerData\|null` | yes |
| `bundle` | `BundleRefData\|null` | yes |

### McpServerData

| field | type | required |
| --- | --- | --- |
| `id` | `integer` | yes |
| `user_id` | `integer` | yes |
| `product_id` | `integer` | yes |
| `name` | `string` | yes |
| `enabled` | `boolean` | yes |
| `product_name` | `string` | yes |
| `tools` | `array<McpToolData>` | yes |

### McpToolData

| field | type | required |
| --- | --- | --- |
| `id` | `integer` | yes |
| `mcp_server_id` | `integer` | yes |
| `product_id` | `integer` | yes |
| `name` | `string` | yes |
| `enabled` | `boolean` | yes |

### OrderData

| field | type | required |
| --- | --- | --- |
| `id` | `integer` | yes |
| `number` | `string` | yes |
| `status` | `string` | yes |
| `status_label` | `string` | yes |
| `subtotal_cents` | `integer` | yes |
| `discount_total_cents` | `integer` | yes |
| `total_cents` | `integer` | yes |
| `currency` | `string` | yes |
| `payment_method` | `string` | yes |
| `items` | `array<OrderItemData>` | yes |
| `can_withdraw` | `boolean` | yes |
| `receipt_url` | `string\|null` | yes |
| `supply_cents` | `integer\|null` | yes |
| `vat_cents` | `integer\|null` | yes |
| `subscription_name` | `string\|null` | yes |

### OrderItemData

| field | type | required |
| --- | --- | --- |
| `id` | `integer` | yes |
| `product_id` | `integer` | yes |
| `product_name` | `string` | yes |
| `product_url` | `string` | yes |
| `list_unit_price_cents` | `integer` | yes |
| `unit_price_cents` | `integer` | yes |
| `quantity` | `integer` | yes |
| `subtotal_cents` | `integer` | yes |
| `discount_total_cents` | `integer` | yes |
| `total_cents` | `integer` | yes |
| `entitlements` | `array<EntitlementData>` | yes |

### OrderShowData

| field | type | required |
| --- | --- | --- |
| `order` | `OrderData` | yes |

### OrdersData

| field | type | required |
| --- | --- | --- |
| `orders` | `array<OrderData>` | yes |

### PassCheckData

| field | type | required |
| --- | --- | --- |
| `has_active_pass` | `boolean` | yes |
| `in_grace_period` | `boolean` | yes |
| `expires_at` | `string(date-time)\|null` | yes |
| `grace_period_ends_at` | `string(date-time)\|null` | yes |
| `grace_warning` | `string\|null` | yes |
| `tier` | `string` | yes |
| `features` | `array<string>` | yes |
| `allowed_apps` | `array<string>` | yes |
| `renewal_url` | `string\|null` | yes |
| `message` | `string\|null` | yes |

### ProductApiData

| field | type | required |
| --- | --- | --- |
| `id` | `integer` | yes |
| `name` | `string` | yes |
| `description` | `string\|null` | yes |
| `list_price_cents` | `integer\|null` | yes |
| `unit_price_cents` | `integer` | yes |
| `currency` | `string` | yes |
| `active` | `boolean` | yes |
| `entitled` | `boolean` | yes |
| `primary_category` | `ProductCategoryData\|null` | yes |
| `categories` | `array<ProductCategoryData>` | yes |
| `tags` | `array<ProductTagData>` | yes |
| `content_locale` | `string\|null` | yes |

### ProductCategoryData

| field | type | required |
| --- | --- | --- |
| `id` | `integer` | yes |
| `parent_id` | `integer\|null` | yes |
| `name` | `string` | yes |
| `active` | `boolean` | yes |
| `visibility` | `string` | yes |

### ProductDeliveryData

| field | type | required |
| --- | --- | --- |
| `schema_version` | `integer` | yes |
| `strategy` | `enum(agent-skill, vscode-vsix, macos-app, binary, managed-directory, manual)` | yes |
| `payload` | `ProductDeliveryPayloadData` | yes |
| `targets` | `array<enum(codex, claude-code, vscode, macos)>` | yes |
| `dependencies` | `array<ProductDeliveryDependencyData>` | yes |
| `launch` | `ProductDeliveryLaunchData` | yes |
| `requires_confirmation` | `boolean` | yes |
| `compatibility` | `boolean` | yes |

### ProductDeliveryDependencyData

| field | type | required |
| --- | --- | --- |
| `kind` | `enum(executable, macos-version, architecture, host-application)` | yes |
| `name` | `string\|null` | yes |
| `version_constraint` | `string\|null` | yes |
| `architectures` | `array<string>` | yes |
| `bundle_id` | `string\|null` | yes |

### ProductDeliveryLaunchData

| field | type | required |
| --- | --- | --- |
| `kind` | `enum(open-app, reveal-managed-root, open-vscode, none)` | yes |

### ProductDeliveryPayloadData

| field | type | required |
| --- | --- | --- |
| `artifact` | `string\|null` | yes |
| `root` | `string\|null` | yes |
| `skill_manifest` | `string\|null` | yes |
| `vsix_path` | `string\|null` | yes |
| `extension_id` | `string\|null` | yes |
| `app_path` | `string\|null` | yes |
| `bundle_id` | `string\|null` | yes |
| `binary_path` | `string\|null` | yes |
| `executable_name` | `string\|null` | yes |

### ProductDetailApiData

| field | type | required |
| --- | --- | --- |
| `product` | `ProductApiData` | yes |
| `delivery` | `ProductDeliveryData` | yes |
| `detail_html` | `string\|null` | yes |
| `manual` | `ProductManualData\|null` | yes |
| `runtime` | `string\|null` | yes |
| `platforms` | `array<string>` | yes |
| `hero_path` | `string\|null` | yes |
| `hero_alt` | `string\|null` | yes |
| `gallery` | `array` | yes |
| `videos` | `array<object>` | yes |
| `usage_path` | `string\|null` | yes |
| `architecture_path` | `string\|null` | yes |
| `ai_prompt_path` | `string\|null` | yes |

### ProductInstallAssetData

| field | type | required |
| --- | --- | --- |
| `platform` | `string` | yes |
| `label` | `string` | yes |
| `path` | `string` | yes |
| `download_url` | `string` | yes |

### ProductInstallData

| field | type | required |
| --- | --- | --- |
| `product_id` | `integer` | yes |
| `kind` | `string` | yes |
| `name` | `string` | yes |
| `version` | `string` | yes |
| `build` | `string\|null` | yes |
| `channel` | `string\|null` | yes |
| `filename` | `string` | yes |
| `sha256` | `string` | yes |
| `size` | `integer` | yes |
| `format` | `string` | yes |
| `download_url` | `string` | yes |
| `download_expires_at` | `string(date-time)` | yes |
| `target` | `string` | yes |
| `slug` | `string\|null` | yes |

### ProductManualData

| field | type | required |
| --- | --- | --- |
| `summary` | `string\|null` | yes |
| `requirements` | `array<string>` | yes |
| `install_steps` | `array<string>` | yes |
| `verification_steps` | `array<string>` | yes |
| `troubleshooting` | `array<string>` | yes |

### ProductReleaseData

| field | type | required |
| --- | --- | --- |
| `id` | `integer` | yes |
| `product_id` | `integer` | yes |
| `version` | `string` | yes |
| `notes_ko` | `string\|null` | yes |
| `notes_en` | `string\|null` | yes |
| `notes` | `string\|null` | yes |
| `released_at` | `string(date-time)` | yes |

### ProductSetupData

| field | type | required |
| --- | --- | --- |
| `id` | `integer` | yes |
| `name` | `string` | yes |
| `type_label` | `string` | yes |
| `description` | `string\|null` | yes |
| `delivery` | `ProductDeliveryData` | yes |
| `manual` | `ProductManualData\|null` | yes |
| `outputs` | `array<ProductSetupOutputData>` | yes |
| `install_assets` | `array<ProductInstallAssetData>` | yes |
| `runtime` | `string\|null` | yes |
| `platforms` | `array<string>` | yes |

### ProductSetupOutputData

| field | type | required |
| --- | --- | --- |
| `id` | `integer` | yes |
| `title` | `string` | yes |
| `category_label` | `string\|null` | yes |
| `summary` | `string\|null` | yes |
| `prompt` | `string` | yes |
| `usage_guide` | `string\|null` | yes |
| `action_url` | `string\|null` | yes |

### ProductSetupProgressData

| field | type | required |
| --- | --- | --- |
| `completed_step_indexes` | `array<integer>` | yes |
| `completed_count` | `integer` | yes |
| `total_count` | `integer` | yes |
| `progress_percent` | `integer` | yes |
| `completed` | `boolean` | yes |
| `completed_at` | `string(date-time)\|null` | yes |

### ProductTagData

| field | type | required |
| --- | --- | --- |
| `id` | `integer` | yes |
| `name` | `string` | yes |
| `type` | `string\|null` | yes |
| `active` | `boolean` | yes |

### ProductUpdateApiData

| field | type | required |
| --- | --- | --- |
| `product_id` | `integer` | yes |
| `current_version` | `string` | yes |
| `released_at` | `string(date-time)\|null` | yes |
| `changelog` | `string\|null` | yes |
| `source_ref` | `string\|null` | yes |
| `artifact_sha256` | `string\|null` | yes |
| `artifact_size` | `integer\|null` | yes |
| `update_available` | `boolean` | yes |
| `download_allowed` | `boolean` | yes |
| `download_url` | `string` | yes |

### PublicDisclosureData

| field | type | required |
| --- | --- | --- |
| `ko` | `DisclosureData` | yes |
| `en` | `DisclosureData` | yes |

### SubscriptionData

| field | type | required |
| --- | --- | --- |
| `id` | `integer` | yes |
| `status` | `string` | yes |
| `status_label` | `string` | yes |
| `is_trial` | `boolean` | yes |
| `starts_at` | `string(date-time)` | yes |
| `ends_at` | `string(date-time)\|null` | yes |
| `cancelled_at` | `string(date-time)\|null` | yes |
| `order_id` | `integer\|null` | yes |
| `order_number` | `string\|null` | yes |
| `can_cancel` | `boolean` | yes |
| `cancel_at_period_end` | `boolean` | yes |
| `next_charge_at` | `string(date-time)\|null` | yes |
| `renewal_amount_cents` | `integer\|null` | yes |
| `renewal_currency` | `string\|null` | yes |
| `payment_method_brand` | `string\|null` | yes |
| `payment_method_last4` | `string\|null` | yes |
| `is_pass` | `boolean` | yes |
| `offer_code` | `string\|null` | yes |
| `offer_name` | `string\|null` | yes |
| `action` | `string` | yes |
| `estimated_krw_amount` | `integer\|null` | yes |
| `estimated_krw_exchange_rate` | `string\|null` | yes |
| `estimated_krw_exchange_source` | `string\|null` | yes |
| `estimated_krw_currency` | `string\|null` | yes |
| `update_card_url` | `string\|null` | yes |
| `update_payment_method_url` | `string\|null` | yes |

### SubscriptionsData

| field | type | required |
| --- | --- | --- |
| `subscriptions` | `array<SubscriptionData>` | yes |
