Verify Node.js SDK events

An event moves through four stages between your track() call and Amplitude's servers. Verify each stage in order. The first stage where the event looks wrong is where the problem is.

For problems common to every Amplitude SDK, go to SDK troubleshooting and debugging. If behavior changed after an SDK upgrade, check the Node.js SDK release notes.

Turn on debug logging

Set logLevel to Debug while you verify. Debug logs include every public method call, the SDK's state before and after it, each event as plugins change it, and the upload result.

js
import { init, track, Types } from "@amplitude/analytics-node";

init("AMPLITUDE_API_KEY", {
  logLevel: Types.LogLevel.Debug,
});

track("Sign Up Completed", { plan: "starter" }, { user_id: "user-12345" });

The levels are 0 None, 1 Error, 2 Warn (default), 3 Verbose, and 4 Debug. Each message starts with Amplitude Logger. The default logger writes errors with console.error, warnings with console.warn, and Verbose and Debug messages with console.log. Filter both standard output and standard error on that prefix.

Debug logs contain your API key

At the Debug level, the SDK logs its configuration, including the API key, and every event payload. Return logLevel to Warn before you deploy, and don't ship Debug logs to a shared log store.

Check what the SDK captured

With debug logging on, each public call prints a block like this:

text
Amplitude Logger [Debug]: {
  "type": "invoke public method",
  "name": "track",
  "args": ["Sign Up Completed", { "plan": "starter" }, { "user_id": "user-12345" }],
  "stacktrace": ["at file:///srv/app/signup.mjs:6:7", "..."],
  "time": { "start": "2026-11-10T19:47:54.361Z", "end": "2026-11-10T19:48:04.383Z" },
  "states": {
    "before": { "config.apiKey": "...", "timeline.queue.length": 0 },
    "after": { "config.apiKey": "...", "timeline.queue.length": 0 }
  }
}

For methods that return a promise, such as track(), identify(), and flush(), the SDK prints the block after the promise resolves. A track block therefore appears after the upload attempt, about 10 seconds after the call.

Check the block for these problems:

Check how plugins transformed the event

At Verbose or Debug level, the SDK logs the event after each before and enrichment plugin, then the final event it hands to destinations:

text
Amplitude Logger [Log]: Timeline.apply: Event after before plugin 'context', event: {"time":1794340064240,"insert_id":"9dfa11d7-...","event_type":"Sign Up Completed","user_id":"user-12345",...}
Amplitude Logger [Log]: Timeline.apply: Final event before destinations, event: {...}

A plugin that returns null drops the event, and the log shows Event filtered out by with the plugin's name. The built-in context plugin adds time, insert_id, event_id, and library to every event.

To inspect events in code, add an enrichment plugin after init(). Enrichment plugins run after the built-in context plugin, so your plugin sees the fields the SDK added:

js
import { init, add, track } from "@amplitude/analytics-node";

await init("AMPLITUDE_API_KEY").promise;

add({
  name: "inspect-events",
  type: "enrichment",
  execute: async (event) => {
    console.log("Amplitude event:", JSON.stringify(event));
    return event;
  },
});

track("Sign Up Completed", { plan: "starter" }, { user_id: "user-12345" });

The plugin prints the event as the SDK sends it:

json
{
  "event_type": "Sign Up Completed",
  "user_id": "user-12345",
  "event_properties": { "plan": "starter" },
  "time": 1794340064240,
  "insert_id": "9dfa11d7-2592-47b5-ba83-a599db8f8536",
  "event_id": 0,
  "library": "amplitude-node-ts/1.5.83"
}

event_id is a counter that starts at 0 for each SDK instance. The SDK overwrites any event_id or library value you pass. Remove the inspection plugin when you finish, so event payloads don't reach your production logs.

Check what the SDK sent

A server process has no Network tab. To see the exact request body, point serverUrl at a local server that prints each request and answers like Amplitude:

js
// capture-server.mjs: run with node capture-server.mjs
import http from "node:http";

http
  .createServer((req, res) => {
    let body = "";
    req.on("data", (chunk) => (body += chunk));
    req.on("end", () => {
      console.log(req.method, req.url, body);
      const events = JSON.parse(body).events ?? [];
      res.writeHead(200, { "Content-Type": "application/json" });
      res.end(
        JSON.stringify({
          code: 200,
          events_ingested: events.length,
          payload_size_bytes: body.length,
          server_upload_time: Date.now(),
        }),
      );
    });
  })
  .listen(8080);

Then send events to it:

js
import { init, track } from "@amplitude/analytics-node";

init("AMPLITUDE_API_KEY", {
  serverUrl: "http://localhost:8080/2/httpapi",
});

track("Sign Up Completed", { plan: "starter" }, { user_id: "user-12345" });

The server prints one POST /2/httpapi request per upload, with a body like this:

json
{
  "api_key": "AMPLITUDE_API_KEY",
  "events": [
    {
      "time": 1794340064240,
      "insert_id": "9dfa11d7-2592-47b5-ba83-a599db8f8536",
      "event_type": "Sign Up Completed",
      "user_id": "user-12345",
      "event_properties": { "plan": "starter" },
      "event_id": 0,
      "library": "amplitude-node-ts/1.5.83"
    }
  ],
  "options": {},
  "client_upload_time": "2026-11-10T19:48:04.374Z"
}

The SDK sends plain JSON. It doesn't compress request bodies. options.min_id_length appears only when you set minIdLength. Remove serverUrl when you finish, so events go to Amplitude again.

Check what Amplitude received

Read the upload result

track(), identify(), groupIdentify(), setGroup(), and revenue() return an object whose promise resolves to a result with event, code, and message:

js
import { init, track } from "@amplitude/analytics-node";

init("AMPLITUDE_API_KEY");

const result = await track(
  "Sign Up Completed",
  { plan: "starter" },
  { user_id: "user-12345" },
).promise;

console.log(result.code, result.message);

For how the SDK retries each status, go to How the SDK uploads events.

Find the event in Amplitude

  1. In Amplitude, open User Lookup and search for the user_id or device_id you sent.
  2. Open the Activity tab and turn on Live event updates.
  3. Find the event by name, and confirm its properties.

If the upload result is 200 but the event doesn't appear, check these settings:

  • The API key belongs to the project you're viewing.
  • The project's data center matches serverZone.

Was this helpful?

For AI agents: a documentation index is available at /docs/llms.txt. Append .md to any page URL for markdown, or send Accept: text/markdown.

On this page