Requête exemple
Réponse exemple
Le endpoint POST `/v1/me/acquisition` estampille l'attribution first-touch (premier point de contact marketing) et l'identifiant de visiteur anonyme sur le profil de l'utilisateur actuellement connecté. Il est conçu pour être appelé une seule fois par inscription (en mode fire-and-forget depuis le formulaire d'inscription). L'opération est idempotente : une fois qu'un acquisition_source existe sur le profil, il n'est jamais écrasé. À la première estampille, le endpoint calcule également days_to_signup à partir du timestamp de capture de l'attribution et de la date de création du profil, puis émet en arrière-plan un événement de funnel signup_completed.
Authentification
Ce endpoint s'appuie sur la session Supabase de l'utilisateur (cookie de session), via requireUser(). Il n'exige aucun scope de clé API spécifique. Si la session est absente, la réponse est un 204 silencieux plutôt qu'une erreur d'authentification. L'écriture sur le profil est ensuite réalisée côté serveur avec le client de service, ciblant exclusivement l'identifiant de l'utilisateur authentifié (auth.user.id).
Corps de la requête
Le corps est un objet JSON optionnel. Un corps invalide ou vide est toléré (interprété comme {}). Tous les champs textuels sont nettoyés (trim) et tronqués : 200 caractères par défaut, 512 pour referrer et landing, 64 pour visitor_id. Une chaîne vide après trim devient null.
| Champ | Type | Requis | Description |
|---|---|---|---|
| attribution | objet | null | Non | Bloc d'attribution first-touch. Si absent ou null, la source par défaut devient direct. |
| attribution.source | string | null | Non | Source d'acquisition (ex. google, facebook). Par défaut direct si vide. Tronqué à 200 caractères. |
| attribution.medium | string | null | Non | Canal/medium (ex. cpc, organic, email). Tronqué à 200 caractères. |
| attribution.campaign | string | null | Non | Nom de campagne marketing. Tronqué à 200 caractères. |
| attribution.term | string | null | Non | Mot-clé ou terme de recherche. Tronqué à 200 caractères. |
| attribution.content | string | null | Non | Variante de contenu/créa publicitaire. Tronqué à 200 caractères. |
| attribution.gclid | string | null | Non | Identifiant de clic Google Ads. Tronqué à 200 caractères. |
| attribution.fbclid | string | null | Non | Identifiant de clic Meta/Facebook. Tronqué à 200 caractères. |
| attribution.referrer | string | null | Non | URL du référent. Tronqué à 512 caractères. |
| attribution.landing | string | null | Non | URL de la page d'atterrissage. Tronqué à 512 caractères. |
| attribution.at | number | null | Non | Timestamp epoch (ms) de capture de l'attribution. Sert à calculer days_to_signup. |
| visitor_id | string | null | Non | Identifiant de visiteur anonyme. Tronqué à 64 caractères. |
Réponse
La réponse JSON contient un champ booléen ok et un champ status. Trois issues sont possibles en cas de succès : status vaut `stamped` lorsque l'attribution vient d'être enregistrée pour la première fois ; `already_stamped` lorsque le profil portait déjà une source d'acquisition (aucune modification n'est faite, conformément à l'idempotence) ; et un 204 sans corps lorsqu'aucune session n'est active. Lors d'une estampille réussie, le serveur calcule et persiste un objet acquisition_source complet (avec captured_at_iso, days_to_signup et visitor_id) puis insère un événement de funnel signup_completed en arrière-plan, sans bloquer la réponse.
Erreurs
| Code | Quand | Résolution |
|---|---|---|
| 204 No Content | Aucune session active au moment de l'appel. | Comportement attendu. Ne pas réessayer : l'utilisateur sera traité comme direct. |
| 404 Not Found | Aucun profil (coffrify_profiles) ne correspond à l'utilisateur authentifié (réponse { ok: false, error: 'profile_not_found' }). | Vérifier que le profil de l'utilisateur a bien été créé avant d'appeler ce endpoint. |
Voir aussi
/v1/me/profileRécupérer le profil de l'utilisateur connecté, y compris la source d'acquisition estampillée./v1/funnel/eventsÉmettre manuellement un événement de funnel, comme celui généré automatiquement par ce endpoint.