# @kubiks/otel-autumn

OpenTelemetry instrumentation for the [Autumn](https://useautumn.com) billing SDK.
Capture spans for every billing operation including feature checks, usage tracking, checkout flows, product attachments, and cancellations with detailed metadata.

![Autumn Trace Visualization](https://github.com/kubiks-inc/otel/blob/main/images/otel-autumn-trace.png)

_Visualize your billing operations with detailed span information including operation type, customer IDs, feature IDs, and billing metadata._

## Installation

```bash
npm install @kubiks/otel-autumn
# or
pnpm add @kubiks/otel-autumn
```

**Peer Dependencies:** `@opentelemetry/api` >= 1.9.0, `autumn-js` >= 0.1.0

## Quick Start

```ts
import { Autumn } from "autumn-js";
import { instrumentAutumn } from "@kubiks/otel-autumn";

const autumn = instrumentAutumn(
  new Autumn({
    secretKey: process.env.AUTUMN_SECRET_KEY!,
  }),
);

// All operations are now automatically traced
const checkResult = await autumn.check({
  customer_id: "user_123",
  feature_id: "messages",
});

await autumn.track({
  customer_id: "user_123",
  feature_id: "messages",
  value: 1,
});
```

`instrumentAutumn` wraps your Autumn client instance — no configuration changes needed. Every SDK call creates a client span with detailed billing attributes.

## What Gets Traced

This instrumentation wraps the core Autumn billing methods:

- **`check`** - Feature access and product status checks
- **`track`** - Usage event tracking
- **`checkout`** - Checkout session creation
- **`attach`** - Product attachment to customers
- **`cancel`** - Product cancellation

Each operation creates a dedicated span with operation-specific attributes.

## Span Attributes

### Common Attributes (All Operations)

| Attribute            | Description               | Example                |
| -------------------- | ------------------------- | ---------------------- |
| `billing.system`     | Constant value `autumn`   | `autumn`               |
| `billing.operation`  | Operation type            | `check`, `track`       |
| `autumn.resource`    | Resource being accessed   | `features`, `products` |
| `autumn.target`      | Full operation target     | `features.check`       |
| `autumn.customer_id` | Customer ID               | `user_123`             |
| `autumn.entity_id`   | Entity ID (if applicable) | `org_456`              |

### Check Operation

| Attribute                 | Description                    | Example    |
| ------------------------- | ------------------------------ | ---------- |
| `autumn.feature_id`       | Feature being checked          | `messages` |
| `autumn.product_id`       | Product being checked          | `pro`      |
| `autumn.allowed`          | Whether access is allowed      | `true`     |
| `autumn.balance`          | Current balance/remaining uses | `42`       |
| `autumn.usage`            | Current usage                  | `8`        |
| `autumn.included_usage`   | Included usage in plan         | `50`       |
| `autumn.unlimited`        | Whether usage is unlimited     | `false`    |
| `autumn.required_balance` | Required balance for operation | `1`        |

### Track Operation

| Attribute                | Description               | Example        |
| ------------------------ | ------------------------- | -------------- |
| `autumn.feature_id`      | Feature being tracked     | `messages`     |
| `autumn.event_name`      | Custom event name         | `message_sent` |
| `autumn.value`           | Usage value tracked       | `1`            |
| `autumn.event_id`        | Generated event ID        | `evt_123`      |
| `autumn.idempotency_key` | Idempotency key for dedup | `msg_456`      |

### Checkout Operation

| Attribute               | Description                         | Example                           |
| ----------------------- | ----------------------------------- | --------------------------------- |
| `autumn.product_id`     | Product being purchased             | `pro`                             |
| `autumn.product_ids`    | Multiple products (comma-separated) | `pro, addon_analytics`            |
| `autumn.checkout_url`   | Stripe checkout URL                 | `https://checkout.stripe.com/...` |
| `autumn.has_prorations` | Whether prorations apply            | `true`                            |
| `autumn.total_amount`   | Total checkout amount               | `2000` (cents)                    |
| `autumn.currency`       | Currency code                       | `usd`                             |
| `autumn.force_checkout` | Whether to force Stripe checkout    | `false`                           |
| `autumn.invoice`        | Whether to create invoice           | `true`                            |

### Attach Operation

| Attribute             | Description                    | Example                           |
| --------------------- | ------------------------------ | --------------------------------- |
| `autumn.product_id`   | Product being attached         | `pro`                             |
| `autumn.success`      | Whether attachment succeeded   | `true`                            |
| `autumn.checkout_url` | Checkout URL if payment needed | `https://checkout.stripe.com/...` |

### Cancel Operation

| Attribute           | Description                    | Example |
| ------------------- | ------------------------------ | ------- |
| `autumn.product_id` | Product being cancelled        | `pro`   |
| `autumn.success`    | Whether cancellation succeeded | `true`  |

## Configuration

You can optionally configure the instrumentation:

```ts
import { instrumentAutumn } from "@kubiks/otel-autumn";

const autumn = instrumentAutumn(client, {
  // Capture customer data in spans (default: false)
  captureCustomerData: true,

  // Capture product options/configuration (default: false)
  captureOptions: true,
});
```

## Usage Examples

### Feature Access Control

```ts
const autumn = instrumentAutumn(
  new Autumn({ secretKey: process.env.AUTUMN_SECRET_KEY! }),
);

// Check if user can access a feature
const result = await autumn.check({
  customer_id: "user_123",
  feature_id: "messages",
  required_balance: 1,
});

if (result.data?.allowed) {
  // User has access
  console.log(`Remaining: ${result.data.balance}`);
}
```

### Usage Tracking

```ts
// Track feature usage
await autumn.track({
  customer_id: "user_123",
  feature_id: "messages",
  value: 1,
  idempotency_key: `msg_${messageId}`, // Prevent double-counting
});
```

### Checkout Flow

```ts
// Create a checkout session for a product
const result = await autumn.checkout({
  customer_id: "user_123",
  product_id: "pro",
  force_checkout: false, // Use billing portal if payment method exists
});

if (result.data?.url) {
  // Redirect to Stripe checkout
  console.log(`Checkout URL: ${result.data.url}`);
}
```

### Product Management

```ts
// Attach a free product
const attachResult = await autumn.attach({
  customer_id: "user_123",
  product_id: "free",
});

// Cancel a subscription
const cancelResult = await autumn.cancel({
  customer_id: "user_123",
  product_id: "pro",
});
```

## License

MIT
