Référence API Trustt SMS

Endpoint par endpoint, champ par champ · calée sur la doc « API Trustt SMS » 09/06/2026 et sur un crawl exhaustif du compte entier (19/06/2026 : 54 entreprises, 98 marques, 334 campagnes, 9594 ambassadeurs, 4141 posts réels) complétant le relevé échantillonné du 10/06.

Référence technique exhaustive : 5 endpoints (4 en lecture, 1 en écriture), tous les champs typés, toutes les valeurs d'énumération observées en prod, et les pièges connus. Le code couleur : GET lecture   écriture.

Sommaire

Conventions générales

ÉlémentValeur
Base URL (prod)https://pro.trustt.io/
Préfixeapi_trusttsms/{action}
MéthodeGET uniquement (y compris l'écriture, cf. endpoint 5)
AuthentificationParamètre query key sur chaque appel (clé dédiée Trustt SMS, serveur uniquement)
TransportHTTPS obligatoire
Réponseapplication/json

Enveloppe de succès

{ "data": [ ... ] }

Une liste vide est un succès (HTTP 200), pas une 404. L'endpoint 5 renvoie un objet (data non tableau).

Paramètres UUID (filtres)

ParamFormatPour
coidUUID 36 car.companyUUID (Brands)
bidUUID 36 car.brandUUID (Campaigns)
caidUUID 36 car.campagneUUID (Ambassadors)
cidUUID 36 car.candidateUUID (filtre Ambassadors ; obligatoire pour save_receipt)

Codes HTTP & erreurs

Corps d'erreur : { "error": "Message court" }.

CodeSignificationQuand
200OKSuccès (y compris data vide)
401Unauthorizedkey absente / invalide → {"error":"Unauthorized"}
404Not FoundCompany / Brand / Campaign introuvable ou non éligible
422Unprocessable EntityParamètre UUID obligatoire manquant (coid/bid/caid)
500Internal Server ErrorErreur applicative non gérée (corps variable, non structuré)

1Companies GET

Entreprises avec au moins un contrat Community Builder (id_saas_product=1) démarré, non expiré, statut actif (2) ou en pause (1). Tri : nom croissant.

GET /api_trusttsms/companies?key=xxx

Réponse — champs

ChampTypeDescription
companyUUIDstring (UUID)Identifiant entreprise (sert de coid)
nomstringRaison sociale

2Brands GET

Marques de production (is_preprod=0) d'une entreprise, ayant au moins une campagne publiée. Tri : publicName croissant.

GET /api_trusttsms/brands?key=xxx&coid=<companyUUID>

Réponse — champs

ChampTypeDescription
brandUUIDstring (UUID)Identifiant marque (sert de bid)
publicNamestringNom public. Peut être vide en prod (à ne pas filtrer pour la récupération des campagnes).

3Campaigns GET

Campagnes publiées et éligibles d'une marque : id_progress entre 4 et 8, hors archivée (9) et annulée (99). Tri : name croissant.

GET /api_trusttsms/campaigns?key=xxx&bid=<brandUUID>

Réponse — champs

ChampTypeDescription
campaignUUIDstring (UUID)Identifiant campagne (sert de caid)
namestringNom de la campagne (peut contenir des emojis)
type{ typeId:int, label:string }Type de campagne. Piège : prod renvoie label, l'exemple de la doc libelle.
progress{ progressId:int, label:string }Avancement. Même piège label/libelle.
languageCodestringLangue (ex. fr)
nbTestDaysintDurée du test en jours (0 à 84 observés)
socialType{ socialTypeId:int, label:string }Plateforme cible (Instagram, TikTok)
mentionstringMention à utiliser (ex. @garanciabeauty)
hashtagstringHashtag (peut être vide)
ugcStartDate / ugcEndDatestring|nullFenêtre de publication
pickupStartDate / pickupEndDatestring|nullFenêtre de retrait
expectedUgcobjetLivrables attendus (voir ci-dessous)

expectedUgc (attendu de la campagne)

ChampTypeDescription
expectedPost[][{ socialTypeId, socialTypeLabel, postTypeId, postTypeLabel, quantity }]Posts attendus (ex. 2 × « Vidéo TikTok »)
reviewnull | { expected, hasVideo, hasTestingPhotos }Avis produit attendu (173 campagnes ; +photos 37, +vidéo 2)
merchantReviewnull | { expected, merchantName, merchantUrl }Avis marchand attendu (46 : Farmaline, Peggy Sage, Labo ACM, Cocooncenter, Trustpilot…)
testForms[][{ formId, name, nbDaysStart }]Questionnaires attendus (136 ; J+7/14/21/28…)

4Ambassadors GET

Ambassadeurs sélectionnés (testeurs) d'une campagne éligible, avec identité, téléphones et livrables réalisés. cid optionnel restreint à un ambassadeur. Tri : lastName puis firstName.

GET /api_trusttsms/ambassadors?key=xxx&caid=<campagneUUID>[&cid=<candidateUUID>]

Réponse — champs

ChampTypeDescription
candidateUUIDstring (UUID)Identifiant par participation (clé de dédup ; la même personne sur N campagnes a N candidateUUID)
emailstring|nullEmail
firstName / lastNamestringIdentité (peuvent être vides)
phoneE164string|nullEn prod : le vrai E.164 (« +33… »). 977/984.
phonestring|nullEn prod : format national (« 0… »). Piège : l'exemple de la doc inverse les deux.
receiptStatusintColis : 0 non reçu, 1 reçu, 2 non documenté (cf. pièges)
receiptDatestring|null« YYYY-MM-DD HH:mm:ss » sans fuseau ; null si non reçu
testEndDatestring|nullFin de mission, même format
realizedUgcobjetLivrables réalisés (voir ci-dessous)

realizedUgc (réalisé par l'ambassadeur)

ChampTypeDescription
posts[][{ socialTypeId, postTypeId, postTypeLabel, status, dateCrea }]Posts publiés. status : valid (4009) / pending (132), aucun rejeté. Jamais d'URL ni d'id de post (0 sur 4141).
reviewnull | { dateCrea, isVideo }Avis produit réalisé (4122 remplis sur le compte)
merchantReview[][] | [{ dateCrea }]Avis marchand réalisé (491 entrées)
testForms[][] | [{ formId, name, dateCrea }]Questionnaires remplis. Bien rempli en prod (4428 entrées) — pas vide contrairement à un ancien relevé.
Trouvaille majeure (crawl 4141 posts) : un post réalisé ne porte jamais d'URL ni d'identifiant (0 sur 4141). Trustt suit les posts par type, pas par lien, et les auto-détecte (des milliers en valid sans rien pousser). Aucune URL nulle part non plus dans les avis. C'est le prérequis bloquant de la remontée des publications (cf. onglet « Remontée publications »).

Règle dates : si receiptStatus=0 ou pas de date, receiptDate/testEndDate peuvent être null/vides. cid fourni mais inexistant → 200 + {"data":[]}.

5save_receipt écriture

Déclare la réception / le retrait du produit pour un ambassadeur (ajouté le 08/06/2026). Effet de bord côté Trustt : pose la réception ET lance la période de test. À n'appeler qu'avec confirmation explicite.

POST /api_trusttsms/save_receipt?key=xxx&cid=<candidateUUID>&rd=<Y-m-d H:i:s>
ParamFormatObligatoireDescription
cidUUIDouicandidateUUID de l'ambassadeur
rd« Y-m-d H:i:s »ouiDate de réception

Réponse (objet, pas tableau)

{ "data": { "uuid_candidate": "…", "date_test_start": "2026-06-05 12:10:00", "date_test_end": "2026-06-12 23:59:59" } }
Vérifié : appel en POST, paramètres key + cid + rd (le double cid de l'exemple de la doc était une coquille). cid inconnu → HTTP 404 {"error":"Ambassador not found"} (statut terminal côté Trustt SMS : aucun retry).

6cancel_receipt écriture

Annule la réception / le retrait d'un ambassadeur (ajouté le 23/06/2026) : remet receiptStatus à 0 et date_test_start à null. Appelé quand un retrait déjà remonté est décoché localement.

POST /api_trusttsms/cancel_receipt?key=xxx&cid=<candidateUUID>
ParamFormatObligatoireDescription
cidUUIDouicandidateUUID de l'ambassadeur

Réponse (objet, pas tableau)

{ "data": { "uuid_candidate": "…", "date_test_start": null, "date_test_end": "…" } }
Route vérifiée empiriquement (23/06) : la vraie route est cancel_receipt (le changelog Trustt écrivait cancel_recipt, coquille : cette route renvoie un 200 HTML vide, donc inexistante). save_receipt sans rd n'annule pas non plus (200 HTML). cid inconnu → 404 « Ambassador not found » (traité comme « rien à annuler »).

Catalogue des valeurs réelles

Crawl exhaustif du compte entier le 19/06/2026 (occurrences entre parenthèses) ; les enums campagne viennent du relevé échantillonné du 10/06. Source machine : docs/trustt-data-cases.json + docs/TRUSTT-DATA-CASES.md du dépôt.

Type de campagne (typeId)

idLibellé
1Produit (126)
2Influence (109)
3Evènement (12)
4Produit acheté (2)
5Test d'usage (13)
6Achat vérifié (2)

Avancement (progressId)

idLibellé
4Inscription en cours (55)
5Sélection du panel (40)
6Produits à envoyer (doc, non observé)
7Test en cours (69)
8Terminée (100)

Plateforme (socialType) & type de post (postTypeId)

postTypeIdLibelléAttendu (campagnes)Réalisé (posts)
1Articlejamais5
3Post Instagram541415
4Story Instagramjamais366
5Post Facebookjamais55
6non observé (libellé inconnu)0
7Vidéo Youtubejamais14
8Post Pinterestjamais2
9Vidéo TikTok49 (+1 en qty 2)2284

socialTypeId réalisés observés : 1 (Article/web), 2 (Instagram), 3 (Facebook), 5 (Youtube), 6 (Pinterest), 7 (TikTok). Plateformes ciblées (campagnes) : Instagram (2), TikTok (7). Statut de post : valid (4009) / pending (132), aucun rejeté. Quantité attendue : 1 (quasi toujours), 2 (rare). L'attendu formel (Post Instagram, Vidéo TikTok) est plus étroit que le réalisé : Story, Facebook, Youtube, Article, Pinterest sont publiés sans être formellement attendus.

Briques de contenu réalisé (compte entier)

BriqueForme réaliséeRéalisé
Post (posts[]){ socialTypeId, postTypeId, postTypeLabel, status, dateCrea } — sans URL4141
Avis produit (review){ dateCrea, isVideo }4122
Avis marchand (merchantReview[]){ dateCrea }491
Formulaire (testForms[]){ formId, name, dateCrea }4428

Colis (receiptStatus)

ValeurSensOccurrences (compte entier)
0Non reçu1674
1Reçu7916
2Non documenté — à clarifier4

Pièges connus

PiègeDétailNotre parade
phoneE164 / phone inversésL'exemple de la doc met l'E.164 dans phone ; la prod le met dans phoneE164.On retient le champ qui commence par « + », l'autre en national de secours.
label vs libelleProd renvoie label dans type/progress ; la doc montre libelle.Schéma qui accepte les deux, normalise vers un seul.
receiptStatus = 2Valeur non documentée (2 cas).Affichée « Statut 2 », jamais mappée à reçu/non reçu ; filtre « reçu » = ===1.
Posts sans URLUn post réalisé n'a jamais d'URL ni d'id (0 sur 4141). Trustt suit par type, pas par lien, et auto-détecte les posts.Verdict de publication au type + quantité ; remontée des liens impossible à réconcilier sans URL (cf. onglet dédié).
Types réalisés > attendusStory / Facebook / Youtube / Article / Pinterest réalisés alors que l'attendu n'est que Instagram / TikTok.On affiche postTypeLabel tel quel.
testForms réalisés non videsUn ancien relevé les voyait vides ; le crawl complet en trouve 4428 ({ formId, name, dateCrea }).On lit bien realizedUgc.testForms comme livrable.
publicName de marque videCertaines marques sans nom public.Ne pas filtrer là-dessus pour récupérer les campagnes.
Dates sans fuseau« YYYY-MM-DD HH:mm:ss », pas ISO, sans timezone.Parsing au mapping (jour pour le pivot).
Résolu par le crawl 19/06 : postTypeId 8 = Post Pinterest (et 1 = Article), receiptStatus=2 confirmé présent (4 cas sur tout le compte). Résolu le 23/06 : save_receipt (POST, key+cid+rd) et cancel_receipt (POST, key+cid ; le cancel_recipt du changelog était une coquille) vérifiés empiriquement. Questions encore ouvertes côté Trustt : sens exact de receiptStatus=2 · libellé de postTypeId 6 (toujours non observé) · progressId 6 · exposition de l'URL d'un post réalisé (prérequis à la remontée des publications).