Installation guides
Node.js and server SDK (@metricraven/node)
On this page
@metricraven/node sends what only your backend knows: confirmed sign-ups, verified identities, payments and refunds. It runs anywhere fetch does, including Node 18+, Bun, Deno, Cloudflare Workers and Vercel Edge, and it has no dependencies.
It wraps the same secret-key endpoints as server events, identify and the Payments API, with types, timeouts and retries. Calls never throw into your request handler: each one returns a result.
Install
Add the package
terminalnpm install @metricraven/nodeSet your secret key on the server
Copy the site secret key (
ana_sec_…) from Settings and store it asMETRICRAVEN_SECRET_KEY. Never ship it to the browser or a mobile app.Send an event after the action succeeds
server.ts// lib/metricraven.ts — server only. Reads METRICRAVEN_SECRET_KEY. import { createMetricRaven, getVisitorId } from "@metricraven/node"; export const metricraven = createMetricRaven(); // In your sign-up route handler, after the account exists: await metricraven.track({ name: "signup", userId: user.id, visitorId: getVisitorId(request), // the tracker cookie, or null props: { plan: "free" }, });
Verify it works
The event appears in the Goal tab at the bottom of Traffic after refreshing. If it doesn't, log result.error and check it against the table under Results and errors below.
Calls
| Call | What it sends |
|---|---|
| track({ name, userId?, visitorId?, props?, timestamp? }) | A goal (server event). Needs userId or visitorId. Up to 10 props. |
| identify({ visitorId, userId, email?, name?, traits? }) | A verified link from a visitor to your user. It replaces any identity the browser claimed. |
| payment({ amount, currency, transactionId, visitorId?, userId?, plan?, customerId?, renewal?, isFreeTrial?, email? }) | A payment in major currency units. Repeats of a transactionId are ignored. Without visitorId or userId, email matches a visitor identified with it or a browser that called payment({ email }); see Attribute by email. |
| refund({ amount, currency, transactionId }) | A refund, with the cumulative amount refunded so far. |
| crawl({ url, userAgent, status?, ip? }) | One crawler request. Uses the bot token when one is set. For middleware, use @metricraven/ai-crawl. |
await metricraven.identify({
visitorId,
userId: user.id,
email: user.email,
name: user.name,
traits: { plan: "pro" },
});// After the payment succeeds. amount is in major units: 29 means $29.
await metricraven.payment({
amount: 29,
currency: "USD",
transactionId: order.id,
visitorId,
userId: user.id,
plan: "pro",
});
// Refunds send the cumulative amount refunded so far.
await metricraven.refund({ amount: 15, currency: "USD", transactionId: order.id });track, identify, payment, refund and crawl are also exported as functions that use a shared client configured from the environment, or from configure(options).
Bot token for crawler reports
If a server only reports crawlers, give it the site's ingest-only bot token (mrbot_…) as METRICRAVEN_BOT_TOKEN or botToken, instead of the secret key. It can only send crawl events.
| Configured | What happens |
|---|---|
| Bot token only | crawl() uses it. track, identify, payment and refund return bot_token_cannot_track (…_identify, …_payment, …_refund) without a request. |
| Bot token and secret key | crawl() uses the bot token; every other call uses the secret key. |
| siteId (METRICRAVEN_SITE_ID) | Optional. Crawl reports are refused (wrong_site) if the token belongs to another website. |
Attribute to the visit
getVisitorId(request) reads the visitor ID from a Fetch Request, a Node or Express request, Headers or a raw Cookie header. It checks the tracker's ana_vid cookie, then the metricraven_visitor_id cookie that the checkout recipes set. It returns null when there's neither.
window.analytics.getVisitorId() from the browser with your own request, or rely on userId after identify.Results and errors
const result = await metricraven.track({ name: "signup", userId: user.id });
if (!result.ok) console.warn("MetricRaven:", result.error, result.status);| error | Meaning |
|---|---|
| invalid_input | Rejected before sending, for example a bad event name, visitor ID or currency. |
| missing_secret_key / invalid_secret_key | No key, or not an ana_sec_ key. Nothing was sent. |
| invalid_bot_token | botToken isn't an mrbot_ token. Nothing was sent. |
| bot_token_cannot_track (…_identify, …_payment, …_refund) | Only a bot token is set, and it can only report crawlers. Nothing was sent. |
| unauthorized | The key wasn't accepted, for example after a rotation's grace period ended. |
| invalid_payload | The API rejected the body. |
| domain_not_allowed | The crawl URL's host isn't one of the site's domains. |
| tracking_paused | The plan's usage limit was reached. |
| bot_tracking_paused | crawl: the bot request allowance is used up until the reset. |
| bot_token_required | crawl: the website only accepts crawler reports sent with its bot token. |
| wrong_site | crawl: the bot token belongs to a different website than siteId. |
| timeout / network_error / http_5xx | No usable response after retries. |
Options
| Option | What it does |
|---|---|
| secretKey | Defaults to METRICRAVEN_SECRET_KEY. |
| botToken | Defaults to METRICRAVEN_BOT_TOKEN. Used by crawl() only. |
| siteId | Defaults to METRICRAVEN_SITE_ID. Optional check of the bot token's website. |
| apiHost | Defaults to METRICRAVEN_API_BASE, then https://metricraven.com. |
| timeoutMs | Per attempt. Defaults to 5000. |
| retries | Extra attempts after 5xx or network errors. Defaults to 2. 4xx responses are never retried. |
| retryDelayMs | First backoff wait. Defaults to 300, and doubles each time with jitter. |
| onError | Called with each failure: code, status and endpoint. Never includes the key or token. |
| throwOnError | Rejects instead of returning { ok: false }. |
| disabled | Skips the network, for tests and local development. |
| fetch | A custom fetch. |
Edge and serverless
Don't hold the response while a call runs. Hand the promise to waitUntil (Workers ctx, Next.js after() or Vercel's waitUntil):
// Cloudflare Workers: don't hold the response for analytics.
import { createMetricRaven } from "@metricraven/node";
export default {
async fetch(request, env, ctx) {
const metricraven = createMetricRaven({ secretKey: env.METRICRAVEN_SECRET_KEY });
ctx.waitUntil(metricraven.track({ name: "api_call", userId: "user_42" }));
return new Response("ok");
},
};Details
- After a timeout, payments and identify are retried, because the server deduplicates or overwrites them. Events and crawls aren't retried after a timeout, because they have no deduplication key.
- The unscoped
metricravenpackage exposes the same API atmetricraven/node. - ESM and CommonJS, with TypeScript types. It uses only
fetch, with no Node built-ins.