SDK
SDK de servidor Node.js
Reporta registros y compras desde un backend Node.js con @traqio/node y vincula cada uno a su clic en el anuncio. Express, Next.js, NestJS, Fastify.
@traqio/node es el SDK de Traqio para backends Node.js — la mitad servidor del SDK web de Traqio. La conversión que cuenta en un sitio web — un registro, una suscripción, una compra — ocurre en tu backend, donde se conoce el id de usuario. Este paquete la reporta con el trackID, el click id del anuncio que @traqio/web guardó en almacenamiento first-party desde la página de llegada, y Traqio acredita la campaña que trajo al usuario — de forma determinista, por el id de ese clic, nunca por una suposición. Sin dependencias; Express, Next.js, NestJS, Fastify o node:http simple.
Disponible en npm
@traqio/node · versión 0.1.0Nivel de prueba: Con pruebas unitarias y ejecutado de extremo a extremo contra una API de Traqio en marcha: registro con un trackID → atribuido.Requisitos#
- Node.js 18 o posterior (el SDK usa el
fetchintegrado). ESM y CommonJS, con tipos TypeScript. - Una clave de servidor para el producto (
tq_sk_…). Ni la clave SDK de la app ni una clave web funcionan aquí. - @traqio/web en el sitio, y anuncios que apunten a enlaces de seguimiento con un destino web — así es como el trackID llega a tu formulario de registro.
Configuración#
Crear una clave de servidor#
En la consola, abre Integraciones → SDK y crea una clave de servidor. Se muestra una sola vez: ponla en el entorno del backend, nunca en el código ni en una página. Traqio solo guarda su huella; una clave perdida se sustituye por una nueva y la antigua se revoca.
TRAQIO_SECRET_KEY=tq_sk_...Instalar el paquete#
npm install @traqio/nodeCrear el cliente#
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! });El cliente no mantiene ninguna conexión: basta con uno por proceso. Rechaza una clave web (
tq_web_…) y se niega a arrancar en un navegador.Reportar el registro con su trackID#
Llama a
signup()una vez, donde se crea la cuenta. @traqio/web escribe el trackID en la cookietraq_clickde tu propio dominio, así que el navegador lo envía con la petición de registro: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 el backend está en otro dominio que el sitio, la cookie no le llega: envía en su lugar
Traqio.getTrackId()desde el navegador con el formulario. El registro es el momento en que el usuario es captado; el último clic anterior, dentro de tu ventana de atribución, se lleva el crédito.Registrar las conversiones#
await traqio.track({ userId: user.id, name: "purchase", value: 49, currency: "EUR", transactionId: paymentIntent.id, // one payment, one row — whoever reports it store: "stripe", });Las conversiones siguen al usuario: se acreditan a la campaña que lo captó. Se pueden enviar varios eventos en una sola llamada:
await traqio.send({ userId: user.id, events: [ { name: "trial_started" }, { name: "purchase", value: 49, currency: "EUR", transactionId: invoice.id, store: "stripe" }, ], });
Qué te dice la respuesta#
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 | Significado |
|---|---|
matched | El clic pertenece ahora a este usuario. Un reintento vuelve a responder matched. |
unknown | Ningún clic de este producto tiene ese id — una errata, un enlace de otro producto o un valor que no escribió @traqio/web. |
expired | El clic es más antiguo que la ventana de atribución del producto. |
claimed | Otro usuario, o una instalación de app, ya tiene ese clic. Un clic acredita una sola identidad. |
refused | El clic no superó una regla antifraude (pasó por una red de anonimización). Se contabiliza, nunca se acredita. |
Qué hace hoy y qué no#
| Hoy | |
|---|---|
| Vincular un registro a su clic e indicar a quién se acreditó | Sí — en la respuesta |
| Registrar compras con valor, moneda e id de pago, sin duplicados | Sí |
| Dejar intactas las instalaciones, los ingresos y el ROAS de tus apps | Sí — nunca se vincula a una instalación de app |
| Mostrar las conversiones web en los informes de la consola | Sí — por campaña, junto a las instalaciones |
| Enviar las conversiones web a Meta o TikTok | Todavía no |
Las conversiones enviadas aquí aparecen en Eventos en directo, marcadas como no vinculadas a una instalación — lo esperado para un sitio web. Un registro acreditado a una campaña se cuenta en el informe de ROAS como una conversión web de esa campaña, junto a sus instalaciones, y la respuesta de cada llamada te da la atribución al instante.
Errores y reintentos#
Un error de red, un timeout, un 429 o un 5xx se reintenta — dos veces por defecto, con el mismo cuerpo, así que nada se cuenta dos veces. Un 4xx (clave errónea o revocada, payload no válido) no se reintenta. Ambos terminan en un 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);
}
}Reporta después de tu propia escritura
Referencia de la 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étodo | Qué hace |
|---|---|
signup({ userId, trackId?, events? }) | Reporta el registro — la captación — con su trackID. Se conserva el primero. |
track({ userId, name, value?, currency?, transactionId?, store?, eventId?, trackId? }) | Registra una conversión de un usuario conocido. |
send({ userId, trackId?, signup?, events? }) | La llamada en bruto detrás de ambos, para varios eventos a la vez. |
trackIdFromRequest(req) | El trackID de las cookies de una petición (Express, Fastify, Koa, node:http, una Request Fetch), o null. |
trackIdFromCookieHeader(header) | Lo mismo, a partir de una cabecera Cookie en bruto. |
Nivel de prueba
matched con su campaña, una compra reportada dos veces se guardó una sola vez, una clave errónea fue rechazada. Todavía no se ha ejecutado en el backend de producción de un cliente.Preguntas frecuentes#
- ¿Por qué un SDK de servidor necesita su propia clave?
- Porque es el único autorizado a vincular una conversión a un clic en un anuncio. La clave web de @traqio/web está en tu página, donde cualquiera puede leerla, así que nunca puede atribuir nada; la clave de servidor se queda en tu backend, se envía en una cabecera Authorization y Traqio solo guarda su huella.
- ¿Puedo usar @traqio/node en el navegador?
- No. Se niega a arrancar cuando detecta un documento de navegador, y rechaza una clave web. En el navegador, usa @traqio/web, que guarda el trackID hasta que la petición de registro lo lleva a tu backend.
- ¿Y si el visitante no vino de un anuncio?
- Pasa un trackId null, o ninguno. El registro se guarda igualmente, y el usuario es orgánico: no se acredita ninguna campaña.
- ¿Un reintento contará una compra dos veces?
- No. Cada llamada genera sus ids de evento una sola vez y reintenta exactamente el mismo cuerpo, y una compra se identifica además por su transactionId, así que el mismo pago reportado dos veces se guarda una sola vez.