# LLM 코딩 에이전트용 공용 API 마이그레이션 레퍼런스

> 사용자 확보 관리 API (legacy)에서 Ads Unified Platform 공용 API로 클라이언트 통합을 마이그레이션하는 LLM 코딩 에이전트에 대한 레퍼런스입니다.

> **잠재 고객:** 이 파일은 LLM 코딩 에이전트(Claude Code 또는
> similar)는 **레거시** Unity "Advertise API"에서 클라이언트 통합을 마이그레이션합니다.
> (`/advertise/v1/...`, `mz-advertiser-public-api` 서비스)에서 **새로운** Unity Ads
> **광고주** 공용 API(`/ads/advertiser/public/v1/...`). 독립형으로 분할됩니다.
> entity blocks. 각 차단은 *무엇이 바뀌었는지* 그리고 *정확히 어떻게 해야 할지* 상태를 명시합니다. 읽기
> **개념 모델 변경** 및 **전역 변경** 섹션 먼저 - 모든 차단에 적용됩니다.

## 이 파일을 사용하는 방법(에이전트 지침)##how-to-use-this-file-(instructions-for-the-agent)

1. 이 프로시가 아니라 두 계약을 진실의 근원으로 취급하십시오. 레거시 =
   `mz-advertiser-public-api/openapi/specs/advertise-v1.yml`. New =
   `ads-unified-platform/openapi/advertiser/platform-public-v1-routes.yaml`. 이 가이드에서는
   레거시 **v1**만 해당합니다. 클라이언트가 `/advertise/v2alpha1/...`를 호출하는 경우
   (`advertise-v2-alpha.yml`) 별도로 및 표면 v2 전용 사용 인간.
2. 지정된 순서로 한 번에 **한 엔티티 차단**을 마이그레이션합니다(앱 → Attribution → Creatives →
   소재 팩 → 캠페인 관리 → 캠페인 → 입찰 → 메모리 할당량 → 타게팅 → 할당). 수행
   의존하는 블록이 컴파일/패스할 때까지 차단 시작하지 않습니다. 앱 우선: 모든 것이 키에 의해
   새로운 `applicationId` 어트리뷰션은 즉시 이어집니다(앱 레벨).
   관리 룩업(`sdk-event-names`/`eligibility`)은 캠페인보다 먼저 제공되므로
   캠페인 설정
3. 레거시 기능이 **± No equivalent** 아래에 나열되면\*\*\*\*
   엔드포인트 중지하고 인간에게 열린 질문으로 표면. 가장 중요 예는
   **`DELETE app`**(애플리케이션 라이프사이클이 현재 USF에 소유됨) 및 **사용 중단 예정 리텐션
   제품 영역**(리텐션 입찰/적격) — 블록을 참조하십시오.
4. 각 차단 후 필드 테이블별로 필드 이름을 다시 매핑합니다. 필드가 이동된 것으로 간주하지 마십시오.
   표에 "unchanged"라고 표시되지 않는 한 변경되지 않습니다.
5. **돈 변경 단위**(달러 → 마이크로 달러) 및 **앱 식별자 변경 형식**
   (24-ch hex → UUID)。 이 두 가지 트랜스폼은 거의 모든 차단 터치합니다. 먼저 바로 가져옵니다

***

## 개념 모델 변경(첫 번째 읽기)##conceptual-model-change-(read-this-first)

API는 통합 플랫폼으로 다시 호스팅되었고 "캠페인 세트"(앱)에서 \*\*중심이 변경되었습니다.
플랫폼 `Application`(UUID)\*\*에 대한 24자리 Mongo ID로, 하위 리소스 읽기를
캠페인 오브젝트 5개의 개별 단가 엔드포인트가 하나로 축소되었습니다.

### 레거시 모델##legacy-model

```xml
조직
└── 앱 (campaignSetId, 24자 16진수) ← 경로 "앱"
    ├── 소재
    ├── 소재 팩
    └── 캠페인(목적: 설치 |리텐션 |roas|creativeTesting|eventOptimization)
        ├── cpi-bids | source-bids | roas-bids | 리텐션-입찰 | 이벤트-최적화-입찰
        │ (5개의 별도의 하위 리소스, 각각 GET/PUT/PATCH, 달러 문자열)
        ├── 타게팅(GET + PATCH)
        ├── 메모리 할당량(GET + PATCH, 달러, 일일 OR dailyPerCountry 맵)
        └── 소재(한 번에 하나씩 POST/DELETE)
```

### 새로운 모델##new-model

```xml
조직
└── 애플리케이션applicationId, UUID) ← 경로 "applications"
    ├── Creatives(현재 패치 + 삭제)
    ├── 소재 팩
    ├── 어트리뷰션(GET + PATCH, 앱 레벨) ← 새로운 어트리뷰션 배치
    └── 캠페인(최적화 타입: cpi|legacyRetention|roas|appEventConversion; isCreativeTesting)
        ├── bids (ONE PATCH .../bids; roas|매뉴얼 |manualSource|appEvent)
        ├── 타게팅(패치 전용)
        ├── 메모리 할당량 한도(패치만, 마이크로 달러, countryGroupDailyLimits)
        ├── 소재
        └── (입찰가 수, 메모리 할당량, 타게팅, 팩 수는 GET 캠페인에서 얻을 수 있습니까?includeFields=...)
조직
└── 캠페인(새로운 조직 전체 캠페인 목록, appIds 기준으로 필터링)
```

**아래의 모든 변환, 변형을 구동하는 이동:**

| **#** | **Shift**                       | **마이그레이션의 결과**                                                                                                                                                  |
| ----- | ------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| 1     | **`apps` → `applications`**     | 리소스 경로 세그먼트 이름이 변경되었습니다. ID는 `campaignSetId`(24자리 16진수)에서 `applicationId`(**UUID**)로 변경됩니다. 빌드 `legacy campaignSetId → new applicationId` 룩업.                   |
| 2     | **돈 → 마이크로 달러**                 | 모든 입찰/예산은 달러 문자열(`"0.201"`, `"2500.20"`)입니다. 새 API 정수 **마이크로달러**(1 USD = 1\_000\_000)입니다. 1e6로 곱합니다.                                                            |
| 3     | **5 단가 → 1**                    | `cpi/source/roas/retention/event-optimization-bids`는 입력된 바디가 있는 단일 `PATCH …/bids`로 축소됩니다. **GET bids** - `GET campaign?includeFields=bids`를 통해 읽으십시오.           |
| 4     | **하위 리소스 읽기 폴드인**               | 레거시 `GET …/budget`, `GET …/targeting`, `GET …/*-bids`, `GET …/assigned-creative-packs`는 사라졌습니다. `GET campaign?includeFields=bids,budget,creativePacks`에서 읽어보세요. |
| 5     | **`goal` → `optimizationType`** | 캠페인 유형 열거형이 변경되고 분할되었습니다(`creativeTesting`는 `isCreativeTesting` 부울이 되고, `eventOptimization`는 `appEventConversion` + `appEventType`가 됩니다).                       |
| 6     | **속성 재배치**                      | 레거시 설정합니다. 새로운 API: 캠페인 업데이트에는 `attributionLinks`가 필요하며, 전용 앱 레벨의 `GET/PATCH …/attribution`도 있습니다. 캠페인 **create는 더 이상 어트리뷰션 URL을 허용하지 않습니다**.                   |
| 7     | **할당은 목록 기반입니다.**               | 레거시 할당/할당 해제된 팩 1개 새 API는 한 호출에서 **배열**의 `creativePackIds`로 대체/제거합니다.                                                                                           |

