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);