Dernière mise à jour : novembre 2025 · Niveau : avancé
Pourquoi se passer du SDK Meta
Sur mobile, la plupart des équipes confient le tracking Meta au SDK officiel : il collecte les informations de l’appareil, identifie l’utilisateur, gère l’attribution et envoie les événements à Meta. Un mode « CAPI seule », sans SDK dans l’application, a pourtant de bonnes raisons d’exister :
- Confidentialité : plus de contrôle sur ce qui est collecté, et quand.
- Taille de l’app : pas de SDK tiers de plus.
- Contrôle côté serveur : tout le traitement passe par votre infrastructure.
- Flexibilité : une logique d’événements sur mesure.
- Consentement : un seul endroit (votre backend) pour appliquer le choix de l’utilisateur avant tout envoi.
Le revers : le SDK Meta fait énormément de choses automatiquement. Sans lui, vous devez recréer à la main une grande partie de son travail. Il faut aussi, côté Meta, un dataset rattaché à votre app dans Events Manager (obligatoire avant d’envoyer des événements d’app, une seule app par dataset).
Ce que le SDK fait pour vous et que vous devez désormais gérer
- Identifiants de l’appareil : IDFA (iOS) ou GAID (Android), identifiant anonyme de secours, modèle, version d’OS, résolution, version de l’app.
- Identification de l’utilisateur : e-mail et téléphone hachés (Advanced Matching).
- Contexte de l’événement : horodatage, session, état de l’app, type de connexion.
- Attribution : identifiants de campagne des clics, deep links, deferred deep linking.
- Métadonnées techniques : le fameux tableau
extinfo(16 valeurs ou plus), IP, User Agent, locale, fuseau, opérateur, statut ATT.
Sans SDK, tout cela est à collecter, formater et envoyer vous-même.
Architecture
Le flux tient en quatre étapes : l’app collecte les données brutes, les envoie en HTTPS à votre backend, qui valide, hache et enrichit, puis votre serveur appelle Meta sur graph.facebook.com/v25.0/{dataset_id}/events.
Règle clé sur les données personnelles : envoyez-les en clair de l’app vers votre backend (en HTTPS), et hachez uniquement côté serveur. Ne hachez jamais dans l’app : des implémentations différentes d’une plateforme à l’autre cassent la correspondance.
Paramètre n°1 : les identifiants d’appareil (madid et anon_id)
Le MADID (Mobile Advertising ID) est l’identifiant principal d’attribution : IDFA sur iOS, GAID sur Android.
iOS : récupérer l’IDFA
Depuis iOS 14.5, il faut demander l’autorisation via le framework App Tracking Transparency (ATT). Si l’utilisateur refuse, l’IDFA vaut 00000000-0000-0000-0000-000000000000 : on bascule alors sur l’identifiant anonyme.
ATTrackingManager.requestTrackingAuthorization { status in
switch status {
case .authorized:
let idfa = ASIdentifierManager.shared().advertisingIdentifier.uuidString
self.sendToBackend(idfa: idfa)
case .denied, .restricted, .notDetermined:
// IDFA = 00000000-... : on utilise l'identifiant anonyme
self.sendToBackend(anonId: self.getOrCreateAnonymousId())
@unknown default: break
}
}
Android : récupérer le GAID
Pas de fenêtre d’autorisation, mais les apps ciblant Android 13+ doivent déclarer la permission AD_ID. Un identifiant supprimé par l’utilisateur revient en zéros : on le filtre comme l’IDFA nul, et on respecte le « limit ad tracking ».
Format attendu par Meta
- Correct : l’identifiant tel que renvoyé par l’OS, en minuscules, avec les tirets, non haché. Exemple :
38400000-8cf0-11bd-b23e-10b96e40000d. - Incorrect : majuscules, tirets retirés, haché, ou l’identifiant tout à zéro (à ne jamais envoyer).
function formatMADID(madid) {
if (!madid || madid === '00000000-0000-0000-0000-000000000000') return null;
return madid.toLowerCase(); // on garde les tirets, jamais de hachage
}
anon_id : le secours indispensable
Quand l’IDFA ou le GAID n’est pas disponible, il faut générer un anon_id : un UUIDv4 créé au premier lancement, conservé localement (UserDefaults, Keychain, AsyncStorage, SharedPreferences) et envoyé avec chaque événement sans MADID.
{
"user_data": {
"madid": "38400000-8cf0-11bd-b23e-10b96e40000d",
"anon_id": "a1b2c3d4-e5f6-7890-1234-567890abcdef"
}
}
Envoyez les deux quand ils existent, un seul sinon. Quand l’ATT est refusé, envoyez uniquement anon_id.
Paramètre n°2 : le tableau extinfo
extinfo est un tableau à positions fixes, de 16 valeurs, généré automatiquement par le SDK. Il est obligatoire pour les événements d’app : sans lui, Meta rejette l’événement ou ne peut pas l’exploiter.
| Position | Contenu | Exemple |
|---|---|---|
| 0 | Version d’extinfo (« a2 » Android, « i2 » iOS) | i2 |
| 1 | Package / bundle ID | com.myapp |
| 2 | Version courte de l’app | 1.2.3 |
| 3 | Version longue (build) | 456 |
| 4 | Version de l’OS | 17.2.1 |
| 5 | Modèle d’appareil | iPhone14,2 |
| 6 | Locale | en_US |
| 7 | Abréviation du fuseau | CET |
| 8 | Opérateur | Orange |
| 9-10 | Largeur et hauteur d’écran | 1170 / 2532 |
| 11 | Densité d’écran | 3 |
| 12 | Nombre de cœurs CPU | 6 |
| 13-14 | Stockage total et libre (Go) | 256 / 120 |
| 15 | Fuseau de l’appareil | Europe/Paris |
L’article d’origine fournit les implémentations complètes en React Native (avec react-native-device-info), en Swift et en Kotlin. Les règles à retenir :
- l’ordre est impératif ;
- une valeur manquante devient une chaîne vide
"", on garde la position ; - jamais de tableau vide ;
- toutes les valeurs sont des chaînes, nombres compris (
"1170"), voir la table extinfo de Meta.
Paramètre n°3 : l’Advanced Matching (données personnelles hachées)
L’Advanced Matching permet à Meta de rattacher un événement à un utilisateur même sans IDFA ni GAID. C’est décisif sur iOS 14.5+, où la majorité des utilisateurs refusent l’ATT. Sans SDK, vous devez normaliser, hacher en SHA-256 et envoyer vous-même.
| Champ | Paramètre | Exemple (avant hachage) |
|---|---|---|
em | test@example.com | |
| Téléphone | ph | 33612345678 |
| Prénom | fn | john |
| Nom | ln | doe |
| Ville | ct | paris |
| Région | st | iledefrance |
| Code postal | zp | 75001 |
| Pays | country | fr |
| Genre | ge | m |
| Date de naissance | db | 19900515 |
Normaliser avant de hacher
Meta est très strict : un champ mal normalisé ne correspondra jamais.
- E-mail : espaces retirés, minuscules, rien d’autre (on garde points et « + »).
- Téléphone : chiffres uniquement, jamais de « + » ; le préfixe international « 00 » est supprimé, l’indicatif pays est ajouté si absent.
- Prénom et nom : minuscules, ponctuation retirée, lettres accentuées conservées (UTF-8).
- Ville et région : minuscules, accents retirés, uniquement a-z (ni espace ni ponctuation).
- Code postal : minuscules, espaces et tirets retirés ; aux États-Unis, 5 premiers chiffres seulement.
- Date de naissance : format AAAAMMJJ.
- Genre : « m » ou « f » uniquement.
Hacher côté serveur
Toujours sur le backend : une implémentation unique, plus simple à déboguer, et aucun secret dans l’app.
const crypto = require('crypto');
function hashSHA256(value) {
if (!value) return null;
return crypto.createHash('sha256').update(value).digest('hex');
}
hashSHA256(normalizeEmail("Test@Example.com"));
// "973dfe463ec85785f5f95af5ba3906eedb2d931c24e69824a89ea65dba4e813b"
Bonnes pratiques :
- envoyez autant de champs que vous en avez : plus il y en a, meilleure est la correspondance ;
- n’envoyez ni chaîne vide ni « unknown » : omettez le champ ;
countrydoit lui aussi être haché (sha256("fr")) ;- ne hachez pas
madid,anon_id,client_ip_address,client_user_agent,fbp,fbc(paramètres d’information client).
Paramètre n°4 : app_data
L’objet app_data décrit l’app et l’environnement de tracking. Les champs requis :
| Champ | Type | Rôle |
|---|---|---|
application_tracking_enabled | entier 0/1 | Le tracking au niveau de l’app est-il autorisé ? Mettre 0 si l’utilisateur a refusé dans l’app ou n’a pas consenti (RGPD / CCPA). |
advertiser_tracking_enabled | entier 0/1 | Reflète l’ATT sur iOS : 1 seulement si le statut est « authorized » (1 par défaut avant iOS 14.5). |
extinfo | tableau | Voir paramètre n°2. |
Champs optionnels : installer_package (Android seulement, par exemple com.android.vending ; Meta indique qu’il est « utilisé en interne par les SDK Android », donc à omettre sur iOS), url_schemes, consider_views et campaign_ids.
Paramètre n°5 : action_source
action_source dit à Meta d’où vient l’événement. Pour une app, la valeur est toujours "app". Les autres valeurs (website, email, phone_call, chat, physical_store, system_generated, other) servent à d’autres canaux. Avec « website » par erreur, Meta traite l’événement comme une conversion web : les campagnes d’installation mobile ne voient plus aucune conversion et l’attribution échoue.
Autres paramètres importants
- event_time : timestamp Unix en secondes, pas en millisecondes.
Date.now()renvoie des millisecondes : divisez par 1000. - event_id : identifiant unique pour la déduplication, déterministe (type d’événement + identifiant métier + horodatage), par exemple
purchase_ORDER789_1701234567. - event_source_url : le deep link ou la route de l’app où l’événement a eu lieu.
- opt_out : un simple indicateur sur l’événement, pas un mécanisme de consentement. Si l’utilisateur a refusé, n’envoyez pas l’événement.
- client_ip_address et client_user_agent : à récupérer côté backend, pas dans l’app (anti-usurpation, géolocalisation fiable, format cohérent).
Tester et déboguer
- Dans Events Manager, ouvrez « Test Events » et copiez le code de test (par exemple
TEST12345). - Ajoutez
test_event_codeà votre payload et envoyez l’événement. - Vérifiez qu’il apparaît, sans avertissement ni erreur, avec tous les paramètres requis.
L’Event Match Quality n’est pas affichée pour les événements d’app : contrôlez la livraison et la correspondance dans Test Events et les diagnostics du dataset.
| Symptôme | Causes probables |
|---|---|
| Rien dans Test Events | Mauvais dataset ou dataset non lié à l’app, token invalide, event_time en millisecondes, paramètres requis manquants. |
| Mauvaise correspondance | Ni MADID ni anon_id, pas d’Advanced Matching, hachage sans normalisation, IP ou User Agent absents. |
| Événements visibles mais pas dans Ads Manager | action_source à « website », événement hors fenêtre d’attribution, utilisateur ayant refusé le tracking publicitaire. |
Les huit pièges classiques
- Format du MADID : tirets retirés ou majuscules, au lieu de minuscules avec tirets.
- Hacher avant de normaliser :
" Test@Example.com "haché tel quel ne correspondra à rien. - Timestamp en millisecondes au lieu de secondes.
- Pas d’anon_id de secours : avec l’ATT refusé, le MADID vaut « 0000… » et l’événement est orphelin.
- action_source = « website » pour un événement d’app.
- extinfo incomplet : trois valeurs au lieu de seize.
- Pays non haché : Meta attend le SHA-256 du code ISO en minuscules.
- Lire l’IDFA sans demander l’ATT : il faut d’abord obtenir l’autorisation, et seulement si le statut est « authorized » lire l’IDFA.
Performance, montée en charge et supervision
- Dans l’app : collectez les données d’appareil une fois au lancement et mettez-les en cache, ne bloquez pas le thread d’interface, envoyez en arrière-plan.
- Au-delà de 10 000 événements par minute : envoi par lots (jusqu’à 1 000 événements), file d’attente (Redis, RabbitMQ) et nouvelle tentative avec backoff exponentiel, avec une « dead letter queue » pour les échecs définitifs.
- À superviser : taux de livraison, diagnostics de correspondance du dataset, latence app vers backend vers Meta, erreurs par type, événements dont des champs critiques manquent.
Checklist avant la mise en production
- MADID au bon format, jamais l’identifiant nul ;
anon_idgénéré et conservé. extinfo: 16 valeurs, dans l’ordre, toutes en chaînes.- Données personnelles normalisées puis hachées en SHA-256, hachage vérifié à la main.
action_sourceà"app",event_timeen secondes,event_idunique et déterministe.- IP et User Agent présents, drapeaux de tracking (
application_tracking_enabled,advertiser_tracking_enabled) corrects. - Événements visibles dans Test Events.
Conclusion
La CAPI sans SDK apporte un contrôle total, un point unique pour appliquer le consentement, une app plus légère et une architecture unifiée web et mobile. En échange, elle demande de recréer à la main une grande partie du travail du SDK, avec un fort risque d’échec d’attribution au moindre paramètre erroné, et plus d’infrastructure à maintenir. Si vous débutez sur Meta Ads en mobile, utilisez le SDK. Si vous avez besoin de contrôle maximal, de conformité ou d’une architecture particulière, l’approche CAPI seule est puissante une fois bien en place.
À retenir : MADID plus anon_id (toujours un secours face à l’ATT), extinfo (ne pas l’omettre), normaliser puis hacher, action_source à « app », et beaucoup de tests avant la production.
Sources
- Vue d’ensemble de la Conversions API : https://developers.facebook.com/documentation/ads-commerce/conversions-api
- Paramètres d’événement serveur : https://developers.facebook.com/documentation/ads-commerce/conversions-api/parameters/server-event
- Paramètres d’information client : https://developers.facebook.com/documentation/ads-commerce/conversions-api/parameters/customer-information-parameters
- Événements d’app (CAPI) : https://developers.facebook.com/documentation/ads-commerce/conversions-api/app-events
- Paramètres d’événements d’app : https://developers.facebook.com/documentation/app-events/getting-started-app-events-android
- Bonnes pratiques CAPI : https://developers.facebook.com/documentation/ads-commerce/conversions-api/best-practices
- Advanced Matching : https://developers.facebook.com/documentation/meta-pixel/advanced/advanced-matching
- Déduplication : https://developers.facebook.com/documentation/ads-commerce/conversions-api/deduplicate-pixel-and-server-events
- App Tracking Transparency (Apple) : https://developer.apple.com/documentation/apptrackingtransparency
- Identifiant publicitaire Android : https://support.google.com/googleplay/android-developer/answer/6048248
Pour aller plus loin, lisez la version complète de l’article (en anglais) : The Complete Guide to Meta CAPI for Mobile Apps Without SDK.
Besoin d’aide sur ce sujet ? Consultant GA4 et audit analytics.