Skip to main content

Overview

@kubiks/otel-autumn provides comprehensive OpenTelemetry instrumentation for the Autumn 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
Visualize your billing operations with detailed span information including operation type, customer IDs, feature IDs, and billing metadata.

Installation

npm install @kubiks/otel-autumn
Peer Dependencies: @opentelemetry/api >= 1.9.0, autumn-js >= 0.1.0

Quick Start

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.

Traced Operations

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)

AttributeDescriptionExample
billing.systemConstant value autumnautumn
billing.operationOperation typecheck, track
autumn.resourceResource being accessedfeatures, products
autumn.targetFull operation targetfeatures.check
autumn.customer_idCustomer IDuser_123
autumn.entity_idEntity ID (if applicable)org_456

Check Operation

AttributeDescriptionExample
autumn.feature_idFeature being checkedmessages
autumn.allowedWhether access is allowedtrue
autumn.balanceCurrent balance/remaining uses42
autumn.usageCurrent usage8
autumn.unlimitedWhether usage is unlimitedfalse
autumn.required_balanceRequired balance for operation1
AttributeDescriptionExample
autumn.product_idProduct being checkedpro
autumn.included_usageIncluded usage in plan50

Track Operation

AttributeDescriptionExample
autumn.feature_idFeature being trackedmessages
autumn.event_nameCustom event namemessage_sent
autumn.valueUsage value tracked1
autumn.event_idGenerated event IDevt_123
autumn.idempotency_keyIdempotency key for dedupmsg_456

Checkout Operation

AttributeDescriptionExample
autumn.product_idProduct being purchasedpro
autumn.product_idsMultiple products (comma-separated)pro, addon_analytics
autumn.checkout_urlStripe checkout URLhttps://checkout.stripe.com/...
autumn.has_prorationsWhether prorations applytrue
autumn.total_amountTotal checkout amount2000 (cents)
autumn.currencyCurrency codeusd
autumn.force_checkoutWhether to force Stripe checkoutfalse
autumn.invoiceWhether to create invoicetrue

Attach Operation

AttributeDescriptionExample
autumn.product_idProduct being attachedpro
autumn.successWhether attachment succeededtrue
autumn.checkout_urlCheckout URL if payment neededhttps://checkout.stripe.com/...

Cancel Operation

AttributeDescriptionExample
autumn.product_idProduct being cancelledpro
autumn.successWhether cancellation succeededtrue

Configuration

You can optionally configure the instrumentation:
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,
});
By default, sensitive customer data is not captured. Enable captureCustomerData only if your observability platform is secure and compliant.

Usage Examples

Feature Access Control

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

// Track feature usage
await autumn.track({
  customer_id: "user_123",
  feature_id: "messages",
  value: 1,
});

Checkout Flow

// 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

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

if (attachResult.data?.success) {
  console.log("Product attached successfully");
}

Complete Integration Example

Here’s a complete example integrating Autumn with a Next.js application:
lib/autumn.ts
import { Autumn } from "autumn-js";
import { instrumentAutumn } from "@kubiks/otel-autumn";

export const autumn = instrumentAutumn(
  new Autumn({
    secretKey: process.env.AUTUMN_SECRET_KEY!,
  }),
  {
    captureCustomerData: true,
    captureOptions: true,
  }
);
app/api/features/check/route.ts
import { autumn } from "@/lib/autumn";
import { NextRequest, NextResponse } from "next/server";

export async function POST(request: NextRequest) {
  const { customerId, featureId } = await request.json();

  const result = await autumn.check({
    customer_id: customerId,
    feature_id: featureId,
    required_balance: 1,
  });

  return NextResponse.json(result.data);
}
app/api/usage/track/route.ts
import { autumn } from "@/lib/autumn";
import { NextRequest, NextResponse } from "next/server";

export async function POST(request: NextRequest) {
  const { customerId, featureId, value, idempotencyKey } = await request.json();

  await autumn.track({
    customer_id: customerId,
    feature_id: featureId,
    value,
    idempotency_key: idempotencyKey,
  });

  return NextResponse.json({ success: true });
}

Best Practices

Always use idempotency keys when tracking usage to prevent double-counting:
await autumn.track({
  customer_id: "user_123",
  feature_id: "messages",
  value: 1,
  idempotency_key: `msg_${messageId}`,
});
Check feature access before performing operations:
const check = await autumn.check({
  customer_id: "user_123",
  feature_id: "messages",
  required_balance: 1,
});

if (!check.data?.allowed) {
  throw new Error("Insufficient balance");
}

// Proceed with operation
await sendMessage();

// Track usage
await autumn.track({
  customer_id: "user_123",
  feature_id: "messages",
  value: 1,
});
Handle both checkout URLs and direct product attachments:
const result = await autumn.checkout({
  customer_id: "user_123",
  product_id: "pro",
});

if (result.data?.url) {
  // Redirect to Stripe checkout
  return redirect(result.data.url);
} else {
  // Product attached directly (e.g., free plan)
  return redirect("/dashboard");
}
Only enable additional data capture in secure environments:
const autumn = instrumentAutumn(client, {
  captureCustomerData: process.env.NODE_ENV === "development",
  captureOptions: true,
});

Troubleshooting

Make sure OpenTelemetry is properly configured in your application:
import { NodeSDK } from "@opentelemetry/sdk-node";
import { getNodeAutoInstrumentations } from "@opentelemetry/auto-instrumentations-node";

const sdk = new NodeSDK({
  instrumentations: [getNodeAutoInstrumentations()],
});

sdk.start();
Ensure you’re using the latest version of the package:
npm update @kubiks/otel-autumn
The instrumentation adds minimal overhead. If you experience issues:
  • Verify your OpenTelemetry exporter is configured correctly
  • Check if you’re using sampling to reduce data volume
  • Consider using batch span processors

Resources

License

MIT