Backend - connecteurs régies
Reporting TikTok/Meta (spend, campagnes, vignettes) et activation Meta CAPI
En une phrase : deux familles de connecteurs dans le module attribution - le reporting (lecture seule : spend, campagnes, ad groups, ads depuis les APIs annonceur TikTok/Meta) et l'activation (écriture : nos events consentis poussés vers Meta CAPI, et le câblage de l'adapter TikTok embarqué).
Code : apps/api/src/modules/attribution/connectors/ (reporting) et
activation/ (activation). Décision : ADR-0038.
Reporting : « reads are the whole story »
Promesse produit affichée : on ne fait que LIRE les comptes pub, jamais écrire.
Connexion des comptes
OAuth TikTok (Login for Business, config_id) et Meta (Login for Business,
redirect AVEC slash final - TikTok SANS), token collé en fallback. Tokens
chiffrés AES-256-GCM (common/crypto.ts, format enc:<iv>:<tag>:<data>).
Un compte se rattache à une app APRÈS connexion (l'OAuth arrive avant qu'on
sache pour quelle app) : ad_accounts.project_id nullable, select par ligne
dans la page Intégrations. Un compte non rattaché reste dormant.
La hiérarchie à trois niveaux
campaigns + campaign_daily (0065), puis ad_entities + ad_entity_daily
(0070) - une paire GÉNÉRIQUE pour ad groups ET ads, discriminée par level
(vocabulaire neutre : un adset Meta est un ad_group), avec parent_id.
Le sync (ad-accounts.service.ts) est tolérant par étage : le rapport de
métriques d'abord (ses noms servent de registre de secours si le scope de
lecture manque), le registre ensuite, chaque échec est un warn jamais un throw.
Côté TikTok, les listes de métriques dégradent progressivement
(app_install/skan_conversion/conversion sont optionnelles selon les
comptes) au lieu de faire échouer le sync. Cron nocturne
attribution.spend-sync à 8h45 UTC + bouton par compte.
Les vignettes de créas (2026-09-08)
ad_entities.thumbnail_url (0071), rafraîchies à CHAQUE sync car les URLs CDN
sont signées et expirent ; COALESCE à l'upsert pour garder la dernière cover
connue sur un raté ponctuel.
- Meta : champ
creative{thumbnail_url}sur le listing/ads(même scopeads_read, rien de plus). - TikTok : détour obligatoire - les Spark Ads exposent
tiktok_item_idsur/ad/get/, mais/identity/video/info/exige un scope Identity qu'on n'a pas. Solution : l'oEmbed public (https://www.tiktok.com/oembed?url=.../@_/video/<item_id>, le handle placeholder marche, TikTok redirige) donnethumbnail_urlsans auth. Résolution concurrente (8 workers, timeout 5 s), un échec = cover absente, jamais un sync raté.
Statuts
active/paused/deleted/unknown. TikTok : operation_status est
l'interrupteur, secondary_status porte la suppression. Piège vécu : un token
copié AVANT l'approbation d'un scope liste les entités mais sans statut
(unknown partout) - re-auth après approbation obligatoire.
Activation Meta CAPI (2026-09-08)
activation/ : la table activation_destinations (0074, une par app x
réseau : dataset, token chiffré, curseur, santé), le client
meta-capi.client.ts et le cron attribution.capi-forward toutes les 5 min.
Le tick, par destination active :
- lit les devices consentis (
device_ad_ids, cf. la couche attribution du SDK pour les trois états) - aucun ? rien ne part et le curseur ne bouge pas ; - lit les events ClickHouse depuis le curseur (
app_install,start_trial,purchase,subscribe; backfill initial 72 h ; lots de 500, 20 max par tick) ; - filtre aux devices consentis (un event scanné sans consentement est passé pour toujours : un consentement tardif ne ré-expédie pas le passé) ;
- pousse sur
graph.facebook.com/v23.0/{dataset}/events:event_id= notre UUID (dédup Meta),madidsiad_idprésent,advertiser_tracking_enabled= verdict ATT/LAT,action_source: 'app'; - avance le curseur, met à jour
last_delivery_at/forwarded_count.
Santé : token refusé (code Graph 190) → status='error' visible dans l'UI
(« reconnecte avec un token frais ») ; autre échec → last_error, retry au
tick suivant. La connexion depuis le dashboard valide le credential
immédiatement par un envoi vide.
Pourquoi pas l'OAuth existant ? Trois raisons : scope d'écriture ≠ scope de lecture (consentement minimal par usage), le token OAuth expire (~60 j) là où le token système ne meurt jamais (un pipeline S2S sans surveillance ne doit pas s'éteindre en silence), et lister/écrire les datasets d'un tiers exige l'Advanced Access Meta (review). Les MMP établis demandent la même manip sur ce rail. One-click possible après la review Meta.
Activation TikTok
Pas d'envoi serveur (leur Events API App Install n'est pas GA) : la destination
TikTok stocke le TikTok App ID + le token d'app (chiffré), et
GET /sdk/v1/attribution/activation-config les sert à l'adapter embarqué
(appwin-tiktok-events). Le jour de la GA, l'envoi bascule ici, à côté de Meta.