# addEntity(), removeEntity(), listEntities() (https://docs.billwave.example/sdk/entities)

Entity Management [#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 [#flat-api]

billwave.addEntity(params) [#billwaveaddentityparams]

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

```ts
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:**

```ts
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) [#billwaveremoveentityparams]

Remove an entity and free up the slot.

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

**Returns:**

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

billwave.listEntities(params?) [#billwavelistentitiesparams]

List entities for a customer.

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

**Returns:**

```ts
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 [#examples]

Add Team Members (Seats) [#add-team-members-seats]

```ts
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 [#check-seat-availability-first]

```ts
// 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-and-manage-seats]

```ts
// 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 [#multiple-entity-types]

You can have different entity types per feature:

```ts
// 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 [#entity-scoped-usage]

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

```ts
// 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 [#customer-bound-api]

You can also call entity methods on the customer object:

```ts
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 [#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.

Related [#related]

* [`customer()`](/sdk/customer) - Create and manage customers
* [`check()`](/sdk/check) - Check feature access (supports entity scope)
* [`track()`](/sdk/track) - Track usage (supports entity scope)
* [Seat Pricing Guide](/pricing/seat-pricing) - Complete walkthrough