customer()
Create, resolve, and configure a customer
customer()
Use owo.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.
For customer-facing usage charts and usage dashboards, the same namespace also exposes usageHistory().
Signature
const customer = await owo.customer({
id?: string, // Optional: Your customer ID
email: string, // Required: Customer email
name?: string, // Optional: Display name
metadata?: Record<string, unknown>, // Optional: Custom data
});
await owo.customer.setFeatureConfig({
customer: string,
feature: string,
overage?: "block" | "charge" | null,
maxOverageUnits?: number | null,
});
await owo.customer.setOverageLimit({
customer: string,
maxOverageAmount: number | null,
onLimitReached?: "block" | "notify",
});
await owo.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
owo.customer(params)
| 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 |
owo.customer.setFeatureConfig(params)
| 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.
owo.customer.setOverageLimit(params)
| 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.
owo.customer.usageHistory(params)
| 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 owo.billing.usage() when you want uninvoiced overage and estimated billable amounts.
Returns
owo.customer(), setFeatureConfig(), and setOverageLimit() all return the customer plus the current customer billing config.
interface CustomerResult {
id: string;
email: string;
name?: string;
metadata?: Record<string, unknown>;
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;
}owo.customer.usageHistory() returns aggregated usage history:
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
Create or Resolve a Customer
import { Owostack } from "owostack";
const owo = new Owostack({ secretKey: process.env.OWOSTACK_API_KEY });
// Create an org customer
const org = await owo.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 existing customer by email
const existing = await owo.customer({
email: "billing@acme.com",
});
// Or by your internal ID
const byId = await owo.customer({
id: "org_acme",
email: "billing@acme.com",
});Set a Per-Feature Overage Policy
const updated = await owo.customer.setFeatureConfig({
customer: "cust_123",
feature: "api_calls",
overage: "block",
maxOverageUnits: 1000,
});
console.log(updated.billing.featureConfigs);Set a Customer-Wide Overage Spend Cap
const updated = await owo.customer.setOverageLimit({
customer: "cust_123",
maxOverageAmount: 500_000,
onLimitReached: "block",
});
console.log(updated.billing.overageLimit);Clear Customer Billing Overrides
await owo.customer.setFeatureConfig({
customer: "cust_123",
feature: "api_calls",
overage: null,
maxOverageUnits: null,
});
await owo.customer.setOverageLimit({
customer: "cust_123",
maxOverageAmount: null,
onLimitReached: "block",
});Get Aggregate Usage History
const history = await owo.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
const history = await owo.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
const history = await owo.customer.usageHistory({
customer: "cust_123",
range: "custom",
granularity: "day",
from: "2026-04-01",
to: "2026-04-30",
});Using the Customer Object
The returned customer object has convenience methods:
const org = await owo.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
Customer objects provide these entity management methods:
customer.addEntity(params)
Add an entity (e.g., seat) to the customer.
await org.addEntity({
feature: "seats",
entity: "user_123",
name: "John Doe",
email: "john@acme.com",
metadata: { role: "admin" },
});Parameters:
feature(string, required): Feature slugentity(string, required): Your entity IDname(string, optional): Display nameemail(string, optional): Contact emailmetadata(object, optional): Custom data
customer.removeEntity(params)
Remove an entity and free up the slot.
await org.removeEntity({
feature: "seats",
entity: "user_123",
});customer.listEntities(params?)
List entities for this customer.
// List all entities
const { entities } = await org.listEntities();
// Filter by feature
const { entities: seats } = await org.listEntities({ feature: "seats" });Best Practices
- Create early - Create the customer before attaching subscriptions or adding entities
- Use consistent IDs - Either always pass your own
idor always use email as identifier - Store metadata - Use metadata for customer segmentation and analytics
- Use spend caps as safety rails -
setOverageLimit()protects the whole customer across billable overage - Use feature config for exceptions -
setFeatureConfig()is best for one risky or contract-specific feature - Use usageHistory for charts -
usageHistory()is for usage dashboards, not invoice estimation - Update on changes - Call
customer()when user data changes to keep billing records in sync
Related
- Customer Config - Customer-level billing overrides and spend caps
- billing - Uninvoiced overage, invoices, and invoice generation
attach()- Subscribe customer to a plan- Overage - How plan overage and customer overrides interact
addEntity()- Add entities/seats (flat API)- Seat Pricing Guide - Complete seat-based billing walkthrough