MetrikStack
Sources

Vercel

Zero-code source: point a Vercel Log Drain at MetrikStack and every request through Vercel's proxy becomes a pageview.

A Vercel Log Drain forwards your deployment's logs to any HTTPS endpoint. Point one at POST /v1/logs/vercel and MetrikStack turns the request logs into pageviews: no code in your app, no script on the page, nothing to deploy.

Create a source of kind logs for the site in the dashboard to get an ingest token (in_…).

Set up the drain

  1. Vercel dashboard → your team → Observability → Drains → Add Drain, choose Logs and a Custom endpoint.
  2. Endpoint: https://api.example.com/v1/logs/vercel
  3. Format: NDJSON (a JSON array is accepted too).
  4. Sources: Static and Function (lambda/edge). Build, firewall and external logs can stay off; they are discarded server-side anyway.
  5. Environments: Production (add Preview if you want preview traffic counted in the same site).
  6. Custom headers: Authorization: Bearer in_….
  7. Save. Vercel immediately verifies the endpoint (see below).

If the drain UI you are using cannot set headers, put the token in the URL instead; every /v1/logs/* endpoint accepts it as a query parameter:

https://api.example.com/v1/logs/vercel?token=in_...

A URL carrying a token ends up in Vercel's project settings and in request logs; prefer the header when you have the choice, and rotate the token from the dashboard if it leaks.

Endpoint verification

Before it starts sending, Vercel probes the endpoint and requires the response to carry an x-vercel-verify header with the value shown in the Vercel UI. Set that value as vercel_verify in the source's config (dashboard or API).

The header is attached to every response of /v1/logs/vercel, including GET (which is what Vercel probes) and error responses, so a mistyped token never blocks verification.

Signature checking (optional)

Vercel signs each delivery with x-vercel-signature, the hex SHA-1 HMAC of the raw request body keyed with the drain's secret. Configure the secret as vercel_secret in the source config, and every unsigned or mismatched delivery is rejected with 403. With no secret configured the signature is ignored and the bearer token is the only credential.

What becomes a pageview

An entry counts when all of the following hold:

  • source is static, lambda or edge, and the entry has a proxy object, that is, it describes a real request rather than build output or a function's stdout;
  • proxy.method is GET or HEAD;
  • proxy.path is not an asset path (same extension and prefix rules as every other source; see Access logs);
  • the status is not a 3xx redirect.

Everything else is counted in the response's skipped figure, not rejected.

Fields map like this:

VercelMetrikStack
proxy.pathpath + query
proxy.hosthostname
proxy.methodmethod
proxy.statusCodestatus_code
proxy.userAgent[0]user agent (bot classification, device, browser)
proxy.refererreferrer
proxy.clientIpvisitor hash + GeoIP country, then dropped
proxy.vercelCache, proxy.cacheIdcache_status
timestamp (ms)event timestamp
requestIdrequest_id

proxy.region is the Vercel edge region (fra1), not the visitor's location, so it is not stored as a region; country comes from the client IP at ingest.

Drains and the request-id merge

The drain only ever sees requests that go through Vercel's proxy. Traffic served by another CDN in front of Vercel, or by a rewrite to an external origin, never reaches it.

Vercel's requestId is the platform's own id. It is not the X-MetrikStack-Request-Id that the Node SDK sets on the response, and a drain entry carries no application response headers, so the two ids cannot be matched. In practice:

  • Drain as the only source: every line is a pageview. This is the simplest and most accurate setup.
  • Drain plus the tracker script or the Node SDK: there is no shared request id, so merging falls back to the 2 s dedupe window on (site, visitor, path). That is good enough for most sites, but a drain is usually better run as a standalone source, or as a secondary source for the cached/bot traffic the script never sees.

Response

{ "accepted": 42, "skipped": 118, "rejected": [] }

skipped covers build logs, function output, assets, redirects and non-GET requests. Batches arrive in near real time; Vercel retries failed deliveries, so a short outage on our side does not lose data.

On this page