nozle.track() sends one event to baseUrl/api/v1/events (default https://api.nozle.app/engine/api/v1/events).
Signature
track() returns the transactionId that was sent. Keep it: it is the parentTransactionId for a Cost Event and the value to reuse on a retry.
Subscription resolution
When subscriptionId is omitted, the SDK queries eventsUrl/api/v1/subscriptions for active subscriptions belonging to the customer:
- exactly one active subscription: its external ID is cached and used;
- no active subscriptions:
track() rejects with No active subscription for customer '<id>';
- multiple active subscriptions:
track() rejects with Customer '<id>' has <n> active subscriptions — pass subscriptionId explicitly.
Pass subscriptionId explicitly for customers with multiple active subscriptions and for latency-sensitive event paths that should avoid the initial lookup.
The cache lasts for the life of the Nozle instance. From 0.10.0 the entry is dropped after subscribe(), cancelSubscription(), applySubscriptionTransition(), checkout(), a scoped checkoutStatus() or verifyCheckout(), and checkoutInvoice() clears every entry, so the next track() looks the subscription up again. Earlier versions never refreshed it. A change made outside this instance, or a track() between a paid checkout() and its payment, can still use the old subscription ID, and the Events API accepts it. Pass subscriptionId in those cases, or call checkoutStatus() with a scope after payment.
subscribe to a second plan adds a subscription; it does not replace the first. To move a customer to another plan, change or cancel the old subscription, then pass the new subscription ID to track().
timestamp accepts an RFC 3339 string with Z or an offset, such as 2026-08-04T10:30:00Z, or Unix seconds as a numeric string. A date only (2026-08-04) or a time without an offset (2026-08-04T10:30:00) throws track timestamp must be RFC3339 or Unix seconds before any request is sent. The SDK sends the value as Unix seconds with three decimals.
Request payload
Idempotency
Generate a deterministic transaction ID and capture the event timestamp from the business operation whenever possible. Retry the same logical event with both values unchanged. See errors and retries.
Safe retries
A timeout, a network error or a 5xx can arrive after Nozle recorded the event. Nozle records an event once per transaction_id, so a retry with the same ID, the same event and the same timestamp does not bill it twice. A new ID is a new event. Without a timestamp, each attempt is stamped with its own arrival time, so pass one and keep it.
From @nozle-js/node 0.13.2, the error track() throws carries the ID it sent as error.transactionId (typed on NozleApiError), also when the SDK generated it. Earlier versions lose a generated ID when the call throws, so pass your own transactionId there.
Operational guidance
- Await
track() when the request must know whether ingestion succeeded.
- For high-volume workloads, publish through your own durable queue and call
track() from a worker.
- Do not create a new random transaction ID on each retry.
- Do not regenerate the timestamp when retrying an accepted business event.
- Match property names to the Feature aggregation configuration exactly.
- Use
usage.track() instead when the operation must atomically enforce and deduct product credits.