SDK
SDK serveur Node.js
Signalez inscriptions et achats depuis un backend Node.js avec @traqio/node, et rattachez chacun à son clic publicitaire. Express, Next.js, NestJS, Fastify.
@traqio/node est le SDK Traqio pour les backends Node.js — la moitié serveur du SDK web de Traqio. La conversion qui compte sur un site — une inscription, un abonnement, un achat — a lieu sur votre backend, là où l’id utilisateur est connu. Ce paquet la signale avec le trackID, le click id de la publicité que @traqio/web a gardé en stockage first-party depuis la page d’arrivée, et Traqio crédite la campagne qui a amené l’utilisateur — de façon déterministe, par l’id de ce clic, jamais par une estimation. Aucune dépendance ; Express, Next.js, NestJS, Fastify ou node:http seul.
Disponible sur npm
@traqio/node · version 0.1.0Niveau de preuve : Testé unitairement, et exécuté de bout en bout contre une API Traqio en fonctionnement : inscription avec un trackID → attribuée.Prérequis#
- Node.js 18 ou plus (le SDK utilise le
fetchnatif). ESM et CommonJS, avec les types TypeScript. - Une clé serveur pour le produit (
tq_sk_…). Ni la clé SDK de l’app ni une clé web ne fonctionnent ici. - @traqio/web sur le site, et des publicités qui pointent vers des liens de tracking avec une destination site web — c’est ainsi que le trackID arrive jusqu’à votre formulaire d’inscription.
Mise en place#
Créer une clé serveur#
Dans la console, ouvrez Intégrations → SDK et créez une clé serveur. Elle n’est affichée qu’une fois : placez-la dans l’environnement du backend, jamais dans le code ni dans une page. Traqio n’en garde qu’une empreinte ; une clé perdue est remplacée par une nouvelle, et l’ancienne révoquée.
TRAQIO_SECRET_KEY=tq_sk_...Installer le paquet#
npm install @traqio/nodeCréer le client#
import { Traqio } from "@traqio/node"; // One client per process. The key comes from the server's environment, never from code. export const traqio = new Traqio({ secretKey: process.env.TRAQIO_SECRET_KEY! });Le client ne garde aucune connexion ouverte : un par processus suffit. Il refuse une clé web (
tq_web_…) et refuse de démarrer dans un navigateur.Signaler l’inscription avec son trackID#
Appelez
signup()une fois, là où le compte est créé. @traqio/web écrit le trackID dans le cookietraq_clicksur votre propre domaine : le navigateur l’envoie donc avec la requête d’inscription.import { trackIdFromRequest } from "@traqio/node"; import { traqio } from "./traqio"; app.post("/signup", async (req, res) => { const user = await users.create(req.body); // The trackID arrives with the request: @traqio/web set the traq_click cookie. const result = await traqio .signup({ userId: user.id, trackId: trackIdFromRequest(req) }) .catch((err) => logger.warn(err)); // never fail the sign-up itself res.status(201).json(user); });// app/api/signup/route.ts (Next.js App Router) import { cookies } from "next/headers"; import { TRACK_ID_COOKIE } from "@traqio/node"; import { traqio } from "@/lib/traqio"; export async function POST(request: Request) { const user = await users.create(await request.json()); const trackId = (await cookies()).get(TRACK_ID_COOKIE)?.value ?? null; await traqio.signup({ userId: user.id, trackId }).catch(console.warn); return Response.json(user, { status: 201 }); }// users.service.ts (NestJS) import { Injectable, Logger } from "@nestjs/common"; import { Traqio } from "@traqio/node"; @Injectable() export class UsersService { private readonly traqio = new Traqio({ secretKey: process.env.TRAQIO_SECRET_KEY! }); private readonly log = new Logger(UsersService.name); async register(dto: RegisterDto, trackId: string | null): Promise<User> { const user = await this.users.create(dto); await this.traqio.signup({ userId: user.id, trackId }).catch((e: Error) => this.log.warn(e.message)); return user; } }Si le backend est sur un autre domaine que le site, le cookie ne lui parvient pas : envoyez plutôt
Traqio.getTrackId()depuis le navigateur avec le formulaire. L’inscription est le moment où l’utilisateur est acquis ; le dernier clic qui la précède, dans votre fenêtre d’attribution, reçoit le crédit.Suivre les conversions#
await traqio.track({ userId: user.id, name: "purchase", value: 49, currency: "EUR", transactionId: paymentIntent.id, // one payment, one row — whoever reports it store: "stripe", });Les conversions suivent l’utilisateur : elles sont créditées à la campagne qui l’a acquis. Plusieurs événements peuvent partir en un seul appel :
await traqio.send({ userId: user.id, events: [ { name: "trial_started" }, { name: "purchase", value: 49, currency: "EUR", transactionId: invoice.id, store: "stripe" }, ], });
Ce que dit la réponse#
const result = await traqio.signup({ userId: user.id, trackId });
result.trackId; // "matched" | "unknown" | "expired" | "claimed" | "refused" (absent without a trackID)
result.acquired; // true: the sign-up is recorded
result.attribution; // { type: "deterministic", network: "meta", campaignId: "…", adsetId, adId }| trackId | Signification |
|---|---|
matched | Le clic appartient désormais à cet utilisateur. Une nouvelle tentative répond de nouveau matched. |
unknown | Aucun clic de ce produit n’a cet id — une faute de frappe, le lien d’un autre produit, ou une valeur que @traqio/web n’a pas écrite. |
expired | Le clic est plus ancien que la fenêtre d’attribution du produit. |
claimed | Un autre utilisateur, ou une installation d’app, possède déjà ce clic. Un clic crédite une seule identité. |
refused | Le clic a échoué à une règle anti-fraude (il est passé par un réseau d’anonymisation). Il est compté, jamais crédité. |
Ce qu’il fait aujourd’hui, et ce qu’il ne fait pas#
| Aujourd’hui | |
|---|---|
| Rattacher une inscription à son clic, et dire qui a été crédité | Oui — dans la réponse |
| Enregistrer les achats avec valeur, devise et id de paiement, dédoublonnés | Oui |
| Laisser intacts les installations, revenus et ROAS de vos apps | Oui — jamais rattachés à une installation d’app |
| Afficher les conversions web dans les rapports de la console | Oui — par campagne, à côté des installations |
| Envoyer les conversions web à Meta ou TikTok | Pas encore |
Les conversions envoyées ici apparaissent dans Événements en direct, marquées comme non rapprochées d’une installation — c’est normal pour un site web. Une inscription créditée à une campagne est comptée dans le rapport ROAS comme une conversion web de cette campagne, à côté de ses installations, et la réponse de chaque appel vous donne l’attribution tout de suite.
Erreurs et nouvelles tentatives#
Une erreur réseau, un timeout, un 429 ou un 5xx est retenté — deux fois par défaut, avec le même corps, donc rien n’est compté deux fois. Un 4xx (clé erronée ou révoquée, payload invalide) n’est pas retenté. Les deux se terminent par une TraqioError :
import { TraqioError } from "@traqio/node";
try {
await traqio.signup({ userId: user.id, trackId });
} catch (err) {
if (err instanceof TraqioError) {
// err.status: 401 = wrong or revoked key, 400 = invalid payload, undefined = network
logger.warn({ status: err.status, body: err.body }, err.message);
}
}Signalez après votre propre écriture
Référence de l’API#
interface TraqioOptions {
secretKey: string; // the server key, tq_sk_… — required
endpoint?: string; // default "https://api.traqio.app"
timeoutMs?: number; // per attempt — default 10000
maxRetries?: number; // after a network error, a 429 or a 5xx — default 2
fetch?: typeof fetch; // a custom fetch (proxies, tests)
}| Méthode | Ce qu’elle fait |
|---|---|
signup({ userId, trackId?, events? }) | Signale l’inscription — l’acquisition — avec son trackID. La première est conservée. |
track({ userId, name, value?, currency?, transactionId?, store?, eventId?, trackId? }) | Enregistre une conversion d’un utilisateur connu. |
send({ userId, trackId?, signup?, events? }) | L’appel brut derrière les deux, pour plusieurs événements à la fois. |
trackIdFromRequest(req) | Le trackID tiré des cookies d’une requête (Express, Fastify, Koa, node:http, une Request Fetch), ou null. |
trackIdFromCookieHeader(header) | La même chose, depuis un en-tête Cookie brut. |
Niveau de preuve
matched avec sa campagne, un achat signalé deux fois a été stocké une fois, une clé erronée a été refusée. Pas encore exécuté dans le backend de production d’un client.Questions fréquentes#
- Pourquoi un SDK serveur a-t-il besoin de sa propre clé ?
- Parce que c’est la seule autorisée à rattacher une conversion à un clic publicitaire. La clé web de @traqio/web est dans votre page, où tout le monde peut la lire : elle ne peut donc jamais rien attribuer. La clé serveur reste sur votre backend, est envoyée dans un en-tête Authorization et n’est conservée par Traqio que sous forme d’empreinte.
- Puis-je utiliser @traqio/node dans le navigateur ?
- Non. Il refuse de démarrer quand il trouve un document de navigateur, et il refuse une clé web. Dans le navigateur, utilisez @traqio/web, qui garde le trackID jusqu’à ce que la requête d’inscription le porte à votre backend.
- Et si le visiteur ne vient pas d’une publicité ?
- Passez un trackId null, ou aucun. L’inscription est quand même enregistrée, et l’utilisateur est organique : aucune campagne n’est créditée.
- Une nouvelle tentative compte-t-elle un achat deux fois ?
- Non. Chaque appel génère ses ids d’événement une seule fois et renvoie exactement le même corps, et un achat est aussi identifié par son transactionId : le même paiement signalé deux fois est stocké une fois.