File size: 56,879 Bytes
1477a90
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
<!-- Code generated by @openmeter/typespec-typescript. DO NOT EDIT. -->

# OpenMeter SDK

TypeScript client for the OpenMeter API — usage metering and billing for
AI and DevTool companies. This package is generated from the OpenMeter
TypeSpec definitions and ships fully-typed request and response models.

> **Important:** This SDK is a work in progress.
>
> This SDK targets the [OpenMeter API v3](https://openmeter.io/docs/api/v3),
> a rewrite of the OpenMeter API following AIP (API Improvement Proposal)
> standardization.

## Table of Contents

- [Installation](#installation)
- [Initialization](#initialization)
- [Configuration](#configuration)
- [Usage](#usage)
- [Pagination](#pagination)
- [Available Resources and Operations](#available-resources-and-operations)
  - [Events](#events)
  - [Meters](#meters)
  - [Customers](#customers)
  - [Entitlements](#entitlements)
  - [Subscriptions](#subscriptions)
  - [Apps](#apps)
  - [Billing](#billing)
  - [Tax](#tax)
  - [Features](#features)
  - [LLMCost](#llmcost)
  - [Plans](#plans)
  - [Addons](#addons)
  - [PlanAddons](#planaddons)
  - [Defaults](#defaults)
- [Internal Operations](#internal-operations)
  - [Internal Subscriptions](#internal-subscriptions)
  - [Internal Invoices](#internal-invoices)
  - [Internal Currencies](#internal-currencies)
  - [Internal Governance](#internal-governance)
- [Runtime Validation (validate option)](#runtime-validation-validate-option)
- [Zod Schemas (./zod export)](#zod-schemas-zod-export)
- [Error Handling](#error-handling)
- [Standalone Functions](#standalone-functions)

## Installation

```bash
npm install @openmeter/client
```

Or with your package manager of choice:

```bash
pnpm add @openmeter/client
yarn add @openmeter/client
```

## Initialization

Create a client with a base URL and an API key. The API key is sent as a
`Bearer` token on every request.

```typescript
import { OpenMeter } from '@openmeter/client'

const client = new OpenMeter({
  baseUrl: 'https://openmeter.cloud/api/v3',
  apiKey: process.env.OPENMETER_API_KEY,
})
```

Konnect regions are addressed with a server template and a `region`
variable:

```typescript
import { OpenMeter, ServerList } from '@openmeter/client'

const client = new OpenMeter({
  baseUrl: ServerList[0],
  serverVariables: { region: 'eu' },
  apiKey: process.env.OPENMETER_API_KEY,
})
```

The `apiKey` may also be a function returning a `string` or
`Promise<string>`, so tokens can be refreshed per request.

## Configuration

`SDKOptions` extends [ky](https://github.com/sindresorhus/ky)'s
`Options`, so every transport setting ky supports is a top-level client
option: retry policy, per-attempt timeout (ky defaults to 10 seconds),
lifecycle hooks, a custom `fetch`, and so on.

```typescript
import { OpenMeter } from '@openmeter/client'

const client = new OpenMeter({
  baseUrl: 'https://openmeter.cloud/api/v3',
  apiKey: process.env.OPENMETER_API_KEY,
  timeout: 30_000,
  hooks: {
    beforeRequest: [
      ({ request }) => {
        console.log(`-> ${request.method} ${request.url}`)
      },
    ],
  },
  fetch: async (input, init) => {
    const start = Date.now()
    const response = await fetch(input, init)
    console.log(`${response.status} in ${Date.now() - start}ms`)
    return response
  },
})
```

ky only retries the idempotent methods by default — `get`, `put`,
`head`, `delete`, `options`, `trace` — never `post`. That means a
dropped `client.events.ingest` call is not retried on a network error
or a 5xx response unless you opt in explicitly:

```typescript
import { OpenMeter } from '@openmeter/client'

const client = new OpenMeter({
  baseUrl: 'https://openmeter.cloud/api/v3',
  apiKey: process.env.OPENMETER_API_KEY,
  retry: { limit: 3, methods: ['get', 'put', 'head', 'delete', 'post'] },
})
```

This is safe specifically for event ingestion: the event `id` is its
deduplication key server-side, so resending the same event on retry is
a no-op rather than a duplicate.

Every method also takes a per-request `RequestOptions` as its second
argument — a curated subset of ky's options (`signal`, `headers`,
`timeout`, `retry`) applied to that call only:

```typescript
import { OpenMeter } from '@openmeter/client'

const client = new OpenMeter({
  baseUrl: 'https://openmeter.cloud/api/v3',
  apiKey: process.env.OPENMETER_API_KEY,
})

const controller = new AbortController()
setTimeout(() => controller.abort(), 5_000)

const meters = await client.meters.list(undefined, {
  signal: controller.signal,
  headers: { 'X-Request-Id': 'batch-42' },
})
```

## Usage

Every operation is reachable through a fluent, namespaced client and
returns a typed response (or throws an `HTTPError` on a non-2xx status).

```typescript
import { OpenMeter } from '@openmeter/client'

const client = new OpenMeter({
  baseUrl: 'https://openmeter.cloud/api/v3',
  apiKey: process.env.OPENMETER_API_KEY,
})

const meter = await client.meters.create({
  name: 'Tokens',
  key: 'tokens',
  aggregation: 'sum',
  eventType: 'request',
  valueProperty: '$.tokens',
})

const meters = await client.meters.list()
```

Each method takes the request object as its first argument and an optional
per-request options object (`RequestOptions`) as its second.

Responses return date-time fields as native `Date` objects (every
`createdAt`/`updatedAt`, meter query row windows, …), and requests accept
either a `Date` or an RFC 3339 string — a meter query `from`/`to`, an
ingested event's `time`, filter operands, all alike.

## Pagination

Every list operation that returns pages also has an `…All` companion —
`client.meters.listAll(request?)` alongside `client.meters.list(request?)` —
that returns an `AsyncIterable` of items instead of one page. It fetches
each following page lazily, only when the previous page is exhausted, so a
`break` (or a `return`) partway through never fires a request for a page
nothing consumes:

```typescript
import { OpenMeter } from '@openmeter/client'

const client = new OpenMeter({
  baseUrl: 'https://openmeter.cloud/api/v3',
  apiKey: process.env.OPENMETER_API_KEY,
})

for await (const meter of client.meters.listAll()) {
  console.log(meter.key)
}
```

The request object accepts the same filters, sort, and page size as the
single-page method — only the page cursor/number itself advances between
requests:

```typescript
for await (const meter of client.meters.listAll({ filter: { key: 'api' } })) {
  if (meter.key === 'api-requests') {
    break // stops iterating; no further pages are fetched
  }
}
```

Cursor-paginated resources (`events`, credit transactions) work the same
way:

```typescript
for await (const event of client.events.listAll()) {
  console.log(event.event.id)
}
```

Auto-pagination takes the same optional `RequestOptions` as every other
method, so one `AbortSignal` cancels the whole iteration, not just the page
in flight:

```typescript
const controller = new AbortController()
setTimeout(() => controller.abort(), 30_000)

for await (const meter of client.meters.listAll(undefined, {
  signal: controller.signal,
})) {
  console.log(meter.key)
}
```

Iteration stops after the server’s last page — an empty or short-of-`size`
page for a page-number resource, or a response with no next cursor for a
cursor resource. A misbehaving server that never signals the end of the
list fails fast instead of looping forever: after 10,000 pages the
iterable throws `PaginationLimitExceededError`.

## Available Resources and Operations

Operations are grouped by resource and exposed as methods on the client.
The full call path, HTTP route, and a short description are listed below.

### Events

| Method                 | HTTP                     | Description                                                                  |
| ---------------------- | ------------------------ | ---------------------------------------------------------------------------- |
| `client.events.list`   | `GET /openmeter/events`  | List ingested events.                                                        |
| `client.events.ingest` | `POST /openmeter/events` | Ingests an event or batch of events following the CloudEvents specification. |

### Meters

| Method                   | HTTP                                     | Description                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    |
| ------------------------ | ---------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `client.meters.create`   | `POST /openmeter/meters`                 | Create a meter.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                |
| `client.meters.get`      | `GET /openmeter/meters/{meterId}`        | Get a meter by ID.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             |
| `client.meters.list`     | `GET /openmeter/meters`                  | List meters.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   |
| `client.meters.update`   | `PUT /openmeter/meters/{meterId}`        | Update a meter.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                |
| `client.meters.delete`   | `DELETE /openmeter/meters/{meterId}`     | Delete a meter.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                |
| `client.meters.query`    | `POST /openmeter/meters/{meterId}/query` | Query a meter for usage. Set `Accept: application/json` (the default) to get a structured JSON response. Set `Accept: text/csv` to download the same data as a CSV file suitable for spreadsheets. The CSV columns, in order, are: `from, to, [subject,] [customer_id, customer_key, customer_name,] <dimensions...>, value` The `subject` column is emitted only when `subject` is in the query's `group_by_dimensions`. The three `customer_*` columns are emitted together only when `customer_id` is in the query's `group_by_dimensions`. |
| `client.meters.queryCsv` | `POST /openmeter/meters/{meterId}/query` |                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                |

### Customers

| Method                                                     | HTTP                                                                                        | Description                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     |
| ---------------------------------------------------------- | ------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `client.customers.create`                                  | `POST /openmeter/customers`                                                                 | Create customer                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 |
| `client.customers.get`                                     | `GET /openmeter/customers/{customerId}`                                                     | Get customer                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    |
| `client.customers.list`                                    | `GET /openmeter/customers`                                                                  | List customers                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  |
| `client.customers.upsert`                                  | `PUT /openmeter/customers/{customerId}`                                                     | Upsert customer                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 |
| `client.customers.delete`                                  | `DELETE /openmeter/customers/{customerId}`                                                  | Delete customer                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 |
| `client.customers.billing.get`                             | `GET /openmeter/customers/{customerId}/billing`                                             | Get customer billing data                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       |
| `client.customers.billing.update`                          | `PUT /openmeter/customers/{customerId}/billing`                                             | Update customer billing data                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    |
| `client.customers.billing.updateAppData`                   | `PUT /openmeter/customers/{customerId}/billing/app-data`                                    | Update customer billing app data                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                |
| `client.customers.billing.createStripeCheckoutSession`     | `POST /openmeter/customers/{customerId}/billing/stripe/checkout-sessions`                   | Create a [Stripe Checkout Session](https://docs.stripe.com/payments/checkout) for the customer. Creates a Checkout Session for collecting payment method information from customers. The session operates in "setup" mode, which collects payment details without charging the customer immediately. The collected payment method can be used for future subscription billing. For hosted checkout sessions, redirect customers to the returned URL. For embedded sessions, use the client_secret to initialize Stripe.js in your application.                                                  |
| `client.customers.billing.createStripePortalSession`       | `POST /openmeter/customers/{customerId}/billing/stripe/portal-sessions`                     | Create Stripe Customer Portal Session. Useful to redirect the customer to the Stripe Customer Portal to manage their payment methods, change their billing address and access their invoice history. Only returns URL if the customer billing profile is linked to a stripe app and customer.                                                                                                                                                                                                                                                                                                   |
| `client.customers.credits.grants.create`                   | `POST /openmeter/customers/{customerId}/credits/grants`                                     | Create a new credit grant. A credit grant represents an allocation of prepaid credits to a customer.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            |
| `client.customers.credits.grants.get`                      | `GET /openmeter/customers/{customerId}/credits/grants/{creditGrantId}`                      | Get a credit grant.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             |
| `client.customers.credits.grants.list`                     | `GET /openmeter/customers/{customerId}/credits/grants`                                      | List credit grants.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             |
| `client.customers.credits.balance.get`                     | `GET /openmeter/customers/{customerId}/credits/balance`                                     | Get a credit balance.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           |
| `client.customers.credits.adjustments.create`              | `POST /openmeter/customers/{customerId}/credits/adjustments`                                | A credit adjustment can be used to make manual adjustments to a customer's credit balance. Supported use-cases: - Usage correction                                                                                                                                                                                                                                                                                                                                                                                                                                                              |
| `client.customers.credits.grants.void`                     | `POST /openmeter/customers/{customerId}/credits/grants/{creditGrantId}/void`                | Void a credit grant, forfeiting the remaining unused balance. Voiding is a forward-looking, irreversible operation. Credits already consumed by usage remain unaffected — only the remaining balance is forfeited. The grant reads as `voided` status afterwards. Payment state is not adjusted when `payment_adjustment` is `none`, so invoice-backed or externally collected payments may still collect the original amount. Only `active` grants can be voided; voiding a pending, expired, or fully consumed grant returns a conflict. Retrying a successful void is an idempotent success. |
| `client.customers.credits.grants.updateExternalSettlement` | `POST /openmeter/customers/{customerId}/credits/grants/{creditGrantId}/settlement/external` | Update the payment settlement status of an externally funded credit grant. Use this endpoint to synchronize the payment state of an external payment with the system so that revenue recognition and credit availability work as expected.                                                                                                                                                                                                                                                                                                                                                      |
| `client.customers.credits.transactions.list`               | `GET /openmeter/customers/{customerId}/credits/transactions`                                | List credit transactions for a customer. Returns an immutable, chronological record of credit movements: funded credits and consumed credits. Transactions are returned in reverse chronological order by default.                                                                                                                                                                                                                                                                                                                                                                              |
| `client.customers.charges.list`                            | `GET /openmeter/customers/{customerId}/charges`                                             | List customer charges. Returns the customer's charges that are represented as either flat fee or usage-based charges.                                                                                                                                                                                                                                                                                                                                                                                                                                                                           |
| `client.customers.charges.create`                          | `POST /openmeter/customers/{customerId}/charges`                                            | Create customer charge.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         |

### Entitlements

| Method                                   | HTTP                                                       | Description                      |
| ---------------------------------------- | ---------------------------------------------------------- | -------------------------------- |
| `client.entitlements.listCustomerAccess` | `GET /openmeter/customers/{customerId}/entitlement-access` | List customer entitlement access |

### Subscriptions

| Method                                       | HTTP                                                                         | Description                                                                                                                                    |
| -------------------------------------------- | ---------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- |
| `client.subscriptions.create`                | `POST /openmeter/subscriptions`                                              | Create subscription                                                                                                                            |
| `client.subscriptions.list`                  | `GET /openmeter/subscriptions`                                               | List subscriptions                                                                                                                             |
| `client.subscriptions.get`                   | `GET /openmeter/subscriptions/{subscriptionId}`                              | Get subscription                                                                                                                               |
| `client.subscriptions.cancel`                | `POST /openmeter/subscriptions/{subscriptionId}/cancel`                      | Cancels the subscription. Will result in a scheduling conflict if there are other subscriptions scheduled to start after the cancelation time. |
| `client.subscriptions.unscheduleCancelation` | `POST /openmeter/subscriptions/{subscriptionId}/unschedule-cancelation`      | Unschedules the subscription cancelation.                                                                                                      |
| `client.subscriptions.change`                | `POST /openmeter/subscriptions/{subscriptionId}/change`                      | Closes a running subscription and starts a new one according to the specification. Can be used for upgrades, downgrades, and plan changes.     |
| `client.subscriptions.listAddons`            | `GET /openmeter/subscriptions/{subscriptionId}/addons`                       | List the add-ons of a subscription.                                                                                                            |
| `client.subscriptions.getAddon`              | `GET /openmeter/subscriptions/{subscriptionId}/addons/{subscriptionAddonId}` | Get an add-on association for a subscription.                                                                                                  |

### Apps

| Method                       | HTTP                                   | Description                      |
| ---------------------------- | -------------------------------------- | -------------------------------- |
| `client.apps.list`           | `GET /openmeter/apps`                  | List installed apps.             |
| `client.apps.get`            | `GET /openmeter/apps/{appId}`          | Get an installed app.            |
| `client.apps.listCatalog`    | `GET /openmeter/app-catalog`           | List available apps.             |
| `client.apps.getCatalogItem` | `GET /openmeter/app-catalog/{appType}` | Get an app catalog item by type. |
| `client.apps.install`        | `POST /openmeter/app-catalog/install`  | Install an app from the catalog. |

### Billing

| Method                         | HTTP                              | Description                                                                                                                                                                                                                                                                                                              |
| ------------------------------ | --------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `client.billing.listProfiles`  | `GET /openmeter/profiles`         | List billing profiles.                                                                                                                                                                                                                                                                                                   |
| `client.billing.createProfile` | `POST /openmeter/profiles`        | Create a new billing profile. Billing profiles contain the settings for billing and controls invoice generation. An organization can have multiple billing profiles defined. A billing profile is linked to a specific app. This association is established during the billing profile's creation and remains immutable. |
| `client.billing.getProfile`    | `GET /openmeter/profiles/{id}`    | Get a billing profile.                                                                                                                                                                                                                                                                                                   |
| `client.billing.updateProfile` | `PUT /openmeter/profiles/{id}`    | Update a billing profile.                                                                                                                                                                                                                                                                                                |
| `client.billing.deleteProfile` | `DELETE /openmeter/profiles/{id}` | Delete a billing profile. Only such billing profiles can be deleted that are: - not the default profile - not pinned to any customer using customer overrides - only have finalized invoices                                                                                                                             |

### Tax

| Method                  | HTTP                                      | Description     |
| ----------------------- | ----------------------------------------- | --------------- |
| `client.tax.createCode` | `POST /openmeter/tax-codes`               | Create tax code |
| `client.tax.getCode`    | `GET /openmeter/tax-codes/{taxCodeId}`    | Get tax code    |
| `client.tax.listCodes`  | `GET /openmeter/tax-codes`                | List tax codes  |
| `client.tax.upsertCode` | `PUT /openmeter/tax-codes/{taxCodeId}`    | Upsert tax code |
| `client.tax.deleteCode` | `DELETE /openmeter/tax-codes/{taxCodeId}` | Delete tax code |

### Features

| Method                      | HTTP                                              | Description                                                                |
| --------------------------- | ------------------------------------------------- | -------------------------------------------------------------------------- |
| `client.features.list`      | `GET /openmeter/features`                         | List all features.                                                         |
| `client.features.create`    | `POST /openmeter/features`                        | Create a feature.                                                          |
| `client.features.get`       | `GET /openmeter/features/{featureId}`             | Get a feature by id.                                                       |
| `client.features.update`    | `PATCH /openmeter/features/{featureId}`           | Update a feature by id. Currently only the unit_cost field can be updated. |
| `client.features.delete`    | `DELETE /openmeter/features/{featureId}`          | Delete a feature by id.                                                    |
| `client.features.queryCost` | `POST /openmeter/features/{featureId}/cost/query` | Query the cost of a feature.                                               |

### LLMCost

| Method                          | HTTP                                             | Description                                                                           |
| ------------------------------- | ------------------------------------------------ | ------------------------------------------------------------------------------------- |
| `client.llmCost.listPrices`     | `GET /openmeter/llm-cost/prices`                 | List global LLM cost prices. Returns prices with overrides applied if any.            |
| `client.llmCost.getPrice`       | `GET /openmeter/llm-cost/prices/{priceId}`       | Get a specific LLM cost price by ID. Returns the price with overrides applied if any. |
| `client.llmCost.listOverrides`  | `GET /openmeter/llm-cost/overrides`              | List per-namespace price overrides.                                                   |
| `client.llmCost.createOverride` | `POST /openmeter/llm-cost/overrides`             | Create a per-namespace price override.                                                |
| `client.llmCost.deleteOverride` | `DELETE /openmeter/llm-cost/overrides/{priceId}` | Delete a per-namespace price override.                                                |

### Plans

| Method                 | HTTP                                     | Description             |
| ---------------------- | ---------------------------------------- | ----------------------- |
| `client.plans.list`    | `GET /openmeter/plans`                   | List all plans.         |
| `client.plans.create`  | `POST /openmeter/plans`                  | Create a new plan.      |
| `client.plans.update`  | `PUT /openmeter/plans/{planId}`          | Update a plan by id.    |
| `client.plans.get`     | `GET /openmeter/plans/{planId}`          | Get a plan by id.       |
| `client.plans.delete`  | `DELETE /openmeter/plans/{planId}`       | Delete a plan by id.    |
| `client.plans.archive` | `POST /openmeter/plans/{planId}/archive` | Archive a plan version. |
| `client.plans.publish` | `POST /openmeter/plans/{planId}/publish` | Publish a plan version. |

### Addons

| Method                  | HTTP                                       | Description                |
| ----------------------- | ------------------------------------------ | -------------------------- |
| `client.addons.list`    | `GET /openmeter/addons`                    | List all add-ons.          |
| `client.addons.create`  | `POST /openmeter/addons`                   | Create a new add-on.       |
| `client.addons.update`  | `PUT /openmeter/addons/{addonId}`          | Update an add-on by id.    |
| `client.addons.get`     | `GET /openmeter/addons/{addonId}`          | Get add-on by id.          |
| `client.addons.delete`  | `DELETE /openmeter/addons/{addonId}`       | Soft delete add-on by id.  |
| `client.addons.archive` | `POST /openmeter/addons/{addonId}/archive` | Archive an add-on version. |
| `client.addons.publish` | `POST /openmeter/addons/{addonId}/publish` | Publish an add-on version. |

### PlanAddons

| Method                     | HTTP                                                    | Description                              |
| -------------------------- | ------------------------------------------------------- | ---------------------------------------- |
| `client.planAddons.list`   | `GET /openmeter/plans/{planId}/addons`                  | List add-ons associated with a plan.     |
| `client.planAddons.create` | `POST /openmeter/plans/{planId}/addons`                 | Add an add-on to a plan.                 |
| `client.planAddons.get`    | `GET /openmeter/plans/{planId}/addons/{planAddonId}`    | Get an add-on association for a plan.    |
| `client.planAddons.update` | `PUT /openmeter/plans/{planId}/addons/{planAddonId}`    | Update an add-on association for a plan. |
| `client.planAddons.delete` | `DELETE /openmeter/plans/{planId}/addons/{planAddonId}` | Remove an add-on from a plan.            |

### Defaults

| Method                                       | HTTP                                | Description                           |
| -------------------------------------------- | ----------------------------------- | ------------------------------------- |
| `client.defaults.getOrganizationTaxCodes`    | `GET /openmeter/defaults/tax-codes` | Get organization default tax codes    |
| `client.defaults.updateOrganizationTaxCodes` | `PUT /openmeter/defaults/tax-codes` | Update organization default tax codes |

## Internal Operations

Operations marked internal in the API definition are exposed under
`client.internal.*`, quarantined from the customer surface. They are not
intended for customer use: they may require additional permissions, and
they can change or be removed without notice or semver consideration.

### Internal Subscriptions

| Method                                      | HTTP                                                    | Description                   |
| ------------------------------------------- | ------------------------------------------------------- | ----------------------------- |
| `client.internal.subscriptions.createAddon` | `POST /openmeter/subscriptions/{subscriptionId}/addons` | Add add-on to a subscription. |

### Internal Invoices

| Method                                        | HTTP                                                               | Description                                                                                                                                                                                                                                                                                                                                                                                                           |
| --------------------------------------------- | ------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `client.internal.invoices.list`               | `GET /openmeter/billing/invoices`                                  | List billing invoices. Returns a page of invoices. Gathering invoices are never included. Use `filter` to narrow by status, customer, dates, or service period start. Use `sort` to control ordering.                                                                                                                                                                                                                 |
| `client.internal.invoices.get`                | `GET /openmeter/billing/invoices/{invoiceId}`                      | Get a billing invoice by ID. Returns the full invoice resource including line items, status details, totals, and workflow configuration snapshot.                                                                                                                                                                                                                                                                     |
| `client.internal.invoices.update`             | `PUT /openmeter/billing/invoices/{invoiceId}`                      | Update a billing invoice. Only the mutable fields of the invoice can be edited: description, labels, supplier, customer, workflow settings, and top-level lines. Top-level lines are matched by `id`; lines without an `id` are created, and existing lines omitted from `lines` are deleted. Detailed (child) lines are always computed and cannot be edited directly. Only invoices in draft status can be updated. |
| `client.internal.invoices.delete`             | `DELETE /openmeter/billing/invoices/{invoiceId}`                   | Delete a billing invoice. Only standard invoices in draft status can be deleted. Deleting an invoice will also delete all associated line items and workflow configuration.                                                                                                                                                                                                                                           |
| `client.internal.invoices.advance`            | `POST /openmeter/billing/invoices/{invoiceId}/advance`             | Advance a billing invoice. Advances the invoice to the next workflow state. The next state is determined by the invoice's current status and workflow configuration. Only invoices in draft or issued status can be advanced.                                                                                                                                                                                         |
| `client.internal.invoices.approve`            | `POST /openmeter/billing/invoices/{invoiceId}/approve`             | Approve a billing invoice. This call instantly sends the invoice to the customer using the configured billing profile app. This call is valid in two invoice statuses: - draft: the invoice will be sent to the customer, the invoice state becomes issued - manual_approval_needed: the invoice will be sent to the customer, the invoice state becomes issued                                                       |
| `client.internal.invoices.retry`              | `POST /openmeter/billing/invoices/{invoiceId}/retry`               | Retry sending a billing invoice. Retry advancing the invoice after a failed attempt. The action can be called when the invoice's statusDetails' actions field contain the "retry" action.                                                                                                                                                                                                                             |
| `client.internal.invoices.snapshotQuantities` | `POST /openmeter/billing/invoices/{invoiceId}/snapshot-quantities` | Snapshot quantities for usage-based line items. This call will snapshot the quantities for all usage based line items in the invoice. This call is only valid in draft.waiting_for_collection status, where the collection period can be skipped using this action.                                                                                                                                                   |

### Internal Currencies

| Method                                            | HTTP                                                        | Description                                                                                                                    |
| ------------------------------------------------- | ----------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------ |
| `client.internal.currencies.list`                 | `GET /openmeter/currencies`                                 | List currencies supported by the billing system.                                                                               |
| `client.internal.currencies.createCustomCurrency` | `POST /openmeter/currencies/custom`                         | Create a custom currency. This operation allows defining your own custom currency for billing purposes.                        |
| `client.internal.currencies.listCostBases`        | `GET /openmeter/currencies/custom/{currencyId}/cost-bases`  | List cost bases for a currency. For custom currencies, there can be multiple cost bases with different `effective_from` dates. |
| `client.internal.currencies.createCostBasis`      | `POST /openmeter/currencies/custom/{currencyId}/cost-bases` | Create a cost basis for a currency.                                                                                            |

### Internal Governance

| Method                                   | HTTP                               | Description                                                                                                                                                                                                                                                                                                          |
| ---------------------------------------- | ---------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `client.internal.governance.queryAccess` | `POST /openmeter/governance/query` | Query feature access for a list of customers. The endpoint resolves each provided identifier to a customer and returns the access status for the requested features, plus optional credit balance availability. _Designed to be called on a fixed refresh interval and the query response is intended to be cached._ |

## Runtime Validation (validate option)

`validate` is off by default. The SDK's normal request/response mapping
only renames keys and converts dates — it never rejects a payload — so
an additive server-side change (a new response field, a new enum value)
never breaks a client running an older SDK version.

Set `validate: true` to additionally check the wire payload — the
request body after mapping to snake_case, and the raw response before
mapping back — against the generated `…Wire` schemas. Those schemas are
strict: an unknown field or an unrecognized enum value fails validation
instead of being silently accepted. That makes `validate` a tool for
catching SDK/server contract drift in development or CI, not something
to run in production, where forward compatibility with additive server
changes matters more than strict rejection.

```typescript
import { OpenMeter, ValidationError } from '@openmeter/client'

const client = new OpenMeter({
  baseUrl: 'https://openmeter.cloud/api/v3',
  apiKey: process.env.OPENMETER_API_KEY,
  validate: true,
})

try {
  await client.meters.get({ meterId: 'meter_123' })
} catch (error) {
  if (error instanceof ValidationError) {
    console.error(error.issues)
  }
}
```

The standalone functions surface the same failure as a `Result` instead
of throwing — check `result.error instanceof ValidationError`.

## Zod Schemas (./zod export)

`@openmeter/client/zod` exports every model twice: once shaped like the
SDK's public surface (camelCase keys, `Date` for date-time fields — the
same shape `meter.eventType`/`meter.createdAt` have in TypeScript) and
once as a strict `…Wire` schema shaped like the literal JSON the server
sends and accepts (snake_case keys, RFC 3339 date-time strings, unknown
fields rejected) — the same `…Wire` schemas the `validate` option checks
internally.

Use the public schemas to validate a payload the SDK did not produce —
a webhook body, a cached record, a test fixture — before trusting its
shape:

```typescript
import * as schemas from '@openmeter/client/zod'

const parsed = schemas.meter.safeParse({
  id: '01HZY3W6VXQK6H3NPC6DFA0PJT',
  name: 'Tokens',
  key: 'tokens',
  aggregation: 'sum',
  eventType: 'request',
  createdAt: new Date(),
  updatedAt: new Date(),
})

if (parsed.success) {
  console.log(parsed.data.eventType)
}
```

## Error Handling

A non-2xx response rejects with an `HTTPError` (`error.name === 'HTTPError'`)
carrying the problem-details fields (`status`, `type`, `title`, `url`)
from the response.

```typescript
import { OpenMeter, HTTPError } from '@openmeter/client'

const client = new OpenMeter({
  baseUrl: 'https://openmeter.cloud/api/v3',
  apiKey: process.env.OPENMETER_API_KEY,
})

try {
  await client.meters.get({ meterId: 'unknown' })
} catch (error) {
  if (error instanceof HTTPError) {
    console.error(error.status, error.title, error.type)
  }
}
```

`error.retryAfter` is the delta-seconds form of a numeric `Retry-After`
header (the common case on 429 and 503 responses) and `undefined`
otherwise. A 400 Bad Request additionally carries a typed
`error.invalidParameters` array describing which fields failed
validation; `error.getField(key)` is an untyped escape hatch for any
other problem-details extension member:

```typescript
import { OpenMeter, HTTPError } from '@openmeter/client'

const client = new OpenMeter({
  baseUrl: 'https://openmeter.cloud/api/v3',
  apiKey: process.env.OPENMETER_API_KEY,
})

try {
  await client.meters.create({
    name: 'Tokens',
    key: 'tokens',
    aggregation: 'sum',
    eventType: 'request',
  })
} catch (error) {
  if (error instanceof HTTPError && error.status === 400) {
    for (const param of error.invalidParameters ?? []) {
      console.error(param)
    }
  }
}
```

The SDK's other typed errors are `ValidationError` (see
[Runtime Validation (validate option)](#runtime-validation-validate-option)), `UnsafeIntegerError`
(an `int64`/`uint64` value exceeds what JSON can represent without
precision loss), `DepthLimitExceededError` (response data nested deeper
than the mapper's safety limit), and `PaginationLimitExceededError` (an
[Pagination](#pagination) iterable exceeded
its page-count safety limit) — each distinguished the same way, by
`instanceof` or by `.name`.

## Standalone Functions

Every method is also available as a standalone, tree-shakeable function
that takes a `Client` and returns a `Result` instead of throwing.

```typescript
import { Client, funcs } from '@openmeter/client'

const client = new Client({
  baseUrl: 'https://openmeter.cloud/api/v3',
  apiKey: process.env.OPENMETER_API_KEY,
})

const result = await funcs.listMeters(client)
if (result.ok) {
  console.log(result.value)
} else {
  console.error(result.error)
}
```

`ok`, `err`, and `unwrap` — the helpers `Result` is built from — are
exported too, so a func call can be unwrapped back into a throwing call
where that is more convenient:

```typescript
import { Client, funcs, unwrap } from '@openmeter/client'

const client = new Client({
  baseUrl: 'https://openmeter.cloud/api/v3',
  apiKey: process.env.OPENMETER_API_KEY,
})

const meters = unwrap(await funcs.listMeters(client))
```