***

## 전역 변경 사항(모든 엔드포인트에 적용)##global-changes-(apply-to-all-endpoints)

| **종횡비**             | **레거시**                                                               | **신규**                                                                                                                                                                        |
| ------------------- | --------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **호스트**             | `https://services.api.unity.com`                                      | `https://services.api.unity.com/ads/advertiser` (prod) · `https://staging.services.api.unity.com/ads/advertiser` (staging)                                                    |
| **경로 접두어**          | `/advertise/v1`                                                       | `/public/v1`                                                                                                                                                                  |
| **앱 경로 세그먼트**       | `…/organizations/{orgId}/apps/{campaignSetId}`                        | `…/organizations/{organizationId}/applications/{applicationId}`                                                                                                               |
| **전체 예제**           | `…/advertise/v1/organizations/{orgId}/apps/{campaignSetId}/campaigns` | `…/ads/advertiser/public/v1/organizations/{orgId}/applications/{applicationId}/campaigns`                                                                                     |
| **Auth**            | HTTP **Basic**(서비스 계정 키)                                              | HTTP **Basic**은 여전히 작동하며 Bearer JWT(`unity`/`ads` 발급자)도 허용됩니다. 자격 증명이 변경되지 않았지만 **필수 역할/권한이 다릅니다** — 아래의 "권한 부여, 인증"을 참조하십시오.                                                 |
| **ORG ID**          | 경로에 있는 `organizationId`(int64)                                        | 경로에 있는 `organizationId`(창세기 ID, 숫자) — **변경되지 않음**                                                                                                                             |
| **앱 ID(타입 변경)**     | `campaignSetId` — 24자 육각형 (`^[0-9a-fA-F]{24}$`)                       | `applicationId` — **UUID**                                                                                                                                                    |
| **Money**           | 달러 소수점 문자열(`"2500.20"` 단가 `"0.201"`)                                  | **마이크로 달러**입니다. 1 USD = 1\_000\_000                                                                                                                                           |
| **캠페인 / 소재 / 팩 ID** | 24자 육각형 — **변경되지 않은** (아직 `^[a-f0-9]{24}$`)                           | 24자 16진수 — **변경되지 않음**                                                                                                                                                        |
| **리스트 엔벨로프**        | 모든 곳에서 `{ total, offset, limit, results[] }`                          | ⚡️ **비일관성:** 캠페인 및 소재 사용 `{ items[], meta:{ total, limit, offset } }`; 소재 팩 사용 `{ total, offset, limit, results[] }`; **애플리케이션은 베어 JSON 배열을 반환**(엔벨로프, 페이지 지정 없음). 엔드포인트별 파싱. |
| **파이닝 파라미터**        | `offset`(0), `limit`(최대 1000, 디폴트 1000) — 모든 목록                       | `offset`(0), `limit`(기본값 **100**)입니다. **`GET …/applications`는 둘 다 사용하지 않습니다.** - 여기서 파라미터를 놓으십시오(차단 A)                                                                        |

### 권한 부여, 인증역할/권한) — 403이 버그 가정하기 전에 읽어야 합니다.##authorization-(roles/permissions)--read-before-assuming-403-is-a-bug

