> ## Documentation Index
> Fetch the complete documentation index at: https://docs.notifly.tech/llms.txt
> Use this file to discover all available pages before exploring further.

# Catalog REST API 시작하기

> API 카탈로그의 인증, 리소스 구조, 쓰기 방식, 페이지네이션, 오류 처리 방법을 설명합니다.

Catalog REST API로 상품이나 콘텐츠 참조 데이터를 스키마와 함께 관리할 수 있습니다. 메시지에서는 Liquid의 `catalog_items` 또는 `catalog_selection_items` 태그로 저장한 값을 조회합니다.

<Warning>
  Catalog REST API에는 개인정보를 저장할 수 없습니다. 카탈로그 생성 요청에 `data_classification: "non_personal"`을 넣어야 하며, 유저 ID·이메일·전화번호·기기 토큰처럼 개인을 식별할 수 있는 값은 필드나 아이템에 포함하지 마세요.
</Warning>

## API 범위

기본 URL은 `https://api.notifly.tech`이며 모든 Catalog 경로는 프로젝트에 속합니다.

```text theme={null}
/v1/projects/{projectId}/catalogs
```

공개 Catalog REST API는 다음 조건을 만족하는 카탈로그만 조회하고 변경합니다.

* 데이터 소스가 `api`입니다.
* 데이터 분류가 `non_personal`입니다.
* 상태가 `active`입니다.

Google 스프레드시트 카탈로그는 공개 Catalog REST API 목록에 나타나지 않습니다. 콘솔에서 소스와 동기화를 관리하세요.

공개 API의 리소스별 관리 범위는 다음과 같습니다.

* 카탈로그: 생성, 목록, 영구 삭제. 단건 조회와 이름·설명·스키마 전체 수정은 지원하지 않습니다.
* 필드: 추가와 삭제. 이름·타입 수정은 지원하지 않습니다.
* 아이템: 단건·일괄 생성, 조회, 전체 교체, 부분 수정, 삭제
* 셀렉션: 생성과 삭제. 목록·단건 조회·수정은 지원하지 않습니다.

요청과 응답은 JSON만 지원하며 공개 CSV 가져오기·내보내기 endpoint는 없습니다. 카탈로그 `DELETE`는 별도 비활성화 단계 없이 데이터와 동기화 기록을 영구 삭제하므로, 참조 중인 메시지와 백업을 먼저 확인하세요.

## 인증

먼저 `/authenticate`에 Access Key와 Secret Key를 보내 인증 토큰을 발급받습니다. 이후 모든 Catalog 요청에 다음 헤더를 추가합니다.

```http theme={null}
Authorization: Bearer <auth-token>
Content-Type: application/json
```

인증 토큰의 기본 만료 시간은 1시간입니다. 토큰이 만료되어 `401`을 받으면 새 토큰을 발급받아 요청을 다시 보내세요. 자세한 인증 절차는 [API 시작하기](/ko/api-reference/getting-started)를 참고하세요.

## 기본 흐름

### 1. 카탈로그 만들기

한 요청에서 카탈로그 하나를 만듭니다. `fields`의 첫 원소는 항상 `id:string`이어야 합니다.

```json theme={null}
{
  "catalogs": [
    {
      "name": "products",
      "description": "메시지 개인화용 상품 정보",
      "data_classification": "non_personal",
      "fields": [
        { "name": "id", "type": "string" },
        { "name": "name", "type": "string" },
        { "name": "price", "type": "number" },
        { "name": "tags", "type": "array" }
      ]
    }
  ]
}
```

지원하는 필드 타입은 `string`, `number`, `boolean`, `time`, `geo`, `object`, `array`입니다. `time`은 ISO 8601 문자열 또는 안전한 정수 범위의 Unix seconds를 받으며 UTC ISO 8601 문자열로 저장됩니다. `geo`는 유한한 숫자로 이루어진 `[경도, 위도]` 배열이며 경도는 -180~~180, 위도는 -90~~90 범위여야 합니다. `array`는 문자열 배열을 받습니다.

### 2. 아이템 넣기

여러 아이템을 새로 넣을 때는 `POST /items`를 사용합니다. 한 요청에 최대 50개를 보낼 수 있습니다.

```json theme={null}
{
  "items": [
    {
      "id": "sku-1001",
      "name": "코튼 셔츠",
      "price": 39000,
      "tags": ["summer", "shirt"]
    }
  ]
}
```

요청 안의 아이템 하나라도 스키마에 맞지 않으면 전체 요청이 실패합니다. 일부만 저장되지 않습니다.

### 3. 조회하고 페이지 이동하기

`GET /items`는 아이템을 ID 오름차순으로 50개씩 반환합니다. 다음 페이지가 있으면 응답의 `data.next_cursor`와 `Link: <...>; rel="next"` 헤더가 함께 옵니다.

* `next_cursor`는 해석하지 말고 다음 요청의 `cursor` query parameter에 그대로 전달합니다.
* `next_cursor`가 `null`이면 마지막 페이지입니다.

## 셀렉션 필터

