Documentation

Events

Events & insights

What happens in your product, next to what it earns. Send events from your code, add milestones by hand, and chart both against revenue.

What events are

Revenue tells you what happened to your money; events tell you why. Metricneed keeps three kinds, in one feed:

  • Milestones — one-off things you type by hand: a launch, a pricing change, a newsletter mention, an outage. They appear as vertical markers on your charts, so a bump in revenue sits right next to the thing that caused it.
  • Product events — anything your backend reports: signups, deploys, support tickets, feature usage. Sent with a secret API key, aggregated per day, chartable on their own or on the same graph as revenue, with counts, sums, averages and unique users.
  • Revenue events — written by Metricneed itself from your payment providers: every sale, refund, new subscription, trial, scheduled cancellation and churn, with no code to add. See events from your revenue.

There are also insights: gauges with a current value (waitlist size, open tickets, queue depth) that you set from your code and read at a glance.

Events never touch your revenue figures. They add context; they don't change numbers that come from your payment provider.

Quickstart

  1. Create a key in Settings → Events API. It is shown once.
  2. Send an event:
curl -X POST https://metricneed.com/api/v1/events \
  -H "Authorization: Bearer mne_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "channel": "signups", "event": "New signup", "icon": "user-plus", "user_id": "usr_42", "tags": { "plan": "pro" } }'
  1. Open the events feed. It's there.

Prefer a client library? See the Node.js SDK.

Authentication

Every request carries Authorization: Bearer mne_…. Keys are write-only: they can create events, set insights and add or edit expenses for their account, and nothing else — a leaked key can't read your revenue. Keep them server-side; revoke and recreate to rotate.

POST /api/v1/events

Send one event as the body, or up to 100 as { "events": [...] }.

{
  "channel": "signups",
  "event": "New signup",
  "description": "[email protected] joined from the pricing page",
  "icon": "user-plus",
  "user_id": "usr_42",
  "value": 29,
  "tags": { "plan": "pro", "source": "producthunt" },
  "timestamp": "2026-09-11T10:32:00Z",
  "idempotency_key": "signup:usr_42"
}

A batch:

{
  "events": [
    { "channel": "deploys", "event": "Production deploy", "pin": true, "tags": { "sha": "a1b2c3" } },
    { "channel": "support", "event": "Ticket resolved", "value": 42, "user_id": "usr_7" }
  ]
}

Fields:

