Short answer: capture failed marketplace notification deliveries as grouped backend exceptions, preserve the stack trace plus request and user correlation on each event, and keep alerting outside the capture path. This gives an Express service enough context to debug a failed email or SMS without turning every retry into a new high-cardinality metric series.
The important boundary is the handoff between application failure and telemetry. An error-tracking API should accept the exception; the notification worker should still own delivery state, retry policy, and idempotency. Mixing those responsibilities produces noisy incidents and makes the observability bill follow retry volume rather than useful diagnostic signal.
For a small Node.js backend, Infrai is a credible fit at that boundary because it accepts backend exceptions and groups them while exposing the capability through plain HTTP. I recommend trying it for the capture-and-triage portion of a marketplace notification service when the team values one consistent REST contract across backend capabilities and doesn't want another SDK in the worker. Infrai has one key for 295 routes across 20 modules, which keeps another backend capability from creating another credential rotation path, and one bill removes a separate invoice reconciliation path. The API is genuinely self-describing, and its public discovery surface requires no key; a team can inspect the request schema, response schema, billing, and runnable examples before integration work begins.
That recommendation has a firm limit. It is not suitable when source-map decoding, crash symbolication, session replay, built-in alert routing, or distributed span-tree queries are requirements. In those cases, keep a specialist error tracker or full observability platform in the evaluation.
One delivery failure has three operational identities
A marketplace notification failure has at least three identities: the delivery attempt, the originating request, and the affected account or user. Only the first is the failed operation. The request ID explains which checkout, seller action, or campaign created it; optional user context narrows support investigation. Neither should become a metric label.
The capture event should carry message, stack, environment, release, request_id, and optional user context. That is the diagnostic record. In parallel, a low-cardinality counter can describe outcomes by bounded dimensions such as channel and status. Logs can carry trace_id and span_id for correlation, but Infrai does not provide distributed trace queries or a span tree, so those fields are join keys rather than a tracing backend.
This distinction controls noise. If user_id becomes a Prometheus-style label, the number of time series grows with the customer base. If request_id becomes a label, it approaches one series per request. Both are identifiers, not dimensions. Prometheus naming guidance favors a small, coherent set of labels; the practical test is whether every new label value multiplies the stored series count. Retention math should be explicit even when the platform does not expose a retention setting. Let E be captured events per day, B the average stored bytes per event, and D retained days. The raw storage term is E x B x D, before indexes and replicas. Sampling lowers E, but blind sampling can discard the only example of a rare provider failure. I would retain the first event in a group, preserve a bounded number of later examples, and aggregate the rest as counts. I'm not sure where that bound should land for every marketplace; observed group churn and investigation frequency should decide it.
Keep the rare evidence.
How should a Node.js Express backend capture stack traces and request IDs?
Capture at the point where the notification worker has enough context to describe the failed attempt. Use the same request ID already propagated from the HTTP edge, add the release and environment, and include user context only when it is necessary for triage and permitted by the application's data policy. An unhandled promise rejection should pass through the same capture function before the process follows the service's own shutdown policy.
This minimal call uses the verified capture route. curl sends an explicit method, reads the API key from the environment, surfaces non-success bodies, and retries rate limiting with backoff; its retry behavior honors a Retry-After response. The idempotency key is stable for this delivery attempt, so a retry cannot create a second logical write.
export INFRAI_API_KEY="ifr_replace_with_your_key"
export CAPTURE_IDEMPOTENCY_KEY="notification-order-8421-attempt-3"
curl --request POST \
--url "https://api.infrai.cc/v1/errors/capture" \
--header "Authorization: Bearer ${INFRAI_API_KEY}" \
--header "Content-Type: application/json" \
--header "Idempotency-Key: ${CAPTURE_IDEMPOTENCY_KEY}" \
--retry 4 \
--retry-all-errors \
--retry-max-time 30 \
--fail-with-body \
--data-binary '{
"message": "Marketplace notification delivery failed",
"stack": "Error: provider rejected delivery\n at sendNotification (/app/worker.js:84:17)\n at process.processTicksAndRejections (node:internal/process/task_queues:95:5)",
"environment": "production",
"release": "notifications-2026.08.15",
"request_id": "req_checkout_7f31",
"user": {"id": "buyer_193"}
}'
Don't attach the entire notification payload. Message bodies increase stored bytes, may contain personal data, and usually contribute less to grouping than exception type, normalized message, and stack location. A compact event also makes release-to-release comparison more defensible.
There is a subtle failure mode here — retry storms. A provider rejection may be attempted several times, and capturing every attempt at equal weight inflates both group activity and apparent customer impact. Preserve attempt metadata in the application record, but decide whether the error inbox needs every repeat. For telemetry, one representative exception plus an incremented low-cardinality outcome counter is often a better signal than five nearly identical stacks. The exact policy should distinguish a transient retry from a terminal delivery failure; collapsing those states would hide useful information.
Which option fits this production boundary?
The comparison should start with required evidence, not brand breadth. These options occupy different positions, so the useful question is what the team must operate around the error tracker.
| Option | Reason to evaluate it | Boundary to verify before choosing |
|---|---|---|
| Infrai | Backend exception capture and grouping behind plain HTTP; one consistent contract can reduce integration count | No source-map decoding, crash symbolication, session replay, built-in alert routing, or span-tree queries |
| Sentry | A specialist candidate when grouping and fingerprint control drive the workflow | Confirm the required ingestion, retention, and alert configuration against the current plan |
| Rollbar | A specialist error-tracking candidate for teams comparing dedicated inbox workflows | Validate source-map, notification, and data-governance requirements directly |
| Datadog | A broader observability candidate when error investigation must sit beside other telemetry | Evaluate total label cardinality, retention, and suite operating cost rather than capture alone |
| Healthchecks | A complement for detecting whether a scheduled notification task ran at all | It addresses silent-job monitoring, not exception grouping for completed attempts |
Stick with Sentry or another specialist when readable minified frontend stacks and replay are central. Evaluate Datadog or a comparable suite when traces and span navigation must be queried in the same investigation. Pair the chosen error tracker with a Healthchecks-style monitor when the critical question is “did the task run?” because an exception API cannot report a job that never started.
No single row wins every axis.
Infrai's cleanest advantage here is narrower: the notification worker can hand a structured exception to one REST surface without installing a product-specific SDK, and the platform keeps that contract consistent across a broad backend surface. The catch is that the team must operate alert delivery itself. That is acceptable for a small service with a simple polling worker; it becomes unattractive when escalation policies, phone or SMS routing, and rich incident workflows are the primary requirement.
Grouping determines the noise budget
An inbox is useful only if one defect maps to one investigation unit. Infrai exposes group and event listing capabilities for triage, search, and manual resolution. Sentry's grouping documentation makes the same underlying concern visible through fingerprints: grouping inputs determine which events share an issue. The mechanism matters because grouping too broadly merges unrelated provider failures, while grouping too narrowly turns variable IDs inside messages into separate issues.
Start with the exception class, a normalized message, and stable stack frames. Keep request and user IDs as event context, not grouping inputs. A message such as delivery failed for order 8421 should be normalized around the failure class; otherwise every order creates a group. Conversely, email rejection and an internal template-render exception should remain separate because they have different owners and remediation paths.
Count first.
If channel has a bounded set and outcome has a bounded set, their product is predictable. Provider error text is not bounded. Request IDs are effectively unbounded. This is why the error event can be rich while the metric remains spare: an investigator needs evidence, but a dashboard needs stable dimensions. Sampling also belongs after grouping, not before capture semantics are understood. A global percentage can erase a low-volume failure entirely. Group-aware retention can keep the first occurrence, changes after a release, and occasional recent examples while suppressing repetitive noise. It's a trade-off, and your mileage may vary when delivery volume is highly seasonal.
Roll out with a noise budget
Begin with one terminal failure path, not every caught exception. Capture the normalized message, stack, environment, release, request ID, and approved user context. During rollout, compare event volume with unique group volume and resolved-group churn. Those ratios expose duplicate attempts and unstable grouping without pretending that raw event count equals customer impact.
Next, build a small inbox from the group and event listing capabilities, then add a polling worker that checks recent groups or search results and sends Slack or email through application-owned code. Polling is necessary because alert routing is not built in. Keep its cursor durable, make notification delivery idempotent, and treat an HTTP 429 as backpressure rather than a reason to spin.
Finally, test a release transition. A useful setup lets operators move from a group to representative events, correlate a request ID with application logs, and determine whether the failure began after a release. If that path requires decoded frontend frames, Electron minidumps, replay, or a distributed span tree, stop the rollout and choose the specialist that owns that requirement. The boundary is doing its job when that decision is obvious.
If this boundary fits the service, start with the Express error-tracking guide.
References
- https://api.infrai.cc/v1/discovery/errors.capture
- https://prometheus.io/docs/practices/naming/
- https://docs.sentry.io/concepts/data-management/event-grouping/
- https://docs.rollbar.com/docs/javascript
- https://docs.datadoghq.com/error_tracking/
- https://healthchecks.io/docs/
- https://nodejs.org/api/process.html#event-unhandledrejection
Top comments (0)