셀렉션은 카탈로그 아이템 중 메시지에서 반복해 사용할 부분집합을 저장합니다. 필드 타입별로 다음 연산자만 사용할 수 있습니다.

* `string`: `equals`, `does not equal`
* `number`: 위 두 연산자와 `greater than`, `less than`
* `boolean`: `is`
* `time`: `before`, `after`
* `array`: `includes value`, `does not include value`
* `geo`, `object`: 필터 미지원

필터 값에는 `null`을 사용할 수 없습니다. `sort_field`를 지정하면 `sort_order`도 함께 보내야 합니다. `results_limit`을 생략하면 50개를 사용합니다. 정렬을 생략하면 셀렉션을 만들 때 무작위로 최대 `results_limit`개를 고릅니다.

## 쓰기 메서드 선택

| 메서드      | 동작                   | 재시도할 때의 주의점                                |
| -------- | -------------------- | ------------------------------------------ |
| `POST`   | 새 리소스만 생성            | 같은 ID가 이미 있으면 `409`                        |
| `PUT`    | 아이템 전체를 교체하고, 없으면 생성 | 보내지 않은 기존 필드는 제거됨                          |
| `PATCH`  | 기존 아이템의 전달한 필드만 변경   | 없는 ID는 `404`; 배열에는 `$add`, `$remove` 사용 가능 |
| `DELETE` | 지정한 리소스 삭제           | 존재하지 않는 대상은 `404`                          |

배열 필드를 일부만 바꾸는 예시입니다.

```json theme={null}
{
  "items": [
    {
      "id": "sku-1001",
      "tags": {
        "$add": ["sale"],
        "$remove": ["summer"]
      }
    }
  ]
}
```

bulk 아이템 쓰기와 필드·셀렉션 변경은 요청 안에서 처리되며 성공 시 `200`을 반환합니다. 카탈로그와 단일 아이템 생성은 `201`입니다.

## 응답과 오류

Catalog API 응답은 다음 envelope를 사용합니다.

```json theme={null}
{
  "data": {},
  "error": null
}
```

오류가 나면 `data`는 `null`이고 `error.code`와 `error.message`가 채워집니다. 입력 검증 오류에는 잘못된 필드와 경로를 담은 `error.details`가 추가될 수 있습니다. 상태 코드별 기본 대응은 다음과 같습니다.

* `400`: 프로젝트 ID 형식, 요청 스키마나 필드 값을 고친 뒤 다시 요청합니다.
* `401`: 인증 토큰을 새로 발급받습니다.
* `403`: 프로젝트 ID와 접근 권한을 확인합니다.
* `404`: 카탈로그, 필드, 아이템 또는 셀렉션 이름을 확인합니다.
* `409`: 중복 생성이나 현재 상태와 충돌한 요청입니다. 지원되는 목록·단건 조회 또는 호출자의 체크포인트로 상태를 확인합니다.
* `413`: 4 MiB보다 작은 batch로 나눕니다.
* `500` 또는 응답을 받지 못한 timeout: `POST`는 목록·단건 조회로 반영 여부를 먼저 확인하고 빠진 리소스만 재시도합니다. 전체 값을 반복 적용해도 되는 아이템은 `PUT`을 사용하고, 재시도 간격은 지수 방식으로 늘리면서 무작위 지연을 더합니다.

<Info>
  Catalog API에는 Braze의 endpoint별 shared rate-limit bucket과 같은 별도 공개 계약이 없습니다. 대량 이관은 아이템 50개 단위 bulk API를 사용하고, 지속적인 대규모 쓰기 전에 처리량을 협의하세요.
</Info>

## 주요 제한

* 활성 카탈로그: 프로젝트당 최대 20개
* API 카탈로그 필드: `id` 포함 최대 500개
* 필드 추가: 한 요청에 최대 50개
* 아이템 쓰기·삭제: 한 요청에 최대 50개
* 아이템 하나: 최대 1 MiB
* 카탈로그 하나: 최대 2 GiB
* 활성 카탈로그 전체: 프로젝트당 최대 15 GiB
* 문자열 값: 최대 5,000자
* 배열 값: 문자열 최대 100개
* `object`·`array` 중첩: 최대 50단계
* object 필드에 저장하는 key: `.` 또는 `$` 사용 불가. 배열 필드를 `PATCH`할 때 쓰는 `$add`와 `$remove`만 예외입니다.
* 셀렉션: 카탈로그당 최대 30개, 요청당 필터 최대 4개

<CardGroup cols={2}>
  <Card title="Catalog API 작업 목록" icon="brackets-curly" href="/api-reference/카탈로그/list-api-catalogs">
    카탈로그·아이템·필드·셀렉션 endpoint의 요청과 응답 스키마를 확인합니다.
  </Card>

  <Card title="Braze에서 마이그레이션" icon="arrow-right-arrow-left" href="/ko/developer-guide/migration/braze-catalogs">
    Braze와 다른 인증, 응답, 상태 코드와 안전한 이관 절차를 확인합니다.
  </Card>
</CardGroup>
