Traqio.

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

Paquete @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 fetch integrado). 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#

  1. 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.

    .env
    TRAQIO_SECRET_KEY=tq_sk_...
  2. Instalar el paquete#

    npm install @traqio/node
  3. Crear 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.

  4. Reportar el registro con su trackID#

    Llama a signup() una vez, donde se crea la cuenta. @traqio/web escribe el trackID en la cookie traq_click de 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);
    });

    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.

  5. 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 }
trackIdSignificado
matchedEl clic pertenece ahora a este usuario. Un reintento vuelve a responder matched.
unknownNingún clic de este producto tiene ese id — una errata, un enlace de otro producto o un valor que no escribió @traqio/web.
expiredEl clic es más antiguo que la ventana de atribución del producto.
claimedOtro usuario, o una instalación de app, ya tiene ese clic. Un clic acredita una sola identidad.
refusedEl 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 duplicadosSí
Dejar intactas las instalaciones, los ingresos y el ROAS de tus appsSí — nunca se vincula a una instalación de app
Mostrar las conversiones web en los informes de la consolaSí — por campaña, junto a las instalaciones
Enviar las conversiones web a Meta o TikTokTodaví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

Llama a Traqio una vez guardada la cuenta o el pago, y nunca dejes que un error de Traqio lo haga fallar.

Referencia de la API#

TraqioOptions
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étodoQué 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

Con tests unitarios, y ejecutado de principio a fin — builds ESM y CommonJS — contra una API de Traqio en funcionamiento: un registro con trackID volvió 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.

Actualizado el 16 de octubre de 2026