Skip to content
LogZuno

Send events with the event tracking API

Send one event to /v1/log or up to 100 to /v1/batch, with users, properties, notifications and safe retries.

An event records that something happened in your product. Every event has a name and a channel; everything else is optional.

Send one event

POST https://api.logzuno.app/v1/log with an ingest key in the Authorization header:

curl -sS -X POST "${LOGZUNO_ENDPOINT:-https://api.logzuno.app/v1}/log" \
  -H "Authorization: Bearer $LOGZUNO_API_KEY" \
  -H "Content-Type: application/json" \
  -w "\nHTTP %{http_code}\n" \
  -d '{
    "channel": "signups",
    "event": "User signed up",
    "user_id": "user-123",
    "description": "A new user created an account",
    "icon": "🎉",
    "notify": true,
    "tags": { "plan": "growth", "source": "website" }
  }'
FieldRequiredWhat it is
channelYesWhere the event belongs: signups, billing, errors. Created the first time it is used
eventYesWhat happened, in words: User signed up
user_idNoYour ID for the person who did it. Links the event to a user
group_idNoThe account or company it belongs to. See groups
descriptionNoOne line shown under the event in the feed
iconNoOne emoji shown beside it
notifyNotrue sends a browser push to members who opted in
tagsNoProperties: a flat object of strings, numbers, booleans or null (properties works too)
timestampNoUnix seconds, if the event happened earlier; defaults to now
insert_idNoA UUID. The same insert_id is stored once, so retries are safe

LogZuno answers 202 Accepted with the event's insert_id. The event is in the live feed and in charts within seconds.

Send up to 100 at once

POST /v1/batch takes an events list of up to 100 events in the same shape. A batch counts as one request against the limit of 5 requests a minute per project, so batching lets one project send up to 500 events a minute.

curl -sS -X POST "${LOGZUNO_ENDPOINT:-https://api.logzuno.app/v1}/batch" \
  -H "Authorization: Bearer $LOGZUNO_API_KEY" \
  -H "Content-Type: application/json" \
  -w "\nHTTP %{http_code}\n" \
  -d '{
    "events": [
      { "channel": "billing", "event": "Invoice created", "user_id": "user-123", "tags": { "amount": 320 } },
      { "channel": "billing", "event": "Payment received", "user_id": "user-123", "notify": true, "tags": { "amount": 320 } }
    ]
  }'

A batch is all or nothing: if any event is invalid, LogZuno rejects the whole request with 422 and names the failing fields (events.1.channel), so you can fix it and resend the batch without creating duplicates.

Retry on 429

When a project goes over its request limit, LogZuno answers 429 Too Many Requests with a Retry-After header in seconds. Wait that long and resend:

Batch with retries (Node.js)
// Node.js 18+: send up to 100 events per request, and retry when LogZuno
// answers 429 (rate limit) after the number of seconds in Retry-After.
const endpoint = process.env.LOGZUNO_ENDPOINT ?? "https://api.logzuno.app/v1";

async function sendBatch(events, attempt = 1) {
  const response = await fetch(`${endpoint}/batch`, {
    method: "POST",
    headers: {
      Authorization: `Bearer ${process.env.LOGZUNO_API_KEY}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({ events }),
  });

  if (response.status === 429 && attempt < 5) {
    const seconds = Number(response.headers.get("Retry-After") ?? 60);
    await new Promise((resolve) => setTimeout(resolve, seconds * 1000));
    return sendBatch(events, attempt + 1);
  }

  return { status: response.status, body: await response.json() };
}

const events = [
  { channel: "product", event: "Report exported", user_id: "user-123", tags: { format: "csv" } },
  { channel: "product", event: "Report shared", user_id: "user-123", tags: { with: "team" } },
];

console.log(await sendBatch(events));

When the organisation has used its monthly allowance, LogZuno also answers 429, with "reason": "quota_exceeded". Waiting won't help that one: upgrade the plan or wait for the next billing period.

Naming events well

  • Use past-tense, human names: Invoice paid, not invoice_paid_v2. They are what your team reads in the feed and in alerts.
  • Put variations in properties, not in the name: Plan upgraded with { "from": "starter", "to": "growth" }, not one event name per plan.
  • Keep one channel per area of the product, so muting and filtering stay useful.
  • Use notify for what someone should act on now. A push for every page view trains people to ignore pushes.

Questions

Should I send events from the browser or the server?

Send anything that matters (sign-ups, payments, errors) from your server: ad blockers can't stop it and a closed tab can't lose it. Use the browser for things only the browser knows, like clicks on a page.

What happens if I send the same event twice?

Give each event an insert_id (a UUID). LogZuno stores an event once per insert_id, so retrying a request never creates duplicates.