Grouping, fingerprints, releases: error tracking terms explained

What an event, a fingerprint, a group, an environment and a release actually are for a SaaS backend — shown against real responses from Infrai's error API.

Five words carry almost all the vocabulary of error tracking, and they chain together: an event is one occurrence, its fingerprint is the key it gets filed under, everything sharing a fingerprint is a group, and each event also records the environment and release it came from. Infrai’s errors API exposes all five as plain fields, which makes it a convenient place to see what the words mean rather than argue about them.

The chain matters because it decides what your issue list looks like on a bad day. Get the fingerprint right and one bug is one row you can resolve; get it wrong and the same bug is four thousand rows, or worse, hidden inside a single row called “request failed”. Sentry and Rollbar derive that key for you from parsed stack frames; here you supply it, which is more work and considerably more control.

The five terms, and where each one shows up

TermWhat it meansField you’ll see
EventA single occurrence, with its own timestampevent_id, timestamp
FingerprintThe grouping key, hashed server-sidefingerprint, fingerprint_source
Group (issue)Every event sharing one fingerprinterror_group_id, count
EnvironmentWhich deployment it came fromenvironment, environment_distribution
ReleaseWhich build it came fromrelease, release_distribution

A sixth concept, the stack trace, behaves differently from what beginners expect and gets its own section below.

Sending one event

export INFRAI_API_KEY="your_infrai_api_key"

curl -sS -X POST "https://api.infrai.cc/v1/errors/capture" \
  -H "Authorization: Bearer ${INFRAI_API_KEY}" \
  -H "Content-Type: application/json" \
  -d '{
    "message": "TypeError: Cannot read properties of undefined (reading '\''id'\'')\n    at applyDiscount (/app/src/checkout.js:42:17)",
    "exception": "TypeError",
    "fingerprint": "checkout:applyDiscount:TypeError",
    "environment": "production",
    "release": "2026.07.4"
  }'
{
  "ok": true,
  "data": {
    "event_id": "evt_err_UpWSnXBBW7twDTS7NswKQyw7",
    "fingerprint": "11028f6ba5a94ed6f95c2e67719a09d06e23f9da70fad8486970b58e462401a9",
    "error_group_id": "errgrp_8frNBStHjj1NY2vTsOH0B3vw",
    "is_new_group": true,
    "dashboard_url": "https://infrai.cc/projects/proj_local/errors/evt_err_UpWSnXBBW7twDTS7NswKQyw7"
  }
}

The fingerprint you sent came back as a 64-character hash — the server hashes your string rather than storing it raw, so checkout:applyDiscount:TypeError and its hash are the same key. is_new_group: true means nothing with that key existed before. On the second occurrence you get the same error_group_id and is_new_group: false, which is grouping working.

Choosing a fingerprint: the two ways to get it wrong

Too fine is the common one. Put anything that varies per request in the key — an order id, a user id, a timestamp, a generated URL — and every occurrence opens its own group. You’ll see thousands of issues, each with a count of 1, and no way to tell which failure is actually frequent.

Too coarse is rarer and worse. A single fingerprint like api-error for every failure in the service produces one group with a count of 90,000 that nobody can act on, because it contains six unrelated bugs.

The shape that survives contact with production is scope plus error class: a stable name for the code path, then the exception type. checkout:applyDiscount:TypeError. Route templates work as the scope (POST /api/invoices/:id), function names work, queue names work. Anything with an id in it doesn’t.

If you send no fingerprint at all, the service derives one from the message text and marks the stored event fingerprint_source: "default"; supply your own and it reads "user". That flag is the quickest way to audit whether your reporter is doing what you think.

Stack traces: what actually survives

Here’s the part that trips people up, and we verified it against the live API rather than reading it off a feature list. The exception field you send is not parsed into frames. The stored event comes back as {"type": "Message", "value": "<your message>", "stacktrace": []} — an empty frame array, always.

So the trace is only as good as the string you put in message. Send the whole error.stack, never just error.message, because that text is the entire trace you will ever get back. It stays searchable and it’s perfectly readable for server-side Node or Python code where filenames aren’t mangled.

What you don’t get is a frame table with in-app markers, source-map resolution for minified browser bundles, or automatic grouping computed from those frames. If those are what you mean by “error tracking”, Sentry is the tool that does them, and this route doesn’t compete on that ground.

Reading a group back

curl -sS "https://api.infrai.cc/v1/errors/group_detail/errgrp_8frNBStHjj1NY2vTsOH0B3vw" \
  -H "Authorization: Bearer ${INFRAI_API_KEY}"
{
  "ok": true,
  "data": {
    "error_group_id": "errgrp_8frNBStHjj1NY2vTsOH0B3vw",
    "title": "TypeError: Cannot read properties of undefined (reading 'id')",
    "first_seen_at": "2026-07-25T16:02:34.779284Z",
    "last_seen_at": "2026-07-26T00:19:28.710363Z",
    "count": 3,
    "user_count": 0,
    "level": "error",
    "is_resolved": false,
    "environments": ["production"],
    "releases": ["2026.07.4"],
    "release_distribution": { "2026.07.4": 3 },
    "environment_distribution": { "production": 3 }
  }
}

That response answers the two questions a beginner guide usually leaves hanging. “Did my deploy cause this?” is first_seen_at against your deploy time, plus release_distribution — a group whose events all carry one release, first seen twenty minutes after it shipped, is a strong hint. “Is it everywhere or just staging?” is environment_distribution.

Note there’s no status string on a group. The boolean is is_resolved.

Environments and releases in practice

An environment is a deployment target — production, staging, preview-pr-412. A release is a build identifier, and any stable string works: a semver tag, a date-stamped build like 2026.07.4, or the git SHA. Use the same value your deploy pipeline already prints, because a release nobody can map back to a commit is decoration.

Filtering by them is asymmetric across the read routes, which is worth knowing before you build tooling:

curl -sS "https://api.infrai.cc/v1/errors/list?environment=production&level=error&limit=5" \
  -H "Authorization: Bearer ${INFRAI_API_KEY}"

The event list honours environment, level and release. The group list only honours status, and other parameters passed to it are ignored silently rather than rejected — a limitation that’s easy to miss because the response still looks correct.

To see the individual occurrences behind one group:

curl -sS "https://api.infrai.cc/v1/errors/events/errgrp_8frNBStHjj1NY2vTsOH0B3vw?limit=10" \
  -H "Authorization: Bearer ${INFRAI_API_KEY}"

What this costs while you’re learning

Reads are free, so exploring groups, events and searches costs nothing. Captures bill at $0.00005 each, verified 2026-07-26, and a new account carries $2 of free credit — enough for tens of thousands of test events before anything is charged.

curl -sS "https://api.infrai.cc/v1/discovery" \
  -H "Authorization: Bearer ${INFRAI_API_KEY}" \
  | jq '.capabilities[] | select(.id == "errors.capture") | .billing'

Catalogue rates drift downward and discounts run, so the live call is the number that counts.

Grouping is the concept worth spending your attention on; everything else here is bookkeeping around it. Pick a fingerprint scheme, apply it consistently, and your issue list stays readable at a hundred events a day or a hundred thousand — whichever tracker you end up paying for.

References

Browse more errors developer guides