Owostack

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. Owostack tracks the current count against the plan limit.

Flat API

owo.addEntity(params)

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

await owo.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.

owo.removeEntity(params)

Remove an entity and free up the slot.

await owo.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
}

owo.listEntities(params?)

List entities for a customer.

await owo.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 { Owostack } from "owostack";

const owo = new Owostack({ secretKey: process.env.OWOSTACK_API_KEY });

// Add team members - validates against seat limit
try {
  await owo.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 owo.check({
  customer: "org@acme.com",
  feature: "seats",
  value: 1,
});

if (check.allowed) {
  await owo.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 owo.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 owo.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 owo.addEntity({
  customer: "org@acme.com",
  feature: "admin-seats",
  entity: "admin_1",
  name: "CEO",
});

// Regular member seats
await owo.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 owo.track({
  customer: "org@acme.com",
  feature: "ai-credits",
  entity: "user_123", // Must exist!
  value: 50,
});

// Check remaining credits for the seat
const status = await owo.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 owo.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

AI Chat

Owostack docs assistant

Start a new chat below.