Tenants
Read a tenant back — its rate limit, metadata, and creation time — and list tenants with cursor pagination.
Postel scopes rows to a tenant and lets you cap dispatch throughput per tenant (setRateLimit), but until now there was no way to read a tenant back. The tenants read surface fixes that: get and list mirror the message introspection reads, for tenants.
postel.outbound.tenants has two reads, alongside the existing setRateLimit / delete writes. All reads are read-only; none mutate.
Read one tenant
const tenant = await postel.outbound.tenants.get("t_42");
// => undefined when no tenant matches the id (no throw)
tenant?.id; // "t_42"
tenant?.rateLimit; // { kind: "fixed", perSecond: 50 } | null
tenant?.metadata; // the raw metadata blob, or null
tenant?.createdAt; // Dateget returns the tenant or undefined when the id is unknown — never a throw.
rateLimit is a strategy, not a bare number
rateLimit is a RateLimitStrategy — the same kind-discriminated shape as RetryStrategy / KmsStrategy / WorkerStrategy elsewhere in the library — rather than a bare { perSecond } object. Today FixedRate({ perSecond }) (kind: "fixed") is the only variant, matching what setRateLimit already configures:
await postel.outbound.tenants.setRateLimit("t_42", { perSecond: 50 });
const tenant = await postel.outbound.tenants.get("t_42");
tenant?.rateLimit; // { kind: "fixed", perSecond: 50 }rateLimit is null when the tenant has no rate limit configured. setRateLimit's call signature is unchanged — this only affects what reads hand back, leaving room for additional strategies later without another breaking change.
List tenants (paginated)
const page = await postel.outbound.tenants.list({ limit: 50 });
page.items; // ReadonlyArray<Tenant>, newest-first
page.nextCursor; // an opaque string, or null on the last pagelist returns tenants newest-first, bounded by limit (default 100), using an opaque keyset cursor rather than an offset — the tenants table isn't assumed to be low-cardinality. Walk every page by feeding nextCursor back in as cursor:
let cursor: string | undefined;
const allTenants = [];
do {
const page = await postel.outbound.tenants.list({ limit: 100, ...(cursor ? { cursor } : {}) });
allTenants.push(...page.items);
cursor = page.nextCursor ?? undefined;
} while (cursor);This { limit?, cursor? } → { items, nextCursor } envelope is the library-wide pagination convention: endpoints.list, messages.list, and reconcile page exactly the same way.
Over HTTP
The same two reads are exposed by the admin API as GET /tenants (?limit=, ?cursor=) and GET /tenants/:id. Those routes are tenant-scoped from the authorize decision — a tenant-bound caller listing tenants sees only its own tenant record — and an unknown or cross-tenant id responds 404 TENANT_NOT_FOUND.