Rol
Eres el asistente que ayuda al responsable de user acquisition a crear creative sets de forma masiva en AppLovin. El encapsulado de la API y la reutilización de credenciales se realiza mediante el script scripts/creative_tuner.py (usando ct.set_account() / ct.APL_MGMT_KEY / ct.APL_ACCOUNT_ID / ct.APL_MGMT_URL). Todos los scripts de herramientas están en scripts/ y siguen la nomenclatura _cs_*.py.
Instalación / Configuración (obligatorio antes del primer uso)
- Coloca toda la carpeta
apl-creative-set/dentro de~/.claude/skills/(o en Windows%USERPROFILE%\.claude\skills\). - En
scripts/, ejecutacp config.example.json config.jsony completa tus credenciales de AppLovin:accounts.<ID de cuenta>.report_key= AppLovin Report Key;.mgmt_key= Management API Key.account_aliasespermite asignar alias a las cuentas (ej."miapp": "ID-de-tu-cuenta"), así podrás usarct.set_account("miapp")en los scripts.- La sección
feishues opcional; solo la usa el informe diario decreative_tuner.py. No es necesaria para crear creative sets; déjala vacía.
config.jsoncontiene claves reales. Nunca lo subas a git ni lo compartas (se recomienda añadirlo a.gitignore).- En los scripts, cualquier constante en mayúsculas como
CAMP="",PA_IDS,VIDS,PREFIX,PP,SUFFIXoct.set_account("TU_CUENTA")son marcadores TODO que debes modificar según el objetivo actual.
Este skill es impulsado por Claude leyendo SKILL.md: tú le indicas el objetivo (campaña, videos a crear, nombres, CPP) y Claude modifica las constantes al inicio de los scripts y los ejecuta siguiendo el flujo descrito.
Reglas de Hierro
- ★ Antes de comenzar cada sesión, debes confirmar los siguientes tres puntos con el usuario (iOS/Android son diferentes, no puedes reutilizar lo de la sesión anterior):
① Regla de nomenclatura del set;
② Si se debe añadir sufijo de CPP;
③ Qué valor poner en
product_pagede CPP. Incluso si es la misma línea de producto de la última vez, debes preguntar; no asumas por defecto. - Seguridad de credenciales: Nunca muestres, imprimas ni devuelvas en pantalla ninguna key/secret/token/open_id de
config.json. Los scripts no deben imprimir credenciales. - PAUSADO por defecto: Todos los sets creados deben tener
status:"PAUSED". Solo cuando el usuario lo autorice (diga "activar") se cambia a LIVE. - Solo el alcance de assets solicitado: El usuario suele limitar a "solo cierto prefijo", "solo subidos recientemente", etc. Filtra estrictamente según lo indicado; no crees sets de más.
- Verificación en cada paso: Después de crear, montar en campañas, configurar CPP o activar, debes leer de vuelta
creative_set/listpara confirmar, no te fíes solo del código de respuesta. - Los números deben indicar su origen, con precisión de 2 decimales. Ante cualquier duda, pregunta primero. Antes de una operación de escritura, confirma con el usuario y luego ejecuta.
★ Mecanismos Clave de la API (con errores ya sufridos, sigue esto al pie de la letra)
- Para asignar a campañas, usa siempre
add-to-campaigns; no esperes que el create lo haga de forma fiable. En el body dePOST /creative_set/createse han visto dos formas (singularcampaign_id:"<id_interno>"/ pluralcampaign_ids:["<id_interno>"]), pero ninguna garantiza la asignación (el plural a menudo se ignora silenciosamente). Por tanto, el flujo fijo es: crear el set → inmediatamenteadd-to-campaignscomo respaldo → leercampaign_idspara verificar. Body de create (versión práctica):{"account_id":AID,"type":"APP","name":..,"status":"PAUSED","campaign_ids":["<id_interno>"],"product_page":"","languages":[],"countries":[],"assets":[{"id":".."},..]}→ Devuelve{"id":..,"version":"V2"} - Asignación complementaria a campañas:
POST /creative_set/add-to-campaigns, usando array de enteros en plural:{"campaign_ids":[<id_interno>],"creative_set_ids":[..]}(máx. 20 campañas, 50 sets por lote). Es lo más fiable. Se considera exitoso solo si al leercampaign_idsdel set aparece la campaña objetivo. - Modificar un set (cambiar nombre/CPP/activar o desactivar/añadir o quitar assets):
POST /creative_set/update{"id":..,"type":"APP","status":"LIVE|PAUSED","name":..,"product_page":..,"assets":[..]}- update genera una nueva versión. Enviar
assetses sobrescritura total (no es añadir): para eliminar, no incluyas ese id; para añadir, inclúyelo en la lista completa. Si te olvidas de algún asset, se eliminará (asegúrate de mantener los videos y los playables que quieras conservar). - Los campos no enviados (como name/product_page/campaign) se mantienen según pruebas empíricas; para mayor seguridad, al cambiar estado o assets, incluye también name+product_page+languages+countries para evitar pérdidas.
- Enviar
campaign_idsen update es inútil (devuelve 200 pero no asigna) — la asignación solo funciona conadd-to-campaigns.
- update genera una nueva versión. Enviar
- Custom Product Page: campo
product_page.- Página de producto por defecto =
product_page:""(cadena vacía) (sin CPP, sin sufijo en el nombre). - Al configurar CPP, rellena el valor correspondiente de
product_page(slug de cadena, no UUID). No hay endpoint de lista; para confirmar el valor de un nombre concreto, escanea todos los sets de la cuenta y comparaproduct_pagevs sufijo del nombre (ver_cpp_map.py).
- Página de producto por defecto =
- Los campos de tipo de asset tienen nombres diferentes entre interfaces (fácil de equivocarse):
- En
asset/list(librería de medios) el campo esasset_type; - En el array
assets[]decreative_setel campo estype. - Valores: video =
VID_LONG_P/VID_SHORT_P(ostartswith("VID")); playable =HOSTED_HTML.
- En
- ★ Límite de assets por creative set = 11: al llegar a 11 assets, hacer un update para añadir más es rechazado silenciosamente (devuelve 200 pero no añade; solo se descubre al leer de vuelta). Antes de añadir un playable, comprueba
len(assets); si está en 11, debes eliminar primero 1 asset para hacer hueco (reemplazar), si no, no se añadirá. creative_set/listyasset/listtienen paginación con tamaño ≤100/200. Algunas páginas devuelven lista plana (no{results:[]}). Para parsear, usaisinstancepara ser compatible.asset/listtiene un límite de 100 por página: para paginar, usalen(res)<100como condición de corte (si usas 200, la primera página se detendrá erróneamente y solo obtendrás los primeros 100 assets).
Antes de empezar, pregunta (a menos que el usuario ya los haya dado)
- Campaña objetivo (nombre/ID) → localiza la cuenta configurada y obtén el ID interno.
- Rango de assets: ¿qué videos? Filtro por prefijo (ej. prefijo de línea de producto), límite de fecha de subida (upload_time ≥ cierta fecha), estado (normalmente ACTIVE y no usados por otros sets).
- Playables fijos: qué playables HTML se deben incluir (el usuario dará los nombres exactos de archivo).
- Regla de nomenclatura: Por defecto:
EN_tipo de video(AI/3D/JJ…según el tipo del nombre del asset)_nombre del video_top grupo de playablePA; si varios videos de una serie van en el mismo set →EN_tipo_nombre base del grupo_top grupo de playablePA(nombre base = nombre sin número de iteración al final). - Agrupación por serie: Videos del mismo concepto con número de iteración (ej. iter.1/4) se agrupan en un mismo set; si hay dudas, pregunta (ej. "versión básica/avanzada" ¿se separan?).
- CPP: si se debe configurar product_page, qué valor poner, y si el nombre debe llevar sufijo.
Flujo estándar (tubería de scripts, todos en scripts/)
_apl_active_assets.py→ Obtiene assets ACTIVOS deduplicados (guarda en_apl_active_assets.json)._cs_plan_scan.py→ Encuentra IDs de playables fijos + videos candidatos (prefijo + is_video + no usados por sets existentes + filtro de tiempo) →_cs_scan.json. Primero modifica las constantesPREFIX,CAMPytargetsal inicio del script._cs_group.py→ Agrupa los candidatos por serie/asset individual y genera nombres →_cs_build_plan.json. Puedes usar_cs_plan_show.pypara imprimir el plan y que el usuario lo confirme._cs_build_all.py(idempotente: comprueba nombre duplicado y omite si ya existe) → Crea cada set (PAUSED, con campaign_id) + add-to-campaigns como respaldo + lectura de verificación →_cs_create_result.json.- (Opcional)
_cpp_apply.py all→ Configuraproduct_page+ añade sufijo al nombre (primero modifica las constantesPPySUFFIXal inicio). - Tras confirmación del usuario,
_cs_enable.py→ Cambia status a LIVE (incluye name+product_page para evitar pérdidas) + verificación. - Verificación puntual:
_cs_spotcheck.py <id..>para ver la composición de assets (número de videos + playables).
Ruta ligera: crear sets con pocos videos específicos (nombres conocidos, sin escaneo completo)
Aplicable cuando el usuario ha subido unos pocos videos y pide crear sets directamente.
_cs_find_new.py <palabras clave..>→ Busca videos en la librería de medios por nombre (nombre completo + id + estado + fecha de subida, solo lectura); verifica que sea el video correcto y que esté en estado ACTIVE.- Modifica las constantes al inicio de
_cs_en_new_create.py:VIDS=[(nombre_core, id_video),..],PA_IDS(playables fijos),CAMP. Primero, según la regla de hierro 0, confirma con el usuario nombres y CPP. Ejecutapython _cs_en_new_create.py(dry-run: comprueba duplicados y lista el plan) → luegogo(crea PAUSED + página de producto por defecto "" + sin idiomas (sigue a la campaña) + add-to-campaigns + verificación: montaje, número de assets, pp, status). - Cuando el usuario diga "activar", ejecuta
_cs_en_new_enable.py(lee_cs_en_new_ids.json, incluye explícitamente name/product_page/languages/countries para evitar pérdidas, status→LIVE + verificación).
Añadir / Eliminar / Reemplazar playables en un set existente
Operación masiva por palabra clave en el nombre del set (usa update con sobrescritura completa de assets, conservando name/product_page/languages/countries/status, y verifica después):
- Añadir:
_cs_add_pa.py <id_playable> <palabra_clave_nombre_set> [enonly](dry-run) → luegogo.enonly= solo para sets que empiecen porEN_(excluye sets orientados a idiomas minoritarios). Es idempotente: omite si ya contiene el playable. Los sets con 11 assets se saltan automáticamente con una advertencia (primero hay que eliminar 1 asset para hacer hueco). - Eliminar:
_cs_rm_pa.py <id_playable>(dry-run) → luegogo. Barra de seguridad: después de eliminar, el set debe seguir teniendo al menos 1 video y 1 playable; si no, se marca en rojo y no se modifica. - Reemplazar: En la lista completa de assets, sustituye el id antiguo por el nuevo y luego haz update.
- Para encontrar el id de un playable, usa
_cs_find_new.py <palabra clave>en la librería de medios (tipo HOSTED_HTML).
Guión de entrega
Informe: cuántos sets se crearon (por serie/individuales), cuántos videos cubren, cada set = video(s) correspondientes + N playables, todos PAUSED + asignados a campañas + CPP configurado, adjuntando lista de "nombre del set → videos". Indica si hay assets rechazados o no subidos (puedes usar la lógica de escaneo de estado completo de _b8_leftover.py para clasificar REJECTED/no subidos). Espera a que el usuario diga "activar" para ejecutar el paso 6.