> For the complete documentation index, see [llms.txt](https://docs.hackle.io/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.hackle.io/development-guide/http-api/catalog.md).

# 카탈로그 관리

카탈로그는 상품, 콘텐츠처럼 **데이터를 저장해두고 쓰기 위한 기능**입니다. 카탈로그에 담은 아이템은 메시지 개인화 등에 활용할 수 있습니다.

카탈로그 API로 카탈로그를 만들고, 아이템을 올리고, 저장된 아이템을 수정할 수 있습니다.

### 인증

모든 요청에 API 키를 헤더로 전달해야 합니다.

| 헤더                 | 값                         |
| ------------------ | ------------------------- |
| `X-HACKLE-API-KEY` | API 키 (대시보드 > 연동 정보에서 확인) |

### 기본 규칙

* Base URL: `https://api.hackle.io`
* 요청 본문은 `Content-Type: application/json` 입니다.
* 카탈로그와 필드는 **이름**으로 지정합니다. 이름은 만들고 나면 바꿀 수 없습니다.
* 카탈로그는 워크스페이스의 **모든 환경**에 만들어지고, 아이템은 **API 키의 환경**에만 저장됩니다.

#### 필드 타입

<table><thead><tr><th width="126.140625">타입</th><th>값 형식</th><th>예시</th></tr></thead><tbody><tr><td><code>STRING</code></td><td>문자열 (750자 이하)</td><td><code>"티셔츠"</code></td></tr><tr><td><code>NUMBER</code></td><td>숫자 (정수부 20자리, 소수부 10자리 이하)</td><td><code>19900</code></td></tr><tr><td><code>BOOLEAN</code></td><td><code>true</code> / <code>false</code></td><td><code>true</code></td></tr><tr><td><code>TIME</code></td><td>ISO-8601 문자열 또는 epoch 초</td><td><code>"2026-08-01T00:00:00Z"</code>, <code>1754006400</code></td></tr></tbody></table>

***

### 1. 카탈로그 목록 조회

**엔드포인트:** `GET https://api.hackle.io/v1/catalogs`

**응답 본문**

| 필드                      | 타입     | 설명                   |
| ----------------------- | ------ | -------------------- |
| catalogs                | Array  | 카탈로그 목록              |
| catalogs\[].catalogName | string | 카탈로그 이름              |
| catalogs\[].description | string | 카탈로그 설명              |
| catalogs\[].itemCount   | number | 아이템 수 (API 키의 환경 기준) |
| catalogs\[].createdAt   | string | 생성 일시                |
| catalogs\[].modifiedAt  | string | 수정 일시                |

```json
{
  "catalogs": [
    {
      "catalogName": "products",
      "description": "상품 카탈로그",
      "itemCount": 1024,
      "createdAt": "2026-08-01T10:00:00",
      "modifiedAt": "2026-08-01T10:00:00"
    }
  ]
}
```

**응답 코드**

* 200 OK: 성공
* 401 Unauthorized: 헤더값 없음 또는 유효하지 않은 API 키

***

### 2. 카탈로그 조회

**엔드포인트:** `GET https://api.hackle.io/v1/catalogs/{catalogName}`

**경로 파라미터**

| 필드          | 타입     | 필수 | 설명      |
| ----------- | ------ | -- | ------- |
| catalogName | string | O  | 카탈로그 이름 |

**응답 본문**

| 필드                  | 타입     | 설명                   |
| ------------------- | ------ | -------------------- |
| catalogName         | string | 카탈로그 이름              |
| description         | string | 카탈로그 설명              |
| itemCount           | number | 아이템 수 (API 키의 환경 기준) |
| createdAt           | string | 생성 일시                |
| modifiedAt          | string | 수정 일시                |
| fields              | Array  | 필드 목록                |
| fields\[].fieldName | string | 필드 이름                |
| fields\[].fieldType | string | 필드 타입                |

```json
{
  "catalogName": "products",
  "description": "상품 카탈로그",
  "itemCount": 1024,
  "createdAt": "2026-08-01T10:00:00",
  "modifiedAt": "2026-08-01T10:00:00",
  "fields": [
    { "fieldName": "name", "fieldType": "STRING" },
    { "fieldName": "price", "fieldType": "NUMBER" }
  ]
}
```

**응답 코드**

* 200 OK: 성공
* 401 Unauthorized: 헤더값 없음 또는 유효하지 않은 API 키
* 404 Not Found: 해당 이름의 카탈로그 없음

***

### 3. 카탈로그 생성

**엔드포인트:** `POST https://api.hackle.io/v1/catalogs`

**요청 본문**

<table><thead><tr><th width="195.0859375">필드</th><th width="115.3671875">타입</th><th width="91.59375">필수</th><th>설명</th></tr></thead><tbody><tr><td>catalogName</td><td>string</td><td>O</td><td>카탈로그 이름. 영문, 숫자, <code>-</code>, <code>_</code> 만 사용하며 200자 이하</td></tr><tr><td>description</td><td>string</td><td>X</td><td>카탈로그 설명</td></tr><tr><td>fields</td><td>Array</td><td>X</td><td>함께 만들 필드 목록 (최대 50개)</td></tr><tr><td>fields[].fieldName</td><td>string</td><td>O</td><td>필드 이름. 카탈로그 안에서 유일해야 하며 <code>id</code> 는 사용할 수 없음</td></tr><tr><td>fields[].fieldType</td><td>string</td><td>O</td><td><code>STRING</code> / <code>NUMBER</code> / <code>BOOLEAN</code> / <code>TIME</code></td></tr></tbody></table>

```json
{
  "catalogName": "products",
  "description": "상품 카탈로그",
  "fields": [
    { "fieldName": "name", "fieldType": "STRING" },
    { "fieldName": "price", "fieldType": "NUMBER" }
  ]
}
```

**응답 본문**

카탈로그 조회와 같습니다.

**응답 코드**

* 201 Created: 성공
* 400 Bad Request: 유효하지 않은 요청
* 401 Unauthorized: 헤더값 없음 또는 유효하지 않은 API 키
* 409 Conflict: 같은 이름의 카탈로그가 이미 있거나 카탈로그 수 상한(20개) 초과

{% hint style="info" %}
필드 생성이 하나라도 실패하면 카탈로그는 만들어지지 않습니다.
{% endhint %}

### 4. 카탈로그 삭제

**엔드포인트:** `DELETE https://api.hackle.io/v1/catalogs/{catalogName}`

**경로 파라미터**

| 필드          | 타입     | 필수 | 설명      |
| ----------- | ------ | -- | ------- |
| catalogName | string | O  | 카탈로그 이름 |

**응답 코드**

* 204 No Content: 성공
* 401 Unauthorized: 헤더값 없음 또는 유효하지 않은 API 키
* 404 Not Found: 해당 이름의 카탈로그 없음

{% hint style="danger" %}
카탈로그를 삭제하면 담겨 있던 아이템도 함께 사용할 수 없게 됩니다. 삭제한 이름은 다시 사용할 수 있습니다.
{% endhint %}

### 5. 필드 추가

**엔드포인트:** `POST https://api.hackle.io/v1/catalogs/{catalogName}/fields`

**요청 본문**

<table><thead><tr><th width="116.0625">필드</th><th width="94.546875">타입</th><th width="98.60546875">필수</th><th>설명</th></tr></thead><tbody><tr><td>fieldName</td><td>string</td><td>O</td><td>필드 이름. 카탈로그 안에서 유일해야 하며 <code>id</code> 는 사용할 수 없음</td></tr><tr><td>fieldType</td><td>string</td><td>O</td><td><code>STRING</code> / <code>NUMBER</code> / <code>BOOLEAN</code> / <code>TIME</code></td></tr></tbody></table>

```json
{ "fieldName": "brand", "fieldType": "STRING" }
```

**응답 본문**

```json
{ "fieldName": "brand", "fieldType": "STRING" }
```

**응답 코드**

* 201 Created: 성공
* 400 Bad Request: 유효하지 않은 요청
* 401 Unauthorized: 헤더값 없음 또는 유효하지 않은 API 키
* 404 Not Found: 해당 이름의 카탈로그 없음
* 409 Conflict: 같은 이름의 필드가 이미 있거나 필드 수 상한(50개) 초과

***

### 6. 필드 삭제

**엔드포인트:** `DELETE https://api.hackle.io/v1/catalogs/{catalogName}/fields/{fieldName}`

**경로 파라미터**

| 필드          | 타입     | 필수 | 설명      |
| ----------- | ------ | -- | ------- |
| catalogName | string | O  | 카탈로그 이름 |
| fieldName   | string | O  | 필드 이름   |

**응답 코드**

* 204 No Content: 성공
* 401 Unauthorized: 헤더값 없음 또는 유효하지 않은 API 키
* 404 Not Found: 해당 이름의 카탈로그 또는 필드 없음

***

### 7. 아이템 추가

**엔드포인트:** `POST https://api.hackle.io/v1/catalogs/{catalogName}/items`

**요청 본문**

| 필드              | 타입     | 필수 | 설명                                       |
| --------------- | ------ | -- | ---------------------------------------- |
| items           | Array  | O  | 추가할 아이템 목록. 1개 이상 500개 이하                |
| items\[].id     | string | O  | 아이템 식별자. 영문, 숫자, `-`, `_` 만 사용하며 300자 이하 |
| items\[].fields | Object | X  | 필드 이름과 값. 카탈로그에 정의된 필드만 사용 가능            |

```json
{
  "items": [
    {
      "id": "P1",
      "fields": {
        "name": "티셔츠",
        "price": 19900
      }
    }
  ]
}
```

**응답 본문**

| 필드             | 타입     | 설명        |
| -------------- | ------ | --------- |
| processedCount | number | 처리한 아이템 수 |

```json
{ "processedCount": 1 }
```

**응답 코드**

* 200 OK: 성공
* 400 Bad Request: 유효하지 않은 요청 또는 아이템 검증 실패 (아이템 업로드 실패 참고)
* 401 Unauthorized: 헤더값 없음 또는 유효하지 않은 API 키
* 404 Not Found: 해당 이름의 카탈로그 없음
* 409 Conflict: 같은 카탈로그에 다른 업로드가 진행 중

{% hint style="info" %}
이미 있는 `id` 는 추가되지 않고 무시됩니다. 값을 바꾸려면 아이템 수정을 사용하세요.
{% endhint %}

### 8. 아이템 수정

**엔드포인트:** `PUT https://api.hackle.io/v1/catalogs/{catalogName}/items`

요청 본문과 응답은 아이템 추가와 같습니다.

```json
{
  "items": [
    {
      "id": "P1",
      "fields": {
        "name": "티셔츠",
        "price": 15900
      }
    }
  ]
}
```

**아이템을 통째로 교체합니다. 보내지 않은 필드는 값이 지워집니다.** 한 필드만 바꾸려는 경우에도 나머지 필드를 함께 보내야 합니다.

**응답 코드**

* 200 OK: 성공
* 400 Bad Request: 유효하지 않은 요청 또는 아이템 검증 실패
* 401 Unauthorized: 헤더값 없음 또는 유효하지 않은 API 키
* 404 Not Found: 해당 이름의 카탈로그 없음
* 409 Conflict: 같은 카탈로그에 다른 업로드가 진행 중

{% hint style="info" %}
없는 `id` 는 수정되지 않고 무시됩니다.
{% endhint %}

### 9. 아이템 삭제

**엔드포인트:** `DELETE https://api.hackle.io/v1/catalogs/{catalogName}/items`

**요청 본문**

| 필드  | 타입    | 필수 | 설명                            |
| --- | ----- | -- | ----------------------------- |
| ids | Array | O  | 삭제할 아이템 식별자 목록. 1개 이상 500개 이하 |

```json
{ "ids": ["P1", "P2"] }
```

**응답 본문**

```json
{ "processedCount": 2 }
```

**응답 코드**

* 200 OK: 성공
* 400 Bad Request: 유효하지 않은 요청
* 401 Unauthorized: 헤더값 없음 또는 유효하지 않은 API 키
* 404 Not Found: 해당 이름의 카탈로그 없음
* 409 Conflict: 같은 카탈로그에 다른 업로드가 진행 중

***

### 에러 응답

에러는 아래 형식으로 응답합니다.

```json
{
  "code": "CATALOG_NOT_FOUND",
  "message": "Could not find catalog with the given name"
}
```

| 상태 코드 | code                          | 설명                        |
| ----- | ----------------------------- | ------------------------- |
| 400   | `bad_request`                 | 요청 본문 형식이 올바르지 않음         |
| 400   | `CATALOG_NAME_INVALID`        | 카탈로그 또는 필드 이름 규칙 위반       |
| 400   | `CATALOG_FIELD_NAME_INVALID`  | 필드 이름이 중복되거나 예약어(`id`) 사용 |
| 400   | `ITEM_UPLOAD_REQUEST_INVALID` | 아이템 수가 1\~500 범위를 벗어남     |
| 400   | `INVALID_PAGINATION`          | 페이지 번호 또는 페이지 크기가 범위를 벗어남 |
| 401   | -                             | 헤더값 없음 또는 유효하지 않은 API 키   |
| 429   | `TOO_MANY_REQUESTS`           | 호출 한도 초과                  |
| 404   | `CATALOG_NOT_FOUND`           | 해당 이름의 카탈로그 없음            |
| 404   | `CATALOG_FIELD_NOT_FOUND`     | 해당 이름의 필드 없음              |
| 404   | `CATALOG_ITEM_NOT_FOUND`      | 해당 식별자의 아이템 없음            |
| 409   | `CATALOG_NAME_DUPLICATE`      | 같은 이름의 카탈로그 또는 필드가 이미 있음  |
| 409   | `CATALOG_CAP_EXCEEDED`        | 카탈로그 또는 필드 수 상한 초과        |
| 409   | `CONCURRENT_MODIFICATION`     | 같은 카탈로그에 다른 변경이 진행 중      |
| 409   | `ITEM_UPLOAD_IN_PROGRESS`     | 같은 카탈로그에 다른 아이템 업로드가 진행 중 |
| 500   | `internal_server_error`       | 서버 오류                     |

#### 아이템 업로드 실패

아이템 추가·수정 요청의 아이템이 검증에 실패하면, 실패한 위치와 함께 400으로 응답합니다. **한 건이라도 실패하면 요청 전체가 반영되지 않습니다.**

```json
{
  "code": "TYPE_MISMATCH",
  "message": "Item value does not match the field type, or the field is not defined in the catalog",
  "failedRowNumber": 3,
  "failedValue": "price"
}
```

| 필드              | 타입     | 설명                |
| --------------- | ------ | ----------------- |
| code            | string | 실패 사유             |
| message         | string | 실패 사유 설명          |
| failedRowNumber | number | 실패한 아이템의 순번 (1부터) |
| failedValue     | string | 실패한 값             |

| code                 | 설명                                |
| -------------------- | --------------------------------- |
| `TYPE_MISMATCH`      | 값이 필드 타입과 맞지 않거나, 카탈로그에 없는 필드를 사용 |
| `LENGTH_EXCEEDED`    | 값이 너무 김                           |
| `ITEM_LIMIT_REACHED` | 카탈로그의 아이템 수 상한 초과                 |
| `MISSING_ITEM_KEY`   | 아이템 식별자가 비어 있음                    |
| `INVALID_ITEM_KEY`   | 아이템 식별자 규칙 위반                     |
| `MISSING_FIELD`      | 필수 값 누락                           |

***

### 호출 한도

카탈로그를 변경하는 요청(카탈로그·필드 생성/삭제, 아이템 추가/수정/삭제)은 워크스페이스와 환경 단위로 **분당 50회**로 제한합니다.

응답에 남은 호출 수를 헤더로 내려줍니다.

| 헤더                      | 설명                      |
| ----------------------- | ----------------------- |
| `X-RateLimit-Limit`     | 한도                      |
| `X-RateLimit-Remaining` | 남은 호출 수                 |
| `X-RateLimit-Reset`     | 한도가 회복되는 시각 (epoch 초)   |
| `Retry-After`           | 한도 초과 시 재시도까지 기다려야 하는 초 |

한도를 넘으면 429로 응답합니다. `Retry-After` 만큼 기다린 뒤 재시도하세요.

```
{
  "code": "TOO_MANY_REQUESTS",
  "message": "Too many requests. Retry after the time in the Retry-After header"
}
```

***

### 제약사항

| 항목             | 한도                         |
| -------------- | -------------------------- |
| 워크스페이스당 카탈로그 수 | 20개                        |
| 카탈로그당 필드 수     | 50개                        |
| 카탈로그당 아이템 수    | 1,000,000개 (환경별)           |
| 한 요청의 아이템 수    | 1\~500개                    |
| 카탈로그·필드 이름     | 영문, 숫자, `-`, `_` / 200자 이하 |
| 아이템 식별자        | 영문, 숫자, `-`, `_` / 300자 이하 |
| 문자열 값 길이       | 750자                       |
| 숫자 값           | 정수부 20자리, 소수부 10자리         |

* 카탈로그 이름과 필드 이름은 만든 뒤에 바꿀 수 없습니다.
* `id` 는 아이템 식별자용으로 예약되어 필드 이름으로 사용할 수 없습니다.
* 아이템에 값을 넣으려면 필드를 먼저 만들어야 합니다. 카탈로그에 없는 필드를 보내면 요청이 실패합니다.
* 응답의 `processedCount` 는 요청한 아이템 수입니다. 이미 있는 `id` 를 추가하거나 없는 `id` 를 수정·삭제한 경우에도 개수에 포함됩니다.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.hackle.io/development-guide/http-api/catalog.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