FieldTypeMeaning
channelstringRequired. Groups events (signups, billing, deploys). Lowercased and slugified: "Product Launch" becomes product-launch.
eventstringRequired. The event name, up to 120 characters. Kept as typed.
descriptionstringOptional context shown in the feed, up to 1000 characters.
iconstringOptional. One of the icons listed below.
tagsobjectOptional flat map of strings, numbers and booleans (≤ 20). Filter the feed and charts by them.
user_idstringOptional. Your own id for the user — never an email. Powers the "unique users" aggregate.
valuenumberOptional numeric value (an amount, a duration, a count). Powers the "sum" and "average" aggregates.
timestampstring | numberOptional. ISO 8601 with an offset, or a unix epoch in seconds or milliseconds. Defaults to now; up to 5 minutes in the future and 36 months back (bounded by your plan's retention).
idempotency_keystringOptional. Retries with the same key within 7 days are accepted but stored once.
pinbooleanOptional. Show this event as a marker on charts, like a manual milestone.
notifybooleanOptional. Notify you about this event — by email out of the box, or wherever your rules send it (see Notifications).

Response — 200:

{
  "ok": true,
  "accepted": 2,
  "duplicates": 0,
  "ids": ["4a3e…", "9f01…"],
  "usage": { "used": 1204, "limit": 25000, "resetsAt": "2026-10-01T00:00:00.000Z" }
}

usage echoes your month so a client can warn before the quota is hit. duplicates counts events skipped because their idempotency_key was already seen.

Idempotency

Networks fail and SDKs retry. Give an event an idempotency_key (your own id for the fact — signup:usr_42, invoice:inv_9) and a retry is acknowledged without storing it twice or counting it against your quota. Keys are remembered for 7 days per account.

Errors

StatusWhen
400Malformed JSON, a failed validation (the message names the field), a timestamp outside the accepted window, or the reserved `revenue` channel (`data.reason` is `reserved_channel`). The whole request is rejected — nothing is stored.
401Missing, unknown or revoked key.
402Monthly event quota reached (`data.reason` is `event_quota`, with `used`, `limit` and `resetsAt`). Manual milestones are never counted.
413Body over 256 KB.
429Rate limit — 600 requests per minute per key. Honour the Retry-After header.

A validation error names the field:

{
  "statusCode": 400,
  "statusMessage": "events.1.icon: Unknown icon — see the icon list in the docs",
  "data": { "reason": "invalid_payload", "issues": [{ "path": "events.1.icon", "message": "…" }] }
}

Events from your revenue

Metricneed also writes events on its own, from the data your payment providers send. Every new sale, refund, new subscription, trial started, cancellation scheduled and subscription canceled lands in the feed on the revenue channel — nothing to install. They chart and filter like any other event.

  • The value is the amount in your primary currency, converted at that day's rate. Tags carry the provider, the product, the billing interval and the original currency. The user is the customer's Metricneed id — never an email.
  • Each sale, refund or subscription change becomes an event exactly once: resyncs never duplicate it. Connecting a provider doesn't flood the feed with its history — only what happens after the first sync (and at most 48 hours back) appears. To chart the past, use Import history on the revenue channel of the catalog: it fills the daily totals without adding rows to the feed.
  • They don't count against your event quota and can't be edited; deleting one removes it for good. They aren't chart markers unless you turn "show on charts" on for the channel.
  • The revenue channel is reserved: an API event or a milestone sent to it is rejected with a 400.

Revenue events — like the events your code sends — can notify you: a Slack message for every sale, a daily email of refunds. See Notifications.

Quotas and retention

Product events count against a monthly quota (UTC calendar month); milestones, revenue events and insights don't. Past the quota, requests get a 402 until the month resets — nothing is dropped silently, and nothing already stored is ever deleted on a downgrade. Older events fall outside your plan's retention window and are hidden; upgrading brings them back for 30 days after which they are pruned.

PlanEvents / monthHistoryAPI keys
Free1,0003 months1
Starter25,00012 months5
Pro250,00036 monthsUnlimited

POST /api/v1/insights

An insight is a named gauge. Setting it again replaces the value. Same key, same headers, not counted against the event quota.

{ "name": "Waitlist", "value": 1204, "icon": "users" }

Or add to it: increment (instead of value) adds any number, negative to subtract, atomically — concurrent workers never lose an update. A new gauge starts at 0; a gauge holding text answers 409 (data.reason is not_numeric).

{ "name": "Signups today", "increment": 1 }

The name is slugified into a key (Waitlist → waitlist), so the same name always updates the same gauge. Values are numbers or short strings. Numeric gauges keep a day-by-day history (the last, lowest and highest value of each day, as long as your plan keeps events): the Insights page draws the last 30 days, and a dashboard chart can plot a gauge over any period — each point is the value it ended that period at (0 before the gauge's first value).

Icons

icon must be one of the names below (Lucide icons). Unknown names are rejected so that everything stored can be drawn.

Privacy

Send your own user ids, not emails. Events are private to your account, and an API key can never read them back. Deleting your account deletes them.

Nothing reaches your public dashboard unless you opt in, one piece at a time:

  • A milestone you mark Visible on the public page appears as a marker on the public charts that show markers — its title, kind, colour and date only, never its description.
  • Event counts, per chart or stat you turn Show these event counts on the public page on for: the number of events or of unique users, never sums or averages of value, tags or user ids.

Only those milestones become public markers — never pinned, SDK or revenue events — and the event feed and insights never appear there.

activity
zap
rocket
flag
bell
star
heart
sparkles
trophy
target
user-plus
user-minus
users
log-in
log-out
mail
message-square
megaphone
credit-card
wallet
shopping-cart
gift
tag
package
receipt
trending-up
trending-down
circle-check
circle-x
triangle-alert
bug
shield
server
globe
code
git-commit-horizontal
download
upload
refresh-cw
settings
key-round
file-text
calendar
clock
dollar-sign
percent