> ## Documentation Index
> Fetch the complete documentation index at: https://docs.nozle.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Tracking events

> Send idempotent metered events and control subscription attribution with nozle.track().

```python theme={null}
nozle.track(
    "workspace_123",
    "api_calls",
    metadata={"model": "example-model", "tokens": 1_500},
    subscription_id="workspace_123_subscription",
    transaction_id="request_0183f",
    timestamp="2026-08-04T10:30:00Z",
)
```

## Signature

```python theme={null}
nozle.track(
    customer_id,
    event,
    metadata=None,
    subscription_id=None,
    transaction_id=None,
    timestamp=None,
)
```

| Argument          | Description                                            |
| ----------------- | ------------------------------------------------------ |
| `customer_id`     | External Nozle customer ID.                            |
| `event`           | Feature event code.                                    |
| `metadata`        | JSON-compatible aggregation or attribution properties. |
| `subscription_id` | Explicit external subscription target.                 |
| `transaction_id`  | Stable event ID. A UUID is generated when omitted.     |
| `timestamp`       | Optional ISO 8601 event timestamp.                     |

## Subscription resolution

When `subscription_id` is omitted, the SDK queries the Events API for active subscriptions:

* one active subscription is cached and used;
* no active subscription raises `NozleAPIError`; and
* multiple active subscriptions raise `NozleAPIError` and require an explicit ID.

Supply the subscription explicitly for customers with multiple subscriptions and latency-sensitive paths.

## Idempotency

Use a deterministic transaction ID and stable event timestamp for each logical event. Reuse both on retries.

```python theme={null}
transaction_id = f"generation:{generation_id}:completed"
timestamp = generation_completed_at.isoformat()

nozle.track(
    customer_id,
    "text_generation",
    metadata={
        "input_tokens": usage.input_tokens,
        "output_tokens": usage.output_tokens,
    },
    subscription_id=subscription_id,
    transaction_id=transaction_id,
    timestamp=timestamp,
)
```

## FastAPI worker pattern

```python theme={null}
from fastapi import FastAPI

app = FastAPI()


@app.post("/events/generation-completed")
def generation_completed(event: GenerationCompleted) -> dict[str, bool]:
    nozle.track(
        event.customer_id,
        "text_generation",
        metadata={"tokens": event.tokens},
        subscription_id=event.subscription_id,
        transaction_id=f"generation:{event.id}:completed",
        timestamp=event.completed_at.isoformat(),
    )
    return {"accepted": True}
```

For high-volume workloads, enqueue your business event and call `track()` from a durable worker. Use `usage.track()` instead when the operation must atomically enforce product credits.
