# customer() (https://docs.billwave.example/sdk/customer)

customer() [#customer]

Use `billwave.customer()` to create or resolve a customer by email or ID. The response includes the customer's current billing configuration, and the same namespace exposes helpers for customer-specific billing controls.

For the dedicated guide covering customer-level billing overrides, precedence, and recommended usage, see [Customer Config](/sdk/customer-config).

For customer-facing usage charts and usage dashboards, the same namespace also exposes `usageHistory()`.

Every read on this namespace (`get()`, `usageHistory()`, and the setters) accepts the **same identifier you use in `track()` / `check()`**: your own external customer ID, the customer's email, or the internal Billwave ID.

Signature [#signature]

```ts
const customer = await billwave.customer({
  id?: string,         // Optional: Your customer ID
  email: string,       // Required: Customer email
  name?: string,       // Optional: Display name
  metadata?: Record<string, unknown>, // Optional: Custom data
});

const customer = await billwave.customer.get(customer: string);

const page = await billwave.customer.list({
  limit?: number,      // 1-100, default 50
  offset?: number,     // default 0
  search?: string,     // substring match on email, name, externalId
  email?: string,      // exact (case-insensitive)
  externalId?: string, // exact
});

await billwave.customer.setFeatureConfig({
  customer: string,
  feature: string,
  overage?: "block" | "charge" | null,
  maxOverageUnits?: number | null,
});

await billwave.customer.setOverageLimit({
  customer: string,
  maxOverageAmount: number | null,
  onLimitReached?: "block" | "notify",
});

await billwave.customer.usageHistory({
  customer: string,
  range?: "7d" | "30d" | "90d" | "custom",
  granularity?: "day" | "week" | "month",
  feature?: string | null,
  groupBy?: "total" | "feature",
  timezone?: string,
  from?: string,
  to?: string,
});
```

Parameters [#parameters]

billwave.customer(params) [#billwavecustomerparams]

| Parameter  | Type     | Required | Description               |
| ---------- | -------- | -------- | ------------------------- |
| `id`       | `string` | -        | Your internal customer ID |
| `email`    | `string` | ✅        | Customer email address    |
| `name`     | `string` | -        | Customer display name     |
| `metadata` | `object` | -        | Custom key-value data     |

billwave.customer.get(customer) [#billwavecustomergetcustomer]

| Parameter  | Type     | Required | Description                                                            |
| ---------- | -------- | -------- | ---------------------------------------------------------------------- |
| `customer` | `string` | ✅        | External customer ID (what you pass to `track`), email, or internal ID |

Returns the same `CustomerResult` as `billwave.customer()`. Throws `BillwaveError` with `Customer not found` when nothing matches within your organization.

billwave.customer.list(params?) [#billwavecustomerlistparams]

| Parameter    | Type     | Required | Description                                                     |
| ------------ | -------- | -------- | --------------------------------------------------------------- |
| `limit`      | `number` | -        | Page size, 1–100. Defaults to `50`.                             |
| `offset`     | `number` | -        | Customers to skip. Defaults to `0`.                             |
| `search`     | `string` | -        | Case-insensitive substring match on email, name and external ID |
| `email`      | `string` | -        | Exact email match (case-insensitive)                            |
| `externalId` | `string` | -        | Exact external ID match                                         |

Results are newest first. `total` is the number of customers matching the filters, so `offset + data.length < total` means there is another page.

billwave.customer.setFeatureConfig(params) [#billwavecustomersetfeatureconfigparams]

| Parameter         | Type                          | Required | Description                                    |
| ----------------- | ----------------------------- | -------- | ---------------------------------------------- |
| `customer`        | `string`                      | ✅        | Customer ID or identifier                      |
| `feature`         | `string`                      | ✅        | Feature slug or ID                             |
| `overage`         | `"block" \| "charge" \| null` | -        | Override overage behavior for this feature     |
| `maxOverageUnits` | `number \| null`              | -        | Override the hard overage cap for this feature |

Provide at least one of `overage` or `maxOverageUnits`. Pass `null` to clear an existing override.

billwave.customer.setOverageLimit(params) [#billwavecustomersetoveragelimitparams]

| Parameter          | Type                  | Required | Description                                                 |
| ------------------ | --------------------- | -------- | ----------------------------------------------------------- |
| `customer`         | `string`              | ✅        | Customer ID or identifier                                   |
| `maxOverageAmount` | `number \| null`      | ✅        | Customer-wide spend cap in minor units                      |
| `onLimitReached`   | `"block" \| "notify"` | -        | Behavior when the spend cap is reached. Defaults to `block` |

Pass `null` for `maxOverageAmount` to remove the spend cap.

billwave.customer.usageHistory(params) [#billwavecustomerusagehistoryparams]

| Parameter     | Type                                 | Required | Description                                                                          |
| ------------- | ------------------------------------ | -------- | ------------------------------------------------------------------------------------ |
| `customer`    | `string`                             | ✅        | Customer ID                                                                          |
| `range`       | `"7d" \| "30d" \| "90d" \| "custom"` | -        | Preset range or custom window. Defaults to `30d`.                                    |
| `granularity` | `"day" \| "week" \| "month"`         | -        | Bucket size for aggregation. Defaults to `day`.                                      |
| `feature`     | `string \| null`                     | -        | Optional feature slug or ID to filter to one feature.                                |
| `groupBy`     | `"total" \| "feature"`               | -        | Return only the total series or include per-feature breakdowns. Defaults to `total`. |
| `timezone`    | `string`                             | -        | IANA timezone used for bucket boundaries. Defaults to `UTC`.                         |
| `from`        | `string`                             | -        | Inclusive `YYYY-MM-DD` start date when `range` is `custom`.                          |
| `to`          | `string`                             | -        | Inclusive `YYYY-MM-DD` end date when `range` is `custom`.                            |

Use `usageHistory()` when you want chart-friendly usage buckets.

Use [`billwave.billing.usage()`](/sdk/billing) when you want uninvoiced overage and estimated billable amounts.

Returns [#returns]

`billwave.customer()`, `get()`, `setFeatureConfig()`, and `setOverageLimit()` all return the customer plus the current customer billing config.

```ts
interface CustomerResult {
  id: string;                 // Internal Billwave ID
  externalId: string | null;  // Your ID (the `customer` value you track with)
  email: string;
  name?: string | null;
  metadata?: Record<string, unknown> | null;
  billing: {
    overageLimit: {
      maxOverageAmount: number | null;
      onLimitReached: "block" | "notify";
      createdAt: number;
      updatedAt: number;
    } | null;
    featureConfigs: Array<{
      feature: {
        id: string;
        slug: string | null;
        name: string;
      };
      overage: "block" | "charge" | null;
      maxOverageUnits: number | null;
      createdAt: number;
      updatedAt: number;
    }>;
  };
  createdAt: number;
  updatedAt: number;
}
```

`billwave.customer.list()` returns a page of lightweight customer summaries (no billing config):

```ts
interface CustomerListResult {
  success: true;
  data: Array<{
    id: string;
    externalId: string | null;
    email: string;
    name: string | null;
    metadata: Record<string, unknown> | null;
    createdAt: number;
    updatedAt: number;
  }>;
  total: number;
  limit: number;
  offset: number;
}
```

`billwave.customer.usageHistory()` returns aggregated usage history:

```ts
interface UsageHistoryResult {
  customer: {
    id: string;
  };
  query: {
    range: {
      from: string;
      to: string;
    };
    granularity: "day" | "week" | "month";
    feature: string | null;
    groupBy: "total" | "feature";
    timezone: string;
  };
  totals: {
    usage: number;
    records: number;
  };
  series: Array<{
    bucket: string;
    value: number;
  }>;
  breakdown: Array<{
    feature: {
      id: string;
      slug: string | null;
      name: string;
      unit: string | null;
    };
    totals: {
      usage: number;
      records: number;
    };
    series: Array<{
      bucket: string;
      value: number;
    }>;
  }>;
}
```

Examples [#examples]

Create or Resolve a Customer [#create-or-resolve-a-customer]

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

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

// Create an org customer
const org = await billwave.customer({
  email: "billing@acme.com",
  name: "Acme Corporation",
  metadata: { industry: "SaaS" },
});

console.log(org.id);
console.log(org.billing.overageLimit); // null by default
console.log(org.billing.featureConfigs); // []
```

Resolve an Existing Customer [#resolve-an-existing-customer]

```ts
// Resolve existing customer by email
const existing = await billwave.customer({
  email: "billing@acme.com",
});

// Or by your internal ID
const byId = await billwave.customer({
  id: "org_acme",
  email: "billing@acme.com",
});
```

Look Up a Customer by Your Own ID [#look-up-a-customer-by-your-own-id]

```ts
// The id you pass to track()/check() works for reads too
await billwave.track({ customer: "workspace_alpha", feature: "agent_turns" });

const customer = await billwave.customer.get("workspace_alpha");
console.log(customer.id);         // internal UUID
console.log(customer.externalId); // "workspace_alpha"

// Email works as well
const byEmail = await billwave.customer.get("billing@acme.com");
```

List and Search Customers [#list-and-search-customers]

```ts
// First page, newest first
const page = await billwave.customer.list({ limit: 25 });

// Search across email, name and external id
const matches = await billwave.customer.list({ search: "acme" });

// Exact lookups
const { data } = await billwave.customer.list({ externalId: "workspace_alpha" });

// Walk every page
for (let offset = 0; ; offset += 100) {
  const { data, total } = await billwave.customer.list({ limit: 100, offset });
  for (const customer of data) console.log(customer.externalId ?? customer.email);
  if (offset + data.length >= total) break;
}
```

Set a Per-Feature Overage Policy [#set-a-per-feature-overage-policy]

```ts
const updated = await billwave.customer.setFeatureConfig({
  customer: "cust_123",
  feature: "api_calls",
  overage: "block",
  maxOverageUnits: 1000,
});

console.log(updated.billing.featureConfigs);
```

Set a Customer-Wide Overage Spend Cap [#set-a-customer-wide-overage-spend-cap]

```ts
const updated = await billwave.customer.setOverageLimit({
  customer: "cust_123",
  maxOverageAmount: 500_000,
  onLimitReached: "block",
});

console.log(updated.billing.overageLimit);
```

Clear Customer Billing Overrides [#clear-customer-billing-overrides]

```ts
await billwave.customer.setFeatureConfig({
  customer: "cust_123",
  feature: "api_calls",
  overage: null,
  maxOverageUnits: null,
});

await billwave.customer.setOverageLimit({
  customer: "cust_123",
  maxOverageAmount: null,
  onLimitReached: "block",
});
```

Get Aggregate Usage History [#get-aggregate-usage-history]

```ts
const history = await billwave.customer.usageHistory({
  customer: "cust_123",
  range: "30d",
  granularity: "day",
  groupBy: "total",
  timezone: "Africa/Lagos",
});

console.log(history.totals.usage);
console.log(history.series);
```

Get Per-Feature Usage History [#get-per-feature-usage-history]

```ts
const history = await billwave.customer.usageHistory({
  customer: "cust_123",
  range: "90d",
  granularity: "week",
  groupBy: "feature",
});

for (const feature of history.breakdown) {
  console.log(feature.feature.slug, feature.totals.usage);
}
```

Use a Custom Date Window [#use-a-custom-date-window]

```ts
const history = await billwave.customer.usageHistory({
  customer: "cust_123",
  range: "custom",
  granularity: "day",
  from: "2026-04-01",
  to: "2026-04-30",
});
```

Using the Customer Object [#using-the-customer-object]

The returned customer object has convenience methods:

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

// Attach subscription
await org.attach({ product: "team" });

// Manage entities (seats)
await org.addEntity({
  feature: "seats",
  entity: "user_123",
  name: "John Doe",
});

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

Entity Management Methods [#entity-management-methods]

Customer objects provide these entity management methods:

customer.addEntity(params) [#customeraddentityparams]

Add an entity (e.g., seat) to the customer.

```ts
await org.addEntity({
  feature: "seats",
  entity: "user_123",
  name: "John Doe",
  email: "john@acme.com",
  metadata: { role: "admin" },
});
```

**Parameters:**

* `feature` (string, required): Feature slug
* `entity` (string, required): Your entity ID
* `name` (string, optional): Display name
* `email` (string, optional): Contact email
* `metadata` (object, optional): Custom data

customer.removeEntity(params) [#customerremoveentityparams]

Remove an entity and free up the slot.

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

customer.listEntities(params?) [#customerlistentitiesparams]

List entities for this customer.

```ts
// List all entities
const { entities } = await org.listEntities();

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

Best Practices [#best-practices]

1. **Create early** - Create the customer before attaching subscriptions or adding entities
2. **Use consistent IDs** - Either always pass your own `id` or always use email as identifier
3. **Store metadata** - Use metadata for customer segmentation and analytics
4. **Use spend caps as safety rails** - `setOverageLimit()` protects the whole customer across billable overage
5. **Use feature config for exceptions** - `setFeatureConfig()` is best for one risky or contract-specific feature
6. **Use usageHistory for charts** - `usageHistory()` is for usage dashboards, not invoice estimation
7. **Update on changes** - Call `customer()` when user data changes to keep billing records in sync

Related [#related]

* [Customer Config](/sdk/customer-config) - Customer-level billing overrides and spend caps
* [billing](/sdk/billing) - Uninvoiced overage, invoices, and invoice generation
* [`attach()`](/sdk/attach) - Subscribe customer to a plan
* [Overage](/pricing/overage) - How plan overage and customer overrides interact
* [`addEntity()`](/sdk/entities) - Add entities/seats (flat API)
* [Seat Pricing Guide](/pricing/seat-pricing) - Complete seat-based billing walkthrough