Authentication은 변경되지 않았습니다(기본 자격 증명과 동일함), 하지만 **권한 부여, 인증은 없습니다**.
엔드포인트는 게이트웨이 체크 표시된 권한 문자열로 게이트됩니다(새로운
`x-unity-gateway.authorization.rules`의 사양). 광고주 경로에 `advertise.*` 필요
사용 권한(`advertise.campaigns.*`, `advertise.creatives.*`, `advertise.creative_packs.*`,
`advertise.bids.update`, `advertise.targeting.update`, `advertise.apps.*`, ...),
**Unity Cloud 대시보드의 org 범위 서비스 계정 역할**(예: "광고 API 앱
에디터" grants `advertise.apps.get/list/create/update/delete`). 공유 `…/applications`
엔드포인트는 `advertise.apps.*` 또는 퍼블리셔 측의 `monetize.projects.*`를 허용합니다.

**에이전트 지침:**

1. 마이그레이션하기 전에 사용자에게 클라이언트 서비스 계정이 필요한 를 보유하고 있는지 확인하도록 요청합니다.
   새로운 API에 대한 광고주 역할 — 레거시 API 자금이 전달되는 것으로 간주 **하지 않음**.
2. 레거시 API에서 작동한 요청이 새 API에서 동일한 값으로 **403을** 반환하는 경우
   자격 증명, 요청 버그가 아니라 **누락된 역할 부여**로 취급합니다. 중지하고 표면;
   "수정"을 시도하는 요청을 수정하지 않습니다.

### 속도 제한(클라이언트 측 백오프 업데이트)##rate-limits-(update-client-side-backoff)

| **운영 클래스**             | **레거시**      | **신규**                                              |
| ---------------------- | ------------ | --------------------------------------------------- |
| 읽기(`GET`)              | 429 정의됨, 자원당 | **20/s + 8000/hour** per resource limiter           |
| 생성(`POST`)             | (batched)    | **1/s + 60/hour** (`advertiseCreate`)               |
| 업데이트(`PATCH`/`DELETE`) | (batched)    | **1/s + 200/hour** (`advertiseModify`)              |
| 입찰 업데이트                | (배칭 배열)      | **20/s + 12000/hour**(`advertiseBids`) + 수정 계층      |
| 캠페인 관리 읽기              | —            | **6/s + 8000/hour** (`advertiseCampaignManagement`) |

***

## 마스터 엔드포인트 맵##master-endpoint-map

| **레거시 작업**                                  | **레거시 경로**                                                 | **New operationId**                                                      | **새로운 경로**                                                       | **차단** |
| ------------------------------------------- | ---------------------------------------------------------- | ------------------------------------------------------------------------ | ---------------------------------------------------------------- | ------ |
| `advertise-listApps`                        | `GET …/apps`                                               | `publicOrganizationApplications`                                         | `GET …/applications`                                             | A      |
| `advertise-createApp`                       | `POST …/apps`                                              | `publicCreateApplication`                                                | `POST …/applications`                                            | A      |
| `advertise-getApp`                          | `GET …/apps/{campaignSetId}`                               | `publicGetApplicationById`                                               | `GET …/applications/{applicationId}`                             | A      |
| `advertise-updateApp`                       | `PATCH …/apps/{campaignSetId}`                             | `publicUpdateApplication`                                                | `PATCH …/applications/{applicationId}`                           | A      |
| `advertise-deleteApp`                       | `DELETE …/apps/{campaignSetId}`                            | —                                                                        | 삭제 애플리케이션 엔드포인트가 보고되지 않은                                         | A      |
| `advertise-listCreatives`                   | `GET …/apps/{id}/creatives`                                | `publicGetCreatives`                                                     | `GET …/applications/{id}/creatives`                              | B      |
| `advertise-createCreative`                  | `POST …/apps/{id}/creatives`(여러 부분)                        | `publicCreateCreative`                                                   | `POST …/applications/{id}/creatives`(여러 부분)                      | B      |
| `advertise-getCreative`                     | `GET …/creatives/{creativeId}`                             | `publicGetCreativeById`                                                  | `GET …/applications/{id}/creatives/{creativeId}`                 | B      |
| —                                           | —                                                          | `publicUpdateCreativeById`                                               | `PATCH …/creatives/{creativeId}`(새로운: 이름 변경)                     | B      |
| —                                           | —                                                          | `publicDeleteCreativeById`                                               | `DELETE …/creatives/{creativeId}`(신규)                            | B      |
| `advertise-listCreativePacks`               | `GET …/creative-packs`                                     | `publicGetCreativePacks`                                                 | `GET …/applications/{id}/creative-packs`                         | C      |
| `advertise-createCreativePack`              | `POST …/creative-packs`                                    | `publicCreateCreativePack`                                               | `POST …/applications/{id}/creative-packs`                        | C      |
| `advertise-getCreativePack`                 | `GET …/creative-packs/{packId}`                            | `publicGetCreativePackById`                                              | `GET …/applications/{id}/creative-packs/{creativePackId}`        | C      |
| `advertise-updateCreativePack`              | `PATCH …/creative-packs/{packId}`                          | `publicUpdateCreativePack`                                               | `PATCH …/creative-packs/{creativePackId}`                        | C      |
| `advertise-deleteCreativePack`              | `DELETE …/creative-packs/{packId}`                         | `publicDeleteCreativePack`                                               | `DELETE …/creative-packs/{creativePackId}`                       | C      |
| `advertise-listCampaigns`(앱당)               | `GET …/apps/{id}/campaigns`                                | `publicGetCampaigns` (organ-wide)                                        | `GET …/organizations/{orgId}/campaigns?appIds={applicationId}`   | D      |
| `advertise-createCampaign`                  | `POST …/apps/{id}/campaigns`                               | `publicCreateCampaign`                                                   | `POST …/applications/{id}/campaigns`                             | D      |
| `advertise-getCampaign`                     | `GET …/campaigns/{campaignId}`                             | `publicGetCampaignById`                                                  | `GET …/applications/{id}/campaigns/{campaignId}?includeFields=…` | D      |
| `advertise-updateCampaign`                  | `PATCH …/campaigns/{campaignId}`                           | `publicUpdateCampaign`                                                   | `PATCH …/applications/{id}/campaigns/{campaignId}`               | D      |
| `advertise-deleteCampaign`                  | `DELETE …/campaigns/{campaignId}`                          | `publicDeleteCampaignById`                                               | `DELETE …/applications/{id}/campaigns/{campaignId}`              | D      |
| `advertise-list/replace/patchCpiBids`       | `GET/PUT/PATCH …/campaigns/{id}/cpi-bids`                  | `publicUpdateBids` (`manualBids`)                                        | `PATCH …/campaigns/{campaignId}/bids`                            | E      |
| …SourceBids                                 | `…/source-bids`                                            | `publicUpdateBids` (`manualSourceBids`)                                  | `PATCH …/campaigns/{campaignId}/bids`                            | E      |
| …RoasBids                                   | `…/roas-bids`                                              | `publicUpdateBids` (`roasBids`)                                          | `PATCH …/campaigns/{campaignId}/bids`                            | E      |
| …EventOptimizationBids                      | `…/event-optimization-bids`                                | `publicUpdateBids` (`appEventBids`)                                      | `PATCH …/campaigns/{campaignId}/bids`                            | E      |
| …RetentionBids                              | `…/retention-bids`                                         | —                                                                        |  리텐션 입찰 - 더 이상 지원되지 않음                                           | E      |
| `advertise-getBudget`                       | `GET …/campaigns/{id}/budget`                              | — (캠페인을 통해 읽음)                                                           | `GET campaign?includeFields=budget`                              | F      |
| `advertise-updateBudget`                    | `PATCH …/campaigns/{id}/budget`                            | `publicPatchCampaignBudgetCap`                                           | `PATCH …/campaigns/{campaignId}/budget-cap`                      | F      |
| `advertise-getTargeting`                    | `GET …/campaigns/{id}/targeting`                           | — (캠페인을 통해 읽음)                                                           | `GET campaign`(응답의 대상 필드)                                        | G      |
| `advertise-updateTargeting`                 | `PATCH …/campaigns/{id}/targeting`                         | `publicPatchCampaignTargeting`                                           | `PATCH …/campaigns/{campaignId}/targeting`                       | G      |
| `advertise-listAssignedCreativePacks`       | `GET …/campaigns/{id}/assigned-creative-packs`             | — (캠페인을 통해 읽음)                                                           | `GET campaign?includeFields=creativePacks`                       | H      |
| `advertise-assignCreativePack`(한)           | `POST …/assigned-creative-packs`                           | `publicAssignCreativePacks` (list)                                       | `PATCH …/campaigns/{campaignId}/assign-creative-pack`            | H      |
| `advertise-unassignCreativePack`(one)       | `DELETE …/assigned-creative-packs/{id}`                    | `publicUnassignCreativePacks` (list)                                     | `DELETE …/campaigns/{campaignId}/unassign-creative-pack`         | H      |
| `advertise-listSdkEventNames`               | `GET …/audience-pinpointer/sdk-event-names`                | `publicGetSdkEventNames`                                                 | `GET …/applications/{id}/sdk-event-names`                        | I      |
| `advertise-listEventOptInfo`                | `GET …/audience-pinpointer/event-optimization-info`        | `publicGetCampaignManagementEligibility`                                 | `GET …/applications/{id}/eligibility`                            | I      |
| `advertise-listRoasInfo` / `…RetentionInfo` | `GET …/audience-pinpointer/roas-info` / `…/retention-info` | —                                                                        |  ROAS 지원이 더 이상 필요하지 않음, 리텐션 사용 중단 예정                             | I      |
| — (전화/앱에서 할당됨)                              | (`attributionClickUrl`/`attributionStartUrl`)              | `publicGetApplicationAttribution` / `publicUpdateApplicationAttribution` | `GET/PATCH …/applications/{id}/attribution`                      | J      |

***

## 차단 A — 앱 → 응용 프로그램##block-a--apps--applications

### 변경한 사항##what-changed

* 경로 세그먼트 `apps` → `applications`; id `campaignSetId`(24-char hex) → `applicationId`
  (**UUID**). 이는 모든 캠페인/머티리얼/팩 호출이 중첩된 **위험이 가장 높은 리맵**입니다.
  밑 마이그레이션 중에 `legacy campaignSetId → new applicationId` 맵을 빌드하고 유지합니다.
* `platform`는 이제 Create에 대해 **필수적이고 명시적** 필드입니다(`store`에 의해 암시됨).
* 앱 레벨 어트리뷰션 URL(`appAttributionClickUrl`/`appAttributionStartUrl`)이
  전용 어트리뷰션 엔드포인트 — 차단 J 를 참조하십시오.
* **`DELETE app`는 동등하지 않음**(의도적으로 확인됨) 애플리케이션 라이프사이클이 밖으로 이동
  광고주가 Unity 서비스 재단(USF)에 대한 범위와 현재 애플리케이션 삭제가
  비활성화된 플랫폼 전체. 새 애플리케이션 리소스는 리스트/생성/받기/업데이트만 노출합니다. 이 경우
  클라이언트 API 통해 앱 삭제하고 중지하고 표면 표시합니다.
* **리스트가 모양을 변경하고 페이지 지정 / 필터를 잃었습니다.** 레거시 `GET …/apps` 반환됨
  `{ total, offset, limit, results[] }` with `offset`/`limit` and `filter[store]`/`filter[storeId]`
  쿼리 파라미터. 새로운 `GET …/applications`는 모든 조직의 **빈 JSON 배열**을 반환합니다.
  애플리케이션 — 엔벨로프, 페이지 지정, 필터 없음 파라미터를 놓고 필터링/페이지 지정
  client-side.

### 필드 매핑 — create (`advertise-createApp` → `CreateApplicationPublicDto`)##field-mapping--create-(advertise-createapp--createapplicationpublicdto)

| **레거시 필드**                                      | **새 필드**                                 | **참고**                                                                                 |
| ----------------------------------------------- | ---------------------------------------- | -------------------------------------------------------------------------------------- |
| `name`(필수)                                      | `name` (필수)                              | 변경되지 않음                                                                                |
| `store` (`apple`/`google`/`standalone_android`) | `store` + `platform`                     | **Remap 열거형** → `AppleAppStore`/`GooglePlay`/... **및**`platform`(`iOS`/`Android`/...). |
| `storeId` (필수)                                  | `storeId`(선택 사항)                         | 변경되지 않은 값                                                                              |
| —                                               | `platform` (**필수**, 열거형)                 | **신규, 필수.** 레거시 `store`에서 파생됩니다.                                                       |
| `adomain`                                       | —                                        | 는 앱 응답에 필드가 없습니다. 표면(클라이언트가 의존한 경우)입니다.                                                |
| `gameId` (정수)                                   | `gameId` (정수, nullable)                  | **리스폰스**에 존재합니다. 생성 입력이 아니라 서버 측으로 설정합니다.                                              |
| `appAttributionClickUrl`                        | → 어트리뷰션 `clickUrl`                       | **이동.** 생성 후 `PATCH …/attribution`를 통해 설정합니다(차단 J).                                    |
| `appAttributionStartUrl`                        | → 어트리뷰션 `startUrl`                       | **이동.**`PATCH …/attribution`(차단 J)를 통해 설정합니다.                                          |
| —                                               | `privacy.coppa`, `privacy.mixedAudience` | 새로운 선택적 개인정보 보호 오브젝트                                                                   |
| —                                               | `projectId` / `projectName`              | 새로운 선택적 그룹화(공유 퍼블리셔/광고주 앱 모델)                                                          |

새로운 `store` 열거형: `GooglePlay`, `AppleAppStore`, `SamsungGalaxy`, `AmazonAppStore`, `MacAppStore`,
`UDP`, `MicrosoftStore`, `HuaweiAppGallery`, `APK`. 새로운 `platform` 열거형: `Android`, `iOS`, `OSX`,
`Windows`, `Linux`, `WebGL`, `Windows_Store`, `PS4`, `PS5`, `XboxOne`, `tvOS`, `Switch`, `VisionOS`.

레거시 `store` → 새로운 `{store, platform}` (확인): `apple` → `{AppleAppStore, iOS}`; `google` →
`{GooglePlay, Android}`; `standalone_android` → 실제 스토어에 따라 달라집니다 — Samsung →
`{SamsungGalaxy, Android}`, Huawei → `{HuaweiAppGallery, Android}`; 스토어를 식별할 수 없을 때
플랫폼은 그것을 `{APK, Android}`에 매핑합니다.

### 필드 매핑 — 응답(`app` → `ApplicationPublicResponseDto`)##field-mapping--response-(app--applicationpublicresponsedto)

| **레거시 필드**                          | **새 필드**                                                                                 | **참고**                |
| ----------------------------------- | ---------------------------------------------------------------------------------------- | --------------------- |
| `id`(24-char hex, `campaignSetId`)  | `id`(**UUID**)                                                                           | **타입 변경.** 매핑을 유지합니다. |
| `name`                              | `name`                                                                                   | 변경되지 않은               |
| `store` / `storeId`                 | `store` / `storeId`                                                                      | 열거형 리맵(위)             |
| `gameId`                            | `gameId`                                                                                 | 변경되지 않음(정수, nullable) |
| `appAttributionClickUrl`/`StartUrl` | — (참고 `…/attribution`)                                                                   | 이동됨                   |
| `adomain`                           | —                                                                                        |  almost gone          |
| `createdAt` / `updatedAt`           | — (DTO에서 제외)                                                                             | 노출되지 않음               |
| —                                   | `platform`, `iconUrl`, `projectId`, `testMode`, `kidsSettings`, `coppa`, `mixedAudience` | new                   |

> ⚡️ **ID 다시 매핑은 가장 높은 위험 단계입니다.** 지속 `legacy campaignSetId → new {applicationId}`
> 그리고 `campaignSetId`를 사용한 모든 다운스트림 경로를 다시 작성합니다.

***

## 차단 B — 소재##block-b--creatives

### 변경한 사항##what-changed

* `apps/{campaignSetId}` 대신 `applications/{applicationId}` 아래에 중첩되었습니다.
* 여전히 `multipart/form-data`, `creativeInfo` JSON 먼저 바이너리 파일 필드.
* **새로운 운영:** `PATCH …/creatives/{creativeId}`(이름 변경) 및 `DELETE …/creatives/{creativeId}`
* **`type` 열거형 값의 이름이 변경되었습니다** (snake\_case). 다시 맵.
* 이미지 업로드 한도는 **20MB**(5MB)로, 플레이어블은 **10MB**로 증가했습니다.
* **비디오 업로드(`videoFile`)는 레거시**와 동일하게 작동합니다(API 소유자에 의해 확인). OpenAPI
  2026년 7월 말에 출시된 비디오 제작 배리언트를 문서화하는 스키마입니다.
  사양에 비디오 `oneOf` 배리언트가 없습니다. 비디오가 지원되지 않는다고 가정하는 대신 사양을 새로 고칩니다.

### `type` 열거형 리맵##type-enum-remap

| **레거시 `type`**       | **새로운 `type`**               |
| -------------------- | ---------------------------- |
| `squareEndCard`      | `end_card_square`            |
| `endCardPair`        | `end_card_pair`              |
| `portraitVideo`      | `video_portrait`             |
| `landscapeVideo`     | `video_landscape`            |
| `responsivePlayable` | `playable_responsive`        |
| `portraitPlayable`   | `playable_portrait`          |
| `landscapePlayable`  | `playable_landscape`         |
| `squareVideo`        |  — 세로/랜드스케이프 비디오만 존재합니다. 표면. |
| —                    | `banner`(새로운)                |

### 열거형 `status`##status-enum

변경되지 않은 세트: `uploaded`, `processing`, `processingFailed`, `pendingModeration`, `approved`,
`rejected`.

### 다중 부분 필드 이름##multipart-field-names

`creativeInfo`(JSON, first) +: `squareEndCardFile`, `portraitEndCardFile` +
`landscapeEndCardFile` 또는 `playableFile`. `creativeInfo` 하위 객체 파일 레퍼런스
`fileName`; 플레이어블에는 `orientation`(`landscape`/`portrait`/`both`)도 포함됩니다. 응답은
`CreativePublicResponseDto` (`id`, `name`, `language`, `type`, `status`, `files[]{name,url}`,
`createdAt`, `updatedAt`).

***

## 차단 C — 소재 팩##block-c--creative-packs

### 변경한 사항##what-changed

* `applications/{applicationId}` 아래에 중첩되었습니다. CRUD 동사(GET/POST/GET/PATCH/DELETE)
* `type`는 `video`, `playable`, `video+playable`, `banner`의 `banner` 값을 얻습니다.
* **리스트 엔벨로프는 레거시 스타일**의 `{ total, offset, limit, results[] }`입니다(캠페인/크리에이티브와는 달리).
* 더 풍부한 응답: 추가 `applicationId`, `moderationStatus` (`pending`/`approved`/`rejected`,
  nullable), `ageRating` (nullable), `assignedCampaignIds[]`, `createdAt`/`updatedAt`

### 필드 매핑 — create (`createCreativePack` → `CreateCreativePackPublicDto`)##field-mapping--create-(createcreativepack--createcreativepackpublicdto)

| **레거시 필드**            | **새 필드**              | **참고**                   |
| --------------------- | --------------------- | ------------------------ |
| `name`(필수)            | `name` (필수)           | 변경되지 않음                  |
| `creativeIds[]` (1–3) | `creativeIds[]`       | 변경되지 않음 (24자리 육각형 소재 ID) |
| `type`                | `type`                | 같은 값 + 새로운 `banner`      |
| `androidStoreListing` | `androidStoreListing` | 변경되지 않음(nullable)        |
| `appleProductPageId`  | `appleProductPageId`  | 변경되지 않음(UUID, nullable)  |

### 필드 매핑 — 응답##field-mapping--response

| **레거시 필드**                    | **새 필드**                                                                   | **참고**            |
| ----------------------------- | -------------------------------------------------------------------------- | ----------------- |
| `id`                          | `id`                                                                       | 24자 육각형 — 변경되지 않음 |
| `name`, `type`, `creativeIds` | same                                                                       | 변경되지 않은           |
| `campaignIds`                 | `assignedCampaignIds`                                                      | **renamed**       |
| —                             | `applicationId`, `moderationStatus`, `ageRating`, `createdAt`, `updatedAt` | new               |

***

## 차단 D — 캠페인##block-d--campaigns

### 변경한 사항##what-changed

* `applications/{applicationId}` 아래에 중첩되었습니다. Get/Update/Delete는 앱별이며, **리스트는 현재
  org-wide** (`GET …/organizations/{orgId}/campaigns`, 필터 `appIds`) — 앱별 목록이 없습니다.
* **`goal` → `optimizationType`** (+ `isCreativeTesting` 부울 + `appEventType`).
* **애트리뷰션 URL은 더 이상 캠페인 생성의 일부가 아닙니다.** 캠페인을 통해 생성 후 설정
  `PATCH`(`attributionLinks`) 또는 앱 어트리뷰션 엔드포인트(차단 J).
* **`scheduleEnd`는 생성 필드**가 아닙니다. 생성은 `start`만 사용합니다. `PATCH`를 통해 `end`를 설정합니다.
* `GET campaign`는 `includeFields`(`bids`, `budget`, `creativePacks`, `application`)를 지원합니다.
  는 현재 입찰/메모리 할당량/할당 팩을 읽는 방법입니다.
* `postInstallWindow` 값은 **대문자와 확장**: `D0`,`D1`,`D3`,`D7``D14`,`D28`

### `goal` → `optimizationType` remap##goal--optimizationtype-remap

| **레거시 `goal`**      | **신규**                                                                                                                           |
| ------------------- | -------------------------------------------------------------------------------------------------------------------------------- |
| `installs`          | `optimizationType: cpi`                                                                                                          |
| `retention`         | `optimizationType: legacyRetention` — ⚡️ 리텐션 사용 중단 예정: 입찰/적격은 지원되지 않습니다. 새 리텐션 캠페인을 생성하지 마십시오. 표면 활성 캠페인을 생성하십시오.                |
| `roas`              | `optimizationType: roas` (+ `roasType`)                                                                                          |
| `eventOptimization` | `optimizationType: appEventConversion` (+ `appEventType`)                                                                        |
| `creativeTesting`   | `isCreativeTesting: true` + 지지대 **매뉴얼** 최적화(API 소유자별로 현재 지원되는 유일한 지지대입니다. 즉 매뉴얼 CPI: `optimizationType: cpi`, `cpiType: manual`) |

### 필드 매핑 — create (`createCampaign` → `CreateCampaignDto`)##field-mapping--create-(createcampaign--createcampaigndto)

| **레거시 `goal`**      | **신규**                                                                                                                           |
| ------------------- | -------------------------------------------------------------------------------------------------------------------------------- |
| `installs`          | `optimizationType: cpi`                                                                                                          |
| `retention`         | `optimizationType: legacyRetention` — ⚡️ 리텐션 사용 중단 예정: 입찰/적격은 지원되지 않습니다. 새 리텐션 캠페인을 생성하지 마십시오. 표면 활성 캠페인을 생성하십시오.                |
| `roas`              | `optimizationType: roas` (+ `roasType`)                                                                                          |
| `eventOptimization` | `optimizationType: appEventConversion` (+ `appEventType`)                                                                        |
| `creativeTesting`   | `isCreativeTesting: true` + 지지대 **매뉴얼** 최적화(API 소유자별로 현재 지원되는 유일한 지지대입니다. 즉 매뉴얼 CPI: `optimizationType: cpi`, `cpiType: manual`) |

### 필드 매핑 — 업데이트(`updateCampaign` → `UpdateCampaignPublicDto`)##field-mapping--update-(updatecampaign--updatecampaignpublicdto)

| **레거시 필드**                       | **새 필드**                                       | **참고**                                   |
| -------------------------------- | ---------------------------------------------- | ---------------------------------------- |
| `name`                           | `name`                                         | 변경되지 않음                                  |
| `scheduleStart`/`scheduleEnd`    | `start` / `end`                                | 날짜 → 날짜 시간; `end` nullable               |
| `enabled` (부울)                   | `paused` (부울)                                  | **역감** — `enabled:true` ≈ `paused:false` |
| `autoStart`                      | `autoStart` (`ENABLED`/`DISABLED`)             | 위문자                                      |
| `attributionClickUrl`/`StartUrl` | `attributionLinks.{clickUrl,startUrl,viewUrl}` | 현재 중첩된 오브젝트입니다. `viewUrl`는 새로운           |

> ⚡️ **업데이트 응답은 더 얇은 오브젝트입니다** (`CampaignPublicDetailsResponseDto`: `id`,
> `name`, `start`, `end`, `autoStart`, `paused`, `attributionLinks`), 전체 캠페인이 **아닙니다**.
> 클라이언트가 PATCH 응답에서 다른 캠페인 필드를 읽으면 `GET campaign`를 통해 다시 페치합니다.

### 필드 매핑 — 응답(레거시 `campaign` → `CampaignPublicResponseDto`)##field-mapping--response-(legacy-campaign--campaignpublicresponsedto)

| **레거시 필드**                                                              | **새 필드**                                                                                                         | **참고**                                                                          |
| ----------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------- |
| `id`                                                                    | `id`                                                                                                             | 24자 육각형 — 변경되지 않음                                                               |
| `name`, `billingType`                                                   | `name`, `billingType`                                                                                            | 변경되지 않은                                                                         |
| `goal`                                                                  | `optimizationType` (+ `isCreativeTesting`, `appEventType`, `roasType`)                                           | 위의 리맵 표(리버스 방향)                                                                 |
| `enabled` (부울)                                                          | `paused` (부울)                                                                                                    | ⚡️ **reads too에 반전된 의미** — `enabled:true` ≈ `paused:false`                      |
| `status` (`live`/`learning`/`paused`ROAS)                               | —                                                                                                                | 가 보고하지 않았습니다. 표면에 클라이언트가 브랜치하는 경우.                                              |
| `scheduleStart` / `scheduleEnd`                                         | `start` / `end`                                                                                                  | date → datetime; `end` nullable                                                 |
| `biddingStrategy` (`manual`/`automated`)                                | `bidConfigurationMethod` (`manual`/`auto`)                                                                       | 이름 변경 + 값 변경(`automated` → `auto`)                                              |
| `cpiBids[]` / `sourceBids[]` / `roasBids[]` / `eventOptimizationBids[]` | `manualBids[]` / `manualSourceBids[]` / `roasBids[]` / `appEventBids[]`                                          | 이름 변경; **`includeFields=bids`에만 표시**; 마이크로 달러 단위 값                              |
| `retentionBids[]`                                                       | —                                                                                                                |  사용 중단 예정\*\* — 쓰기 지원 없음, 읽기 값은 레거시 전용으로 취급                                     |
| `budget`                                                                | `budget`                                                                                                         | **`includeFields=budget`에만 해당**; 마이크로 달러 + `campaignLimitType`/`dailyLimitType` |
| `eventOptimizationType` / `sdkEventName`                                | `appEventType` / `sdkEventNames[]`                                                                               | 열거형 이름 변경, 단일 → 배열                                                              |
| `autoStart` (`enabled`/`disabled`/`canceled`)                           | `autoStart` (`ENABLED`/`DISABLED`/`CANCELED_REJECTED`/`CANCELED_LESS_THAN_TWO_PACK`)                             | 상주; `canceled` 두 가지 이유로 분할됨                                                     |
| `createdAt` / `updatedAt`                                               | `createdAt` / `updatedAt`                                                                                        | 변경되지 않은                                                                         |
| —                                                                       | `applicationId`, `attribution`, `deviceTargeting`, `budgetPacingType`, `forcePausedEnabled`, `isAutoEligible`, … | 새로운 — 새로운 사양을 참조하십시오                                                            |

### `includeFields`를 통한 읽기##reads-via-includefields

캠페인의 입찰/메모리 할당량/할당 팩에 대한 레거시 읽기에는 별도의 GET이 사용되었습니다. 지금:
`GET …/campaigns/{campaignId}?includeFields=bids,budget,creativePacks,application`. 단가 배열,
캠페인 오브젝트에 `budget`, `creativePackIds[]` 및 전체 `application`가 반환됩니다.

***

## 차단 E — 입찰(단계점 5개 → 1개)##block-e--bids-(five-endpoints--one)

### 변경한 사항##what-changed

레거시 하위 리소스 5개(`cpi-bids`, `source-bids`, `roas-bids`, `retention-bids`,
`event-optimization-bids`), 각각 `GET`/`PUT`/`PATCH` over **dollar strings**, collapse into a
표준 바디 위의 단일 **`PATCH …/campaigns/{campaignId}/bids`**(**마이크로 달러**) **이건
no GET** — `GET campaign?includeFields=bids`를 통해 현재 입찰을 확인하십시오.

### 엔드포인트 + 바디 리맵##endpoint-+-body-remap

| **레거시 하위 리소스**            | **`UpdateCampaignPublicBidDto`의 새로운 바디 필드** | **엔트리당 모양(신규)**                                                                                    |
| ------------------------- | ------------------------------------------- | -------------------------------------------------------------------------------------------------- |
| `cpi-bids`                | `manualBids[]`                              | `{ country, bid }`(`bid`(마이크로)                                                                     |
| `source-bids`             | `manualSourceBids[]`                        | `{ country, sourceAppId, bid }`(마이크로)                                                              |
| `roas-bids`               | `roasBids[]`                                | `{ country, target, maxBid? }` (`maxBid` 마이크로)                                                     |
| `event-optimization-bids` | `appEventBids[]`                            | `{ country, bid }`(마이크로)                                                                           |
| `retention-bids`          | **지원 중단 예정 — 지원 안 함**                       | 레거시 `{country, baseBid, maxBid}`에는 후속자가 없습니다(확인됨). 마이그레이션하지 마십시오. 클라이언트 리텐션 단가 사용을 사람에게 표면 표시하십시오. |

### 재화 변환하다(동사), 트랜스폼(명사)중요)##money-transform-(critical)

레거시 달러 문자열 → 새로운 마이크로 달러: 1\_000\_000을 곱합니다. `"0.201"` → `201000`. ROAS
`goal`/`target` (확인): 레거시 ROAS `goal`는 **백분율 문자열**(`"12.34"` = 12.34%)입니다.
새로운 `target`는 100으로 나누는 **소수점 비율**입니다(`"12.34"` → `0.1234`; 10% → `0.10`).
시맨틱(API 소유자 확인):

| **레거시 PUT/PATCH 모델**            | **새로운 모델**                                                             |
| ------------------------------- | ---------------------------------------------------------------------- |
| `PUT`는 전체 세트를 대체하고, `PATCH`는 병합 | 단일 `PATCH`는 주어진 유형의 전체 단가 세트를 \*\*교체합니다. 병합은 없습니다. 항상 원하는 전체 세트를 보냅니다. |

**지급 단가**(확인): `PATCH`의 해당 단가 유형에 대한 빈 배열 `[]`를 전송합니다.
레거시 `PUT []` 효과

> ⚡️ 새로운 `PATCH`가 병합이 아니라 교체하기 때문에 레거시 `PATCH`의 순진한 포팅 (병합)
> 콜은 요청에 포함되지 않은 모든 단가를 **침묵적으로 내립니다**. 전환 레거시 병합 스타일
> 전체 쓰기 업데이트: `GET campaign?includeFields=bids`를 통해 현재 단가를 읽고,
> 델타로 로컬에서전체 배열 전송합니다

***

## 차단 F — 메모리 할당량 → 메모리 할당량 한도##block-f--budget--budget-cap

### 변경한 사항##what-changed

* `GET …/budget`가 사라졌습니다 → `GET campaign?includeFields=budget`를 통해 읽습니다.
* `PATCH …/budget` → **`PATCH …/campaigns/{campaignId}/budget-cap`**.
* **달러 → 마이크로 달러** (문자열).
* **국가별 맵**(`dailyPerCountry: { "US": "123.45", … }`)은
  **명명된 국가 그룹**(`countryGroupDailyLimits: [{ name, countries[], limit }]`).
* 응답에 대한 새로운 명시적 유형의 차별 요소: `campaignLimitType` (`unlimited`/`total`),
  `dailyLimitType` (`daily`/`countryDaily`/`unlimited`).

### 필드 매핑(`budget` → `PatchBudgetCapDto`)##field-mapping-(budget--patchbudgetcapdto)

| **레거시 필드**                     | **새 필드**                    | **참고**                                                                                                                        |
| ------------------------------ | --------------------------- | ----------------------------------------------------------------------------------------------------------------------------- |
| `total` (달러 문자열)               | `total`(**마이크로** 문자열)       | ×1e6. 한도 표현 시맨틱은 레거시(API 소유자별): `"0"`는 여전히 "한도 없음"을 의미합니다.                                                                    |
| `daily` (달러 문자열)               | `daily`(**마이크로** 문자열)       | ×1e6                                                                                                                          |
| `dailyPerCountry`(맵)           | `countryGroupDailyLimits[]` | **재구성:** 각 `{country: amount}`는 그룹 `{ name, countries:[country], limit: amount×1e6 }`가 됩니다. 클라이언트가 선호하는 그룹입니다. `name`이 필요합니다. |
| `dailySpent` / `spent` (읽기 전용) | — (캠페인 메모리 할당량 참조)          | 패치 리스폰스가                                                                                                                      |

***

## 차단 G — 타게팅##block-g--targeting

### 변경한 사항##what-changed

* `GET …/targeting` is gone → `GET campaign`에서 타게팅 필드를 읽습니다.
* `PATCH …/targeting`가 남았지만 **바디가 재구성되었습니다** 대부분의 레거시 기기 필드는
  내려갔습니다 (그들은 이미 레거시 사양의 `deprecated`이었습니다).

### 필드 매핑(`targetingOptions` → `UpdateCampaignPublicTargetingDto`)##field-mapping-(targetingoptions--updatecampaignpublictargetingdto)

| **레거시 필드**                                                                                      | **새 필드**                                                                                  | **참고**                                                                        |
| ----------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------- |
| `appTargeting.allowList` / `blockList`                                                          | `allowlistSources[]` / `blocklistSources[]`                                               | 이름 변경(소스 앱 ID)                                                                |
| `categoryTargeting.allowList` / `blockList`                                                     | `apptopiaSourceCategoriesAllowlist/Blocklist[]` (+ `sensorTower…`)                        | 현재 공급자별 카테고리 목록                                                               |
| `deviceTargeting` iOS `blockedDevices` / `osMin`                                                | `appleDevice.deviceBlocklist[]` / `appleDevice.osMinVersion`                              | restructured                                                                  |
| `deviceTargeting` Android `osMin` + 제조사/모델                                                      | `androidDevice.{manufacturerAllowlist,manufacturerBlocklist,modelBlocklist,osMinVersion}` | restructured                                                                  |
| `regionalTargeting`                                                                             | `allowlistCountrySubdivisions[] {country, subdivisions[]}`                                | 현재 subdivision-id 기반                                                          |
| `limitedAdTracking`, `connectionType`, `screenSize`, `screenDensity`, `allowedDevices`, `osMax` | —                                                                                         | 가 **제거됨** 보고됨(확인됨 - 통합에서 구성할 수 없음). 이를 설정하는 클라이언트 코드를 삭제하고 휴먼에게 해당 드롭을 언급합니다. |

***

## 차단 H — 할당된 소재 팩(현재 목록 기반)##block-h--assigned-creative-packs-(now-list-based)

### 변경한 사항##what-changed

* 레거시 할당/할당되지 않은 요청당 **한 팩**(`POST`/`DELETE …/assigned-creative-packs`).
* 새 API는 다음과 같은 **리스트**에서 작동합니다.
  * `{ creativePackIds: [...] }`가 있는 `PATCH …/campaigns/{campaignId}/assign-creative-pack` —
    는 할당된 세트를 제공된 ID로 대체합니다.
  * `{ creativePackIds: [...] }`가 있는 `DELETE …/campaigns/{campaignId}/unassign-creative-pack` —
    는 나열된 ID를 제거합니다.
* 현재 할당된 팩 목록: `GET campaign?includeFields=creativePacks`(`creativePackIds[]`).

### 마이그레이션 방식##migration-approach

클라이언트가 캠페인에 할당한 모든 팩을 수집한 다음 **한 개의** `PATCH assign-creative-pack`를 만듭니다.
전체 배열(N `POST`s 대신) 시맨틱이 확인됨: 엔드포인트가 \*\*
제공된 `creativePackIds`로 할당\*\* 완료 — 항상 원하는 전체 세트를 보내거나
배열 누락된 팩은 할당되지 않습니다.

***

## 차단 I — Audience Pinpointer → 캠페인 관리##block-i--audience-pinpointer--campaign-management

### 변경한 사항##what-changed

| **레거시**                                             | **신규**                                      |
| --------------------------------------------------- | ------------------------------------------- |
| `GET …/audience-pinpointer/sdk-event-names`         | `GET …/applications/{id}/sdk-event-names`   |
| `GET …/audience-pinpointer/event-optimization-info` | `GET …/applications/{id}/eligibility`       |
| `GET …/audience-pinpointer/roas-info`               |  — ROAS 캠페인 **자격이 더 이상 필요하지 않음**, 게이트 로직 삭제 |
| `GET …/audience-pinpointer/retention-info`          |  — 리텐션 사용 중단 예정\*\* 완료, 표면 액티브 리텐션 사용       |

`sdk-event-names` 응답: `{ total, results[]{ eventOptimizationType, sdkEventNames[] } }`.
`eligibility` 응답: `{ total, results[]{ eventOptimizationType, countries[] } }`.

***

## 차단 J — Attribution (relocated)##block-j--attribution-(relocated)

### 변경한 사항##what-changed

어트리뷰션 URL이 이전에 **앱**(`appAttributionClickUrl`/`StartUrl`)에서 설정되어 전달되었습니다.
into **campaign create** . 새로운 API에는 **전용 앱 레벨 어트리뷰션 리소스**와
캠페인 업데이트에 대한 캠페인별 `attributionLinks`.

* `GET …/applications/{applicationId}/attribution` → `{ id, startUrl, clickUrl, viewUrl?, partner,
  type?, createdAt, updatedAt }`.
* `PATCH …/applications/{applicationId}/attribution` with `{ startUrl?, clickUrl?, viewUrl? }`.

`partner` 열거형: `other`, `adjust`, `airbridge`, `appsflyer`, `branch`, `bytebrew`, `kochava`, ,
`mailru`, `singular`, `tenjin`, `yandex`. 열거형 `type`: `userLevel`, `nonUserLevel`, `mixed`.

### 마이그레이션 방식##migration-approach

1. 앱 레벨 이동 `appAttributionClickUrl`/`appAttributionStartUrl` → `PATCH …/attribution`
   (`clickUrl`/`startUrl`).
2. 캠페인 만들기 이동 `attributionClickUrl`/`attributionStartUrl` → 캠페인의 `PATCH`
   (`attributionLinks.{clickUrl,startUrl,viewUrl}`) 캠페인을 생성한 **후**.
3. `viewUrl`는 새로운(선택 사항). `{ifa}` 및 기타 URL 매크로: 확인 규칙은 공유
   레거시 API와 신규 API **모두에서** 사용되는 어트리뷰션 검증 라이브러리(확인됨), 따라서 매크로
   동작 변경되지 않으며 레거시 URL을 그대로 유지합니다.

***

## 권장 마이그레이션 알고리즘(에이전트 결정적 순서)##suggested-migration-algorithm-(deterministic-order-for-the-agent)

1. **설정:** 호스트를 `…/ads/advertiser`로 바꾸고 경로 접두사 `/advertise/v1` → `/public/v1` 및
   `apps` 세그먼트 → `applications`. 기본 인증을 유지합니다.
2. **앱(차단 A):** 각 레거시 앱에 대해 `POST …/applications`(리맵 `store` → `{store,
   platform}`).  `legacy campaignSetId → new applicationId`지속。 어트리뷰션 \*\* 보내지 않음\*\*
   URL은 여기에 있습니다.
3. **속성(차단 J):** 예전 앱 레벨 URL을 `PATCH …/applications/{id}/attribution`합니다.
4. **크리에이티브(차단 B):** 다시 업로드/다시 매핑하여 `type` 열거형을 변환합니다. 지속
   ID가 새로 고정되면 `legacy creativeId → new creativeId`(ID가 보존되는지 **확인**).
5. **머티리얼 팩(차단 C):** 새로운 소재 ID를 참조하는 팩을 생성합니다.
6. **캠페인 관리(차단 I):** 청중-피인포인터 룩업을 재지정 —
   `…/audience-pinpointer/sdk-event-names` → `…/applications/{id}/sdk-event-names` 및
   `…/audience-pinpointer/event-optimization-info` → `…/applications/{id}/eligibility`. 읽기 전용;
   결과를 사용하여 캠페인 `appEventType`/`sdkEventNames` 및 자격이 있는 국가를 알립니다.
   ROAS-info 게이트 로직은 삭제할 수 있습니다(자격 요건이 더 이상 필요하지 않음). 리텐션
   사용 중단 예정 리텐션 제품이 사용 중단 예정입니다(단계 13 참조).
7. **캠페인(차단 D):** `optimizationType`로 제작(+ `isCreativeTesting`/`appEventType`/
   `roasType`), `start`만 해당. 그런 다음 `PATCH`는 `end`, `attributionLinks`, `autoStart`, `paused`를 설정합니다.
8. **입찰가(차단 E):** 달러 → **마이크로 달러** 전환(ROAS 목표: 백분율 → 소수점 비율,
   ÷100); 단가 유형별로 원하는 **완전한** 세트가 있는 `PATCH …/bids` 하나를 보내야 합니다(패치
   대체, 병합하지 않음, `[]` 제거). 리텐션 단가는 지원 사용 중단 예정 예정이며 마이그레이션하지 마십시오.
   표면 사용.
9. **메모리 할당량 (차단 F):** 마이크로 달러 단위의 `PATCH …/budget-cap`; 전환 `dailyPerCountry` 맵 →
   `countryGroupDailyLimits[]`.
10. **타게팅(차단 G):** `appleDevice`/`androidDevice`/source+category 목록으로 재구성
    분리된 기기 필드(표면).
11. **할당(차단 H):** 캠페인당 하나의 목록 기반 `PATCH assign-creative-pack`.
12. **읽기:** 모든 `GET …/{bids,budget,targeting,assigned-creative-packs}`를
    `GET campaign?includeFields=…`.
13. **제거된 기능(표면에서 사람):** `DELETE app`(USF 소유의 앱 라이프사이클, 삭제
    사용 중단 예정 리텐션 제품(입찰 + 자격), ROAS 자격
    게이트(더 이상 필요하지 않음 — 삭제), 레거시 사용 중단 예정 타겟 필드, `adomain`,
    `squareVideo` 소재

## 확인 체크리스트(마이그레이션 후 실행)##validation-checklist-(run-after-migration)

* [ ] 요청 경로에는 `/advertise/v1`, `/apps/`, `/cpi-bids`, `/source-bids`,
  `/roas-bids`, `/retention-bids`, `/event-optimization-bids`, `/audience-pinpointer`,
  `/assigned-creative-packs` 또는 `…/budget` (현재 `budget-cap`).
* [ ] 모든 앱 경로는 새로운 **UUID** `applicationId`를 사용하고 24자리 16진수 `campaignSetId`는 사용하지 않습니다.
* [ ] 모든 화폐 값은 **마이크로 달러**(정수)이며, `"2500.20"`처럼 달러 소수점이 아닙니다.
* [ ] 입찰은 단일 `PATCH …/bids`로 이동하고 읽기에는 `GET campaign?includeFields=bids`가 사용됩니다.
* [ ] 모든 단가 `PATCH`는 단가 유형에 대한 **완전한** 세트를 전송합니다(대체 시맨틱 - 부분
  는 누락된 입찰을 내립니다.) 레거시 병합 스타일 업데이트는 읽기-수정-쓰기로 전환되었습니다.
* [ ] ROAS 타겟은 백분율 문자열에서 소수점 비율(`"12.34"` → `0.1234`)으로 전환되었습니다.
* [ ] 메모리 할당량은 `budget-cap` + `countryGroupDailyLimits[]`(`dailyPerCountry` 맵 없음)를 사용합니다.
* [ ] 캠페인 `goal`가 `optimizationType`(+`isCreativeTesting`)로 다시 매핑되고 어트리뷰션
  URL은 캠페인 생성이 아닌 `attributionLinks`/`…/attribution`를 통해 설정됩니다.
* [ ] 소재 `type`는 새로운 snake\_case 값(`end_card_square`, `video_portrait`, ...)을 사용합니다.
* [ ] 레거시 `enabled`의 모든 읽음은 **반전된 의미로** `paused`로 다시 매핑되었습니다
  (`enabled:true` ⇒ `paused:false`) — 리스트, 수신 및 업데이트 응답 모두.
* [ ] 아무것도 캠페인 `PATCH` 응답에서 전체 캠페인 필드를 파싱하지 않습니다.
  details 오브젝트); 전체 상태는 `GET campaign?includeFields=…`를 통해 다시 페치됩니다.
* [ ] 애플리케이션 리스트 파싱은 베어 배열(`{total, results}` 엔벨로프 없음)을 예상하고 더 이상
  전송 `offset`/`limit`/`filter[store]`/`filter[storeId]`.
* [ ] 할당 캠페인당 하나의 리스트 기반 호출 사용하며, N개의 단일 할당이 사용되지 않습니다.
* [ ] `DELETE app`, 사용 중단 예정 리텐션 사용(입찰/적격), `squareVideo` 소재 및
  제거된 타게팅 필드가 휴먼에게 공백으로 표시되었습니다(클라이언트 사용한 경우). ROAS
  자격 게이트가 삭제되었습니다.

## 빠른 레퍼런스: 제거됨 / 동등하지 않음(모두 마이그레이션하거나 생성하지 않음 - 모두 API 소유자에 의해 확인됨)##quick-reference:--removed-/-no-equivalent-(do-not-migrate-or-fabricate--all-confirmed-by-api-owners)

* **`DELETE app`** — 삭제 애플리케이션 엔드포인트가 없습니다. 앱 라이프사이클은 USF가 소유하고 삭제는
  현재 플랫폼 전체에서 비활성화되어 있습니다.
* **ROAS-info** — 제거됨, ROAS 캠페인에는 더 이상 자격이 필요하지 않습니다. 게이트 로직을 삭제합니다.
* **리텐션(전체 제품 영역)** — 사용 중단 예정: `retention-info` 및 리텐션 **입찰 단가**가 없음
  successor。 표면 활성 리텐션 캠페인/입찰 단가
* **GET 입찰 / 메모리 할당량 / 타게팅 / 할당된 팩** — `GET campaign?includeFields=…`로 대체.
* **레거시 중단 예정 타게팅 필드**(`limitedAdTracking`, `connectionType`, `screenSize`,
  `screenDensity`, `allowedDevices`, `osMax`) 및 앱 **`adomain`** — 에서 구성할 수 없음
  통합; 클라이언트 코드를 삭제합니다.
* **`squareVideo` 소재 유형** — 지원되지 않음, `video_portrait`/`video_landscape`만 지원
