Billwave
SDK Reference

addEntity(), removeEntity(), listEntities()

Manage feature entities for per-seat and org-scoped billing

Entity Management

Entities represent scoped, non-consumable resources like seats, workspaces, or team members. Use these methods to manage entities for per-seat billing and entity-scoped usage tracking.

Unlike metered features which are consumed over time, entity features represent fixed capacity. You add entities when they are created and remove them when they are deleted. Billwave tracks the current count against the plan limit.

Flat API

billwave.addEntity(params)

Add an entity to a customer. Validates against feature limits.

await billwave.addEntity({
  customer: string,    // Customer ID or email
  feature: string,     // Feature slug (e.g., "seats")
  entity: string,      // Your entity ID
  name?: string,       // Display name
  email?: string,      // Contact email
  metadata?: Record<string, unknown>, // Custom data
}): Promise<AddEntityResult>

Returns:

interface AddEntityResult {
  success: boolean;
  entityId: string;
  featureId: string;
  count: number; // Current entity count
  limit: number | null;
  remaining: number | null;
}

Throws: limit_exceeded if adding would exceed the plan limit.

billwave.removeEntity(params)

Remove an entity and free up the slot.

await billwave.removeEntity({
  customer: string,    // Customer ID or email
  feature: string,     // Feature slug
  entity: string,      // Entity ID to remove
}): Promise<RemoveEntityResult>

Returns:

interface RemoveEntityResult {
  success: boolean;
  entityId: string;
  count: number; // Remaining entity count
}

billwave.listEntities(params?)

List entities for a customer.

await billwave.listEntities({
  customer: string,    // Customer ID or email
  feature?: string,    // Optional: filter by feature
}): Promise<ListEntitiesResult>

Returns:

interface Entity {
  id: string;
  featureId: string;
  name?: string;
  email?: string;
  metadata?: Record<string, unknown>;
  status: "active" | "pending_removal";
  createdAt: string;
}

interface ListEntitiesResult {
  success: boolean;
  entities: Entity[];
  total: number;
}

Examples

Add Team Members (Seats)

import { Billwave } from "@digvijay-x1/billwave";

const billwave = new Billwave({ secretKey: process.env.BILLWAVE_API_KEY });

// Add team members - validates against seat limit
try {
  await billwave.addEntity({
    customer: "org@acme.com",
    feature: "seats",
    entity: "user_123",
    name: "John Doe",
    email: "john@acme.com",
    metadata: { role: "admin", department: "engineering" },
  });
  console.log("Seat added successfully");
} catch (err) {
  if (err.code === "limit_exceeded") {
    console.log("Seat limit reached - upgrade required");
  }
}

Check Seat Availability First

// Optional: Check if seat is available before adding
const check = await billwave.check({
  customer: "org@acme.com",
  feature: "seats",
  value: 1,
});

if (check.allowed) {
  await billwave.addEntity({
    customer: "org@acme.com",
    feature: "seats",
    entity: "user_456",
    name: "Jane Smith",
  });
} else {
  console.log("No seats available");
}

List and Manage Seats

// List all active seats
const { entities } = await billwave.listEntities({
  customer: "org@acme.com",
  feature: "seats",
});

console.log(`Active seats: ${entities.length}`);
entities.forEach((seat) => {
  console.log(`- ${seat.name} (${seat.email})`);
});

// Remove a team member
await billwave.removeEntity({
  customer: "org@acme.com",
  feature: "seats",
  entity: "user_123",
});

Multiple Entity Types

You can have different entity types per feature:

// Admin seats (different feature, different limit)
await billwave.addEntity({
  customer: "org@acme.com",
  feature: "admin-seats",
  entity: "admin_1",
  name: "CEO",
});

// Regular member seats
await billwave.addEntity({
  customer: "org@acme.com",
  feature: "member-seats",
  entity: "member_1",
  name: "Engineer",
});

Entity-Scoped Usage

Once an entity is added, you can track and check usage scoped to that entity:

// Track AI credits for a specific seat
await billwave.track({
  customer: "org@acme.com",
  feature: "ai-credits",
  entity: "user_123", // Must exist!
  value: 50,
});

// Check remaining credits for the seat
const status = await billwave.check({
  customer: "org@acme.com",
  feature: "ai-credits",
  entity: "user_123",
});

console.log(`${status.balance} credits remaining`);

Important: Entities must be created with addEntity() before using them in check() or track(). Attempting to track a non-existent entity returns entity_not_found error.

Customer-Bound API

You can also call entity methods on the customer object:

const org = await billwave.customer({ email: "org@acme.com" });

// Add entity via customer object
await org.addEntity({
  feature: "seats",
  entity: "user_123",
  name: "John Doe",
});

// List entities
const { entities } = await org.listEntities({ feature: "seats" });

// Remove entity
await org.removeEntity({ feature: "seats", entity: "user_123" });

Validation

  • Entity uniqueness: Entity IDs are unique per feature. You can reuse the same ID across different features.
  • Limit enforcement: addEntity() validates synchronously against the feature limit.
  • Entity required: check() and track() require entities to exist. Returns entity_not_found error otherwise.

On this page

Ask about billwave

Ready

Start a new chat below.

Powered by Cull