# API reference

> Paths, credentials, abilities and input fields of the endpoints called with an account API key, the public lookups, the app distribution feeds and the design host API.

The tables on this page are the generated document built from the server's router and controller declarations, included as is. Nothing is copied by hand, so they cannot drift from the server. The reference itself is written in Korean, with each endpoint described in both Korean and English.

- Machine-readable copy of the same content: [`/openapi.json`](https://developers.gujo.ai/openapi.json) (OpenAPI 3.1)
- What authentication and error codes mean: [Authentication and API keys](auth.md)
- MCP tool names match the `tool` column below: [Connect 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 |
