Skip to content
adscapi

Node.js

Use the adscapi SDK in a plain Node backend. One library, no per-framework wrapper — create the client once and call conversions.track() at the conversion point.

Install

shell
npm install adscapi
# Node 18+ (global fetch). Bun and Deno work the same way.

Requires Node 18+ (global fetch). The dispatch path uses only fetch and WebCrypto — no Node-only built-ins on the hot path.

Configure tokens

Export each platform’s secrets in the environment that runs your process. createAdscapi() reads process.env by default; pass an explicit bag for multi-tenant use.

env
# .env (or your process manager / secrets store)
META_CAPI_TOKEN=…
META_PIXEL_ID=…
# …plus whatever else `npx adscapi platforms` lists for your destinations
shell
npx adscapi check
npx adscapi verify

Track from a request handler

ts
import { createAdscapi } from 'adscapi';
import http from 'node:http';

// Create once at process start. Secrets default to process.env.
const ads = createAdscapi();

const server = http.createServer(async (req, res) => {
  if (req.method === 'POST' && req.url === '/checkout/complete') {
    const body = await readJson(req); // your body parser

    const results = await ads.conversions.track({
      name: 'purchase',
      value: body.amount,
      currency: body.currency ?? 'USD',
      transactionId: body.orderId,
      user: {
        email: body.email,          // raw — never pre-hash
        phone: body.phone,
        ip: req.socket.remoteAddress,
        userAgent: req.headers['user-agent'],
      },
      clickIds: {
        gclid: body.gclid,
        fbclid: body.fbclid,
      },
      consent: {
        adUserData: body.consent?.adUserData === true,
        adPersonalization: body.consent?.adPersonalization === true,
      },
    });

    // One platform failing never throws — inspect per-destination results.
    const failed = results.filter((r) => !r.ok);
    if (failed.length) console.error('adscapi failures', failed);

    res.writeHead(200, { 'content-type': 'application/json' });
    res.end(JSON.stringify({ ok: true, destinations: results }));
    return;
  }

  res.writeHead(404);
  res.end();
});

server.listen(3000);

// helper omitted — use your framework's body parser in real code
async function readJson(req) {
  const chunks = [];
  for await (const c of req) chunks.push(c);
  return JSON.parse(Buffer.concat(chunks).toString('utf8'));
}

Options

ts
// Log the fan-out without sending anything
await ads.conversions.track(event, { dryRun: true });

// Send a real event into Meta's Test Events view
await ads.conversions.track(event, { testEventCodes: { meta: 'TESTxxxxx' } });

// Skip one destination for this call
await ads.conversions.track(event, { destinations: { tiktok: false } });

List & verify from code

ts
// Which platforms are active right now?
console.log(ads.destinations.list());
// → [{ key: 'meta', support: 'real-capi', active: true }, …]

// Live credential check (hits each platform API)
const checks = await ads.destinations.verify();
console.log(checks);

Next