役割
あなたは AppLovin で広告運用責任者のために creative set をバッチ作成 します。APIラッパーや認証情報は、本スキルと同じディレクトリの scripts/creative_tuner.py(ct.set_account() / ct.APL_MGMT_KEY / ct.APL_ACCOUNT_ID / ct.APL_MGMT_URL)を再利用します。すべてのツールスクリプトは scripts/ にあり、ファイル名は _cs_*.py です。
インストール/設定(初回必読)
apl-creative-set/ディレクトリ全体を~/.claude/skills/(Windows の場合は%USERPROFILE%\.claude\skills\)に配置します。scripts/内でcp config.example.json config.jsonを実行し、自分の AppLovin 認証情報を入力します。accounts.<アカウントID>.report_key= AppLovin Report Key;.mgmt_key= Management API Key。account_aliasesでアカウントにエイリアスを設定できます(例:"myapp": "あなたのアカウントID")。スクリプト内でct.set_account("myapp")で使用可能。feishuセクションはオプションで、creative_tuner.pyの最適化レポートでのみ使用されます。creative set の作成のみの場合は不要で、空のままにしておけます。
config.jsonには実際のキーが含まれているため、絶対に git にコミットしたり、外部と共有したりしないでください(.gitignoreに追加することを推奨)。- スクリプト内で
ct.set_account("YOUR_ACCOUNT")、上部のCAMP=""/PA_IDS/VIDS/PREFIX/PP/SUFFIXなどの大文字定数は TODO プレースホルダー です。今回の目標に合わせて変更してください。
本スキルは Claude が SKILL.md を読み込んで駆動 します。あなたが目標(キャンペーン、作成する動画、命名規則、CPP)を Claude に伝えると、Claude は以下の手順に従ってスクリプト上部の定数を変更し、それらを呼び出します。
絶対ルール
- ★ 作業開始前に毎回、ユーザーに以下の3項目を1つずつ確認すること(iOS/Android 両端で異なる場合があるため、前回の設定を引き継がないこと):
① set の命名規則;② CPP サフィックスを付けるかどうか;③ CPP の
product_pageに設定する値。 たとえ前回と同じ製品ラインであっても確認すること。デフォルトを適用しないこと。 - 認証情報の安全性:
config.json内の key/secret/token/open_id を絶対に出力/表示/エコーバックしないこと。スクリプトは認証情報を print しないこと。 - デフォルト PAUSED:新しく作成する set はすべて
status:"PAUSED"に設定し、ユーザーが確認して問題ないと言ってから有効にすること(ユーザーが「有効化」と言った場合のみ →LIVE)。 - 指定された素材範囲のみを対象とする:ユーザーが「特定のプレフィックスのみ」「最近アップロードされたもののみ」などと限定することがよくあります。その制限に厳密に従い、余分に作成しないこと。
- 各ステップで読み取り検証を実施:create/マウント/CPP/有効化の後は必ず
creative_set/listで確認し、レスポンスコードだけに頼らないこと。 - 数字は出典を明記し、小数点第2位まで正確に。不明な点はまず質問し、書き込み操作の前には確認してから実行すること。
★ API の重要な仕組み(過去に踏んだ落とし穴、必ず従うこと)
- キャンペーンの紐付けは常に add-to-campaigns を基準とし、create で自動的に紐付くことを期待しないこと。
POST /creative_set/createの body 内のキャンペーン関連フィールドは2種類の書き方(単数campaign_id:"<内部id>"/ 複数campaign_ids:["<内部id>"])のいずれも見られますが、どちらも確実に紐付くとは限りません(複数形はしばしば無視されます)。したがって、フローは次のように固定します:create で作成 → すぐにadd-to-campaignsで確実にマウント →campaign_idsを読み取って検証。 create body(実運用版):{"account_id":AID,"type":"APP","name":..,"status":"PAUSED","campaign_ids":["<内部id>"],"product_page":"","languages":[],"countries":[],"assets":[{"id":".."},..]}→ 戻り値{"id":..,"version":"V2"} - キャンペーンの追加マウント:
POST /creative_set/add-to-campaignsを使用し、整数の配列(複数) で指定:{"campaign_ids":[<内部id>],"creative_set_ids":[..]}(campaign≤20、set≤50/バッチ)。これが最も信頼性が高い。読み取り検証でcampaign_idsに対象キャンペーンが含まれていることを確認すること。 - set の変更(名前変更/CPP/ON-OFF/素材の追加削除):
POST /creative_set/update{"id":..,"type":"APP","status":"LIVE|PAUSED","name":..,"product_page":..,"assets":[..]}- update は新しいバージョンを生成します。
assetsを渡すことは全体の上書きを意味します(追加ではありません)。削除=その id を渡さない、追加=id を全件リストに含める、渡し忘れたものは削除されます(動画+残したいプレイアブルは必ず保持すること)。 - 未設定のフィールド(name/product_page/campaign 関連など)は実際には保持されることが確認されています。安全のため、ON/OFFや素材変更時には name+product_page+languages+countries も一緒に含めておくことを推奨します。
- update で
campaign_idsを渡しても無効(200 は返るがマウントされない)——マウントは add-to-campaigns のみ。
- update は新しいバージョンを生成します。
- カスタムプロダクトページ (CPP):フィールド
product_page。- デフォルトのストアページ(Use default product page)=
product_page:""空文字列(CPP なし、名前にサフィックスを付けない)。 - CPP を設定する場合は、対応する product_page 値(文字列のスラッグ、UUID ではない) を入力します。リストエンドポイントはありません。特定の名前に対応する値は、全アカウントの set をスキャンして
product_pageと名前のサフィックスを統計することで確認します(_cpp_map.pyを参照)。
- デフォルトのストアページ(Use default product page)=
- アセットタイプフィールドは2つのAPIで異なる名前(よく間違える):
asset/list(メディアライブラリ)のフィールドはasset_type;creative_setのassets[]要素内のフィールドはtype。- 値:動画 =
VID_LONG_P/VID_SHORT_P(またはstartswith("VID"));プレイアブル =HOSTED_HTML。
- ★ 単一 creative set の素材上限 = 11:assets が 11 に達した後に update で追加しようとすると静かに拒否されます(エラーは返さないが追加されず、読み取り検証でのみ発見可能)。プレイアブルを追加する前に
len(assets)を確認し、11 の場合は1つ削除してから追加(置き換え)する必要があります。そうしないと追加できません。 creative_set/listとasset/listのページネーションサイズは ≤100/200 で、一部のページは裸のリスト({results:[]}ではない)を返すため、解析にはisinstanceで互換性を持たせる必要があります。asset/listの1ページあたりの上限は 100:ページ送りの判定にはlen(res)<100をブレーク条件として使用すること(200 を使用すると最初のページで誤って停止し、最初の100件のメディアしか取得できません)。
作業開始前に確認すること(ユーザーが既に提供している場合を除く)
- ターゲットキャンペーン(名前/ID)→ config で設定されたアカウント間で検索し、内部 ID を取得。
- 素材範囲:どの動画か?プレフィックスフィルター(例:特定の製品ライン)、アップロード時間の基準(upload_time≥特定の日付)、ステータス(通常 ACTIVE で他の set で未使用)。
- 固定プレイアブル:どの HTML プレイアブル(ユーザーが正確なファイル名を指定)。
- 命名規則:デフォルト
EN_動画タイプ(AI/3D/JJ…素材名のタイプスロット)_動画名_topプレイアブルグループPA;シリーズの複数動画を同じ set にまとめる場合 →EN_タイプ_ベース名グループ_topプレイアブルグループPA(ベース名=末尾の連番を除去)。 - シリーズ判定:同じコンセプト+末尾番号(イテレーション1/4)を1つのグループにまとめる。紛らわしいものは事前に質問すること(例:「基本バージョン/上級バージョン」を分けるかどうか)。
- CPP:
product_pageを設定するかどうか、入力する値、名前にサフィックスを付けるかどうか。
標準フロー(スクリプトパイプライン、すべて scripts/ 内)
_apl_active_assets.py→ ACTIVE な素材を取得し重複排除(_apl_active_assets.json)。_cs_plan_scan.py→ 固定プレイアブルのID + 候補動画(プレフィックス+is_video+既存のsetで未使用+時間フィルター)を検索 →_cs_scan.json。最初に上部のPREFIX/CAMP/targetsを変更。_cs_group.py→ 候補をシリーズ/単一素材でグループ化し、名前を生成 →_cs_build_plan.json。_cs_plan_show.pyを使ってユーザーに確認用に表示可能。_cs_build_all.py(冪等:名前で重複をチェックし、既存のものはスキップ)→ 1つずつ create(PAUSED、campaign_id 付き) + add-to-campaigns で確実にマウント + 読み取り検証 →_cs_create_result.json。- (オプション)
_cpp_apply.py all→product_pageを設定 + 名前にサフィックスを追加(最初に上部のPP/SUFFIXを変更)。 - ユーザーが確認後
_cs_enable.py→ status→LIVE(name+product_page を保持して消失防止)+ 読み取り検証。 - スポットチェック:
_cs_spotcheck.py <id..>で素材構成(動画数+プレイアブル数)を確認。
軽量パス:少数の指定動画を直接グループ化(動画名が既知の場合、全量スキャン不要)
適用:ユーザーが数本の動画を新しくアップロードし、グループ化を直接指定する場合。
_cs_find_new.py <キーワード..>→ メディアライブラリで動画を名前で検索(完全名+id+ステータス+アップロード時間)(読み取りのみ)。該当する動画かどうか、ステータスが ACTIVE かどうかを確認。_cs_en_new_create.pyの上部のVIDS=[(コア名,動画id),..]/PA_IDS(固定プレイアブル) /CAMPを変更。まず絶対ルール0に従ってユーザーに命名規則+CPPを確認。python _cs_en_new_create.py(dry-run で重複チェック+計画一覧表示)→go(PAUSED + デフォルトストアページ""+ 言語はキャンペーンに従う + add-to-campaigns + 読み取り検証:マウント/assets数/pp/status)。- ユーザーが有効化を指示 →
_cs_en_new_enable.py(_cs_en_new_ids.jsonを読み込み、name/product_page/languages/countries を明示的に指定して消失防止、status→LIVE + 読み取り検証)。
set 内のプレイアブルの追加/削除/置き換え
set 名のキーワードで特定のプレイアブルを一括追加/削除(update で assets を全体上書き、name/product_page/languages/countries/status を保全、実行後に読み取り検証):
- 追加:
_cs_add_pa.py <プレイアブルid> <set名キーワード> [enonly](dry-run)→ 続けてgo。enonly=EN_で始まるグループのみ対象(小言語ターゲットグループを除外)。冪等で既存のものはスキップ。11 に達しているグループは自動的に警告表示してスキップ(事前に1つ削除して空きを作る必要あり)。 - 削除:
_cs_rm_pa.py <プレイアブルid>(dry-run)→go。セーフガード:削除後も set には少なくとも1つの動画と1つのプレイアブルが残っている必要あり。そうでない場合は赤色表示でスキップし変更しない。 - 置き換え = 全assetsリスト内の古いidを新しいidに置き換えてupdate。
- プレイアブルid は
_cs_find_new.py <キーワード>でメディアライブラリを名前で検索(HOSTED_HTML)。
納品時のトーク
レポート:作成したグループ数(シリーズ/単体)、カバーした動画数、各グループ=対応動画+N個のプレイアブル、すべて PAUSED+マウント済み+CPP、および「グループ名→動画」のリスト。未承認/未アップロードの素材がある場合は通知(全ステータスを再スキャンする _b8_leftover.py の考え方で REJECTED/未アップロードに分類)。ユーザーから「有効化」の指示があってからステップ6を実行。