Skip to main content

POST /v1/events

A general-purpose event tracker. Use it for anything you want analytics on that isn't a signup:

  • Page views
  • Lead form submissions
  • Click-tracking on landing pages
  • Custom merchant-defined events

If your data has a meaningful user identity (email/phone), use /v1/signups instead — that gives you per-user dedup. /v1/events is for higher-volume, identity-less data.

Auth

Public key + HMAC. Same scheme as /v1/signups. Your backend signs every call.

Body

{
"name": "lead_submitted",
"properties": { "plan": "pro", "country": "DZ", "form": "footer" },
"nonce": "32-hex-single-use"
}
FieldTypeRequiredNotes
namestring ≤ 64 charsEvent name. snake_case recommended.
propertiesobjectJSON-serializable values. Stored as-is.
nonce32-hex stringSingle-use per key, valid for 1 h

name is also indexed for fast filtering at query time. Stick to a small, stable vocabulary — avoid generating names dynamically (e.g., viewed_product_42 is bad; use name="viewed_product" with properties.product_id=42).

Response 202

{
"data": { "status": "queued", "kind": "event", "store_id": 13 },
"meta": { "request_id": "...", "api_version": "v1", "edge": true }
}

Same flow as signups: queued at the edge in ~30 ms, persisted to origin within 5 s.

Dedup

Single-layer: (store_id, nonce) UNIQUE. There's no email-based dedup. If you submit two events with the same nonce, the second is recorded as status = 'duplicate' and ignored. Otherwise every call counts.

Worked example: tracking page views (Node.js, server-side)

import crypto from 'node:crypto';

export async function trackEvent(name, properties = {}) {
const KEY_ID = process.env.DZ_PUBLIC_KEY;
const SECRET = process.env.DZ_SIGNING_SECRET;
const nonce = crypto.randomBytes(16).toString('hex');
const ts = Math.floor(Date.now() / 1000).toString();
const body = JSON.stringify({ name, properties, nonce });
const hash = crypto.createHash('sha256').update(body).digest('hex');
const sig = crypto.createHmac('sha256', SECRET).update(`${KEY_ID}\n${nonce}\n${ts}\n${hash}`).digest('hex');

await fetch('https://api.dzbuild.app/v1/events', {
method: 'POST',
headers: {
'Authorization': `DZ-Public ${KEY_ID}`,
'X-DZ-Timestamp': ts, 'X-DZ-Nonce': nonce, 'X-DZ-Signature': sig,
'Content-Type': 'application/json',
},
body,
});
}

// Inside Express middleware:
app.use((req, res, next) => {
trackEvent('page_view', { path: req.path, ua: req.get('user-agent') }).catch(() => {});
next();
});

Notice we don't await the call from the page-view middleware — fire-and-forget so the user's request isn't blocked. The edge returns in ~30 ms anyway, but this protects against transient network issues.

What counts toward your quota

Every successful /v1/events call increments usage.event.total AND usage.event.billable. There's no "free reads then billable writes" tier here — events are billable from request 1.

If event volume gets high, batch on your side: store events in your own queue, fire one /v1/events call per logical event in batches of 1 (we don't accept batches in v1; that's a v1.1 feature for true high-volume telemetry).

Errors

Same as /v1/signups. See Errors.

When NOT to use /v1/events

  • For storefront orders — those flow in via the storefront and trigger order.created webhooks automatically. Don't double-fire.
  • For server-internal events that don't relate to merchant data (CPU usage, cache hits). Use a real APM.
  • For massive volumes (>1M events / day). Build your own analytics pipeline and only export aggregates here.