@fhirfly-io/terminology 0.7.1 → 0.8.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +206 -29
- package/dist/index.cjs +199 -13
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +389 -24
- package/dist/index.d.ts +389 -24
- package/dist/index.js +199 -13
- package/dist/index.js.map +1 -1
- package/package.json +12 -4
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @fhirfly-io/terminology
|
|
2
2
|
|
|
3
|
-
Official FHIRfly SDK for Node.js — typed access to healthcare reference data APIs
|
|
3
|
+
Official FHIRfly SDK for Node.js — typed access to healthcare reference data APIs.
|
|
4
4
|
|
|
5
5
|
[](https://www.npmjs.com/package/@fhirfly-io/terminology)
|
|
6
6
|
[](https://opensource.org/licenses/MIT)
|
|
@@ -20,7 +20,7 @@ const client = new Fhirfly({ apiKey: "your-api-key" });
|
|
|
20
20
|
|
|
21
21
|
// Look up a drug by NDC
|
|
22
22
|
const ndc = await client.ndc.lookup("0069-0151-01");
|
|
23
|
-
console.log(ndc.data.
|
|
23
|
+
console.log(ndc.data.brand_name); // "Lipitor"
|
|
24
24
|
|
|
25
25
|
// Look up a provider by NPI
|
|
26
26
|
const npi = await client.npi.lookup("1234567890");
|
|
@@ -30,20 +30,22 @@ console.log(npi.data.name);
|
|
|
30
30
|
## Features
|
|
31
31
|
|
|
32
32
|
- **Full TypeScript support** with comprehensive type definitions
|
|
33
|
-
- **
|
|
33
|
+
- **11 healthcare domains**: NDC, NPI, RxNorm, LOINC, ICD-10, CVX, MVX, FDA Labels, SNOMED CT, Connectivity, Claims
|
|
34
|
+
- **Search** with full-text queries, filters, facets, and pagination
|
|
34
35
|
- **Batch lookups** for efficient bulk operations
|
|
35
36
|
- **Response shapes**: compact, standard, or full detail levels
|
|
37
|
+
- **Two auth modes**: API key (simple) and OAuth2 client credentials (secure)
|
|
36
38
|
- **Automatic retries** with exponential backoff
|
|
37
39
|
- **Detailed error types** for proper error handling
|
|
38
|
-
- **
|
|
40
|
+
- **Zero runtime dependencies**
|
|
39
41
|
|
|
40
|
-
##
|
|
42
|
+
## Authentication
|
|
41
43
|
|
|
42
|
-
###
|
|
44
|
+
### API Key (Simple)
|
|
43
45
|
|
|
44
46
|
```typescript
|
|
45
47
|
const client = new Fhirfly({
|
|
46
|
-
apiKey: "
|
|
48
|
+
apiKey: "ffly_sk_live_...",
|
|
47
49
|
baseUrl: "https://api.fhirfly.io", // Optional, default shown
|
|
48
50
|
timeout: 30000, // Optional, request timeout in ms
|
|
49
51
|
maxRetries: 3, // Optional, retry attempts
|
|
@@ -51,6 +53,21 @@ const client = new Fhirfly({
|
|
|
51
53
|
});
|
|
52
54
|
```
|
|
53
55
|
|
|
56
|
+
### OAuth2 Client Credentials (Secure)
|
|
57
|
+
|
|
58
|
+
```typescript
|
|
59
|
+
const client = new Fhirfly({
|
|
60
|
+
clientId: "ffly_client_...",
|
|
61
|
+
clientSecret: "ffly_secret_...",
|
|
62
|
+
scopes: ["ndc.read", "npi.read"], // Optional
|
|
63
|
+
tokenUrl: "https://api.fhirfly.io/oauth2/token", // Optional, default shown
|
|
64
|
+
});
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
OAuth2 tokens are cached and automatically refreshed before expiry.
|
|
68
|
+
|
|
69
|
+
## API Reference
|
|
70
|
+
|
|
54
71
|
### NDC (National Drug Codes)
|
|
55
72
|
|
|
56
73
|
```typescript
|
|
@@ -70,13 +87,20 @@ const results = await client.ndc.lookupMany([
|
|
|
70
87
|
]);
|
|
71
88
|
|
|
72
89
|
for (const item of results.results) {
|
|
73
|
-
if (item.
|
|
74
|
-
console.log(item.data.
|
|
90
|
+
if (item.status === "ok") {
|
|
91
|
+
console.log(item.data.brand_name);
|
|
75
92
|
}
|
|
76
93
|
}
|
|
94
|
+
|
|
95
|
+
// Search
|
|
96
|
+
const results = await client.ndc.search({
|
|
97
|
+
q: "advil",
|
|
98
|
+
dosage_form: "TABLET",
|
|
99
|
+
is_active: true,
|
|
100
|
+
});
|
|
77
101
|
```
|
|
78
102
|
|
|
79
|
-
Batch lookups return per-item results and never throw for individual misses.
|
|
103
|
+
Batch lookups return per-item results and never throw for individual misses. Each item has `status: "ok" | "not_found" | "invalid"`.
|
|
80
104
|
|
|
81
105
|
### NPI (National Provider Identifiers)
|
|
82
106
|
|
|
@@ -84,8 +108,17 @@ Batch lookups return per-item results and never throw for individual misses.
|
|
|
84
108
|
// Single lookup
|
|
85
109
|
const npi = await client.npi.lookup("1234567890");
|
|
86
110
|
|
|
87
|
-
// Batch lookup
|
|
111
|
+
// Batch lookup (up to 100)
|
|
88
112
|
const results = await client.npi.lookupMany(["1234567890", "0987654321"]);
|
|
113
|
+
|
|
114
|
+
// Search for providers
|
|
115
|
+
const results = await client.npi.search({
|
|
116
|
+
q: "smith",
|
|
117
|
+
specialty: "cardiology",
|
|
118
|
+
state: "CA",
|
|
119
|
+
entity_type: "individual",
|
|
120
|
+
});
|
|
121
|
+
console.log(`Found ${results.total} providers`);
|
|
89
122
|
```
|
|
90
123
|
|
|
91
124
|
### RxNorm
|
|
@@ -94,6 +127,12 @@ const results = await client.npi.lookupMany(["1234567890", "0987654321"]);
|
|
|
94
127
|
// Look up by RxCUI
|
|
95
128
|
const rx = await client.rxnorm.lookup("213169");
|
|
96
129
|
console.log(rx.data.name); // "atorvastatin 10 MG Oral Tablet"
|
|
130
|
+
|
|
131
|
+
// Search for drugs
|
|
132
|
+
const results = await client.rxnorm.search({
|
|
133
|
+
ingredient: "metformin",
|
|
134
|
+
is_prescribable: true,
|
|
135
|
+
});
|
|
97
136
|
```
|
|
98
137
|
|
|
99
138
|
### LOINC
|
|
@@ -101,28 +140,50 @@ console.log(rx.data.name); // "atorvastatin 10 MG Oral Tablet"
|
|
|
101
140
|
```typescript
|
|
102
141
|
// Look up lab code
|
|
103
142
|
const loinc = await client.loinc.lookup("2345-7");
|
|
104
|
-
console.log(loinc.data.
|
|
143
|
+
console.log(loinc.data.display_name);
|
|
144
|
+
|
|
145
|
+
// Search
|
|
146
|
+
const results = await client.loinc.search({
|
|
147
|
+
q: "glucose",
|
|
148
|
+
class: "CHEM",
|
|
149
|
+
system: "Bld",
|
|
150
|
+
});
|
|
105
151
|
```
|
|
106
152
|
|
|
107
153
|
### ICD-10
|
|
108
154
|
|
|
155
|
+
The API auto-detects CM (diagnoses) vs PCS (procedures) based on code format.
|
|
156
|
+
|
|
109
157
|
```typescript
|
|
110
|
-
//
|
|
111
|
-
const diagnosis = await client.icd10.
|
|
158
|
+
// Diagnosis code (CM)
|
|
159
|
+
const diagnosis = await client.icd10.lookup("E11.9");
|
|
160
|
+
console.log(diagnosis.data.display); // "Type 2 diabetes mellitus without complications"
|
|
161
|
+
|
|
162
|
+
// Procedure code (PCS)
|
|
163
|
+
const procedure = await client.icd10.lookup("02HA0QZ");
|
|
112
164
|
|
|
113
|
-
//
|
|
114
|
-
const
|
|
165
|
+
// Batch lookup (mix of CM and PCS, up to 100)
|
|
166
|
+
const results = await client.icd10.lookupMany(["E11.9", "I10", "02HA0QZ"]);
|
|
115
167
|
|
|
116
|
-
//
|
|
117
|
-
const
|
|
118
|
-
|
|
168
|
+
// Search
|
|
169
|
+
const results = await client.icd10.search({
|
|
170
|
+
q: "diabetes",
|
|
171
|
+
code_system: "CM",
|
|
172
|
+
billable: true,
|
|
173
|
+
});
|
|
119
174
|
```
|
|
120
175
|
|
|
121
176
|
### CVX (Vaccine Codes)
|
|
122
177
|
|
|
123
178
|
```typescript
|
|
124
179
|
const cvx = await client.cvx.lookup("208");
|
|
125
|
-
console.log(cvx.data.
|
|
180
|
+
console.log(cvx.data.display);
|
|
181
|
+
|
|
182
|
+
// Search for COVID vaccines
|
|
183
|
+
const results = await client.cvx.search({
|
|
184
|
+
is_covid_vaccine: true,
|
|
185
|
+
status: "active",
|
|
186
|
+
});
|
|
126
187
|
```
|
|
127
188
|
|
|
128
189
|
### MVX (Vaccine Manufacturers)
|
|
@@ -130,33 +191,149 @@ console.log(cvx.data.short_description);
|
|
|
130
191
|
```typescript
|
|
131
192
|
const mvx = await client.mvx.lookup("PFR");
|
|
132
193
|
console.log(mvx.data.manufacturer_name); // "Pfizer, Inc"
|
|
194
|
+
|
|
195
|
+
const results = await client.mvx.search({ q: "pfizer" });
|
|
133
196
|
```
|
|
134
197
|
|
|
135
198
|
### FDA Labels
|
|
136
199
|
|
|
137
200
|
```typescript
|
|
138
|
-
//
|
|
139
|
-
const label = await client.fdaLabels.lookup("
|
|
201
|
+
// Look up by Set ID, NDC, or RxCUI (auto-detected)
|
|
202
|
+
const label = await client.fdaLabels.lookup("0069-0151-01");
|
|
203
|
+
|
|
204
|
+
// With safety sections
|
|
205
|
+
const label = await client.fdaLabels.lookup("0069-0151-01", {
|
|
206
|
+
bundle: "safety",
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
// With specific sections
|
|
210
|
+
const label = await client.fdaLabels.lookup("0069-0151-01", {
|
|
211
|
+
sections: ["boxed_warning", "dosage_and_administration"],
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
// Batch (up to 50, metadata only)
|
|
215
|
+
const results = await client.fdaLabels.lookupMany(["0069-0151-01", "0002-1433-80"]);
|
|
216
|
+
|
|
217
|
+
// Search
|
|
218
|
+
const results = await client.fdaLabels.search({
|
|
219
|
+
substance: "acetaminophen",
|
|
220
|
+
product_type: "otc",
|
|
221
|
+
});
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
### SNOMED CT (IPS)
|
|
225
|
+
|
|
226
|
+
Access to ~12,000 curated clinical concepts from the SNOMED CT IPS (International Patient Set), licensed under CC BY 4.0.
|
|
227
|
+
|
|
228
|
+
```typescript
|
|
229
|
+
// Look up a concept
|
|
230
|
+
const concept = await client.snomed.lookup("73211009");
|
|
231
|
+
console.log(concept.data.preferred_term); // "Diabetes mellitus"
|
|
232
|
+
|
|
233
|
+
// Batch lookup (up to 100)
|
|
234
|
+
const results = await client.snomed.lookupMany(["73211009", "38341003"]);
|
|
235
|
+
|
|
236
|
+
// Search by category
|
|
237
|
+
const results = await client.snomed.search({
|
|
238
|
+
q: "diabetes",
|
|
239
|
+
ips_category: "condition",
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
// List all IPS categories
|
|
243
|
+
const categories = await client.snomed.categories();
|
|
244
|
+
|
|
245
|
+
// Reverse mappings (what ICD-10/RxNorm/NDC codes map to this concept?)
|
|
246
|
+
const mappings = await client.snomed.mappings("73211009");
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
### Connectivity Intelligence
|
|
250
|
+
|
|
251
|
+
Discover how to reach a provider's organization electronically — FHIR endpoints, Direct addresses, and more.
|
|
252
|
+
|
|
253
|
+
```typescript
|
|
254
|
+
// Look up connectivity options for a provider
|
|
255
|
+
const conn = await client.connectivity.lookup("1234567890");
|
|
256
|
+
|
|
257
|
+
console.log(conn.provider_summary.name);
|
|
140
258
|
|
|
141
|
-
|
|
142
|
-
|
|
259
|
+
for (const target of conn.connectivity_targets) {
|
|
260
|
+
console.log(`${target.name} (${target.type})`);
|
|
261
|
+
for (const ep of target.endpoints) {
|
|
262
|
+
console.log(` ${ep.type}: ${ep.url} [${ep.status}]`);
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
### Claims Intelligence
|
|
268
|
+
|
|
269
|
+
CMS claims editing and payment data. Requires the `claims.read` scope.
|
|
270
|
+
|
|
271
|
+
```typescript
|
|
272
|
+
// NCCI PTP: Can these codes be billed together?
|
|
273
|
+
const ncci = await client.claims.validateNcci("99213", "99214");
|
|
274
|
+
console.log(ncci.data.can_bill_together); // true/false
|
|
275
|
+
console.log(ncci.data.summary);
|
|
276
|
+
|
|
277
|
+
// MUE: Maximum units for a code
|
|
278
|
+
const mue = await client.claims.lookupMue("99213");
|
|
279
|
+
for (const limit of mue.data.limits) {
|
|
280
|
+
console.log(`${limit.service_type}: max ${limit.mue_value} units`);
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
// Batch MUE (up to 100 codes)
|
|
284
|
+
const mueResults = await client.claims.lookupMueMany(["99213", "99214"]);
|
|
285
|
+
|
|
286
|
+
// PFS/RVU: Fee schedule and relative value units
|
|
287
|
+
const pfs = await client.claims.lookupPfs("99213");
|
|
288
|
+
console.log(`Work RVU: ${pfs.data.rvu.work}`);
|
|
289
|
+
console.log(`Non-facility payment: $${pfs.data.calculated_payment.non_facility}`);
|
|
290
|
+
|
|
291
|
+
// Batch PFS (up to 100 codes)
|
|
292
|
+
const pfsResults = await client.claims.lookupPfsMany(["99213", "99214", "99215"]);
|
|
293
|
+
|
|
294
|
+
// LCD/NCD Coverage determination
|
|
295
|
+
const coverage = await client.claims.checkCoverage("99213");
|
|
296
|
+
console.log(`${coverage.data.policies_found} coverage policies found`);
|
|
143
297
|
```
|
|
144
298
|
|
|
145
299
|
## Response Shapes
|
|
146
300
|
|
|
147
|
-
All lookup methods accept a `shape` option to control response detail:
|
|
301
|
+
All lookup and search methods accept a `shape` option to control response detail:
|
|
148
302
|
|
|
149
303
|
| Shape | Description |
|
|
150
304
|
|-------|-------------|
|
|
151
|
-
| `compact` | Minimal fields for quick lookups |
|
|
152
|
-
| `standard` | Balanced detail (default) |
|
|
153
|
-
| `full` | Complete data with
|
|
305
|
+
| `compact` | Minimal fields for quick lookups and autocomplete |
|
|
306
|
+
| `standard` | Balanced detail (default for REST) |
|
|
307
|
+
| `full` | Complete data with provenance (default for MCP/agents) |
|
|
154
308
|
|
|
155
309
|
```typescript
|
|
156
|
-
// Get full details
|
|
157
310
|
const ndc = await client.ndc.lookup("0069-0151-01", { shape: "full" });
|
|
158
311
|
```
|
|
159
312
|
|
|
313
|
+
**Exceptions**: SNOMED always returns full data (no shapes). FDA Labels lookup uses a metadata + sections model instead of shapes; search uses shapes.
|
|
314
|
+
|
|
315
|
+
## Search
|
|
316
|
+
|
|
317
|
+
All endpoints except Connectivity support full-text search with filters, facets, and pagination:
|
|
318
|
+
|
|
319
|
+
```typescript
|
|
320
|
+
const results = await client.ndc.search(
|
|
321
|
+
{ q: "advil", is_active: true }, // Search params
|
|
322
|
+
{ shape: "compact", limit: 50, page: 1 } // Options
|
|
323
|
+
);
|
|
324
|
+
|
|
325
|
+
console.log(`${results.total} results, page ${results.page}`);
|
|
326
|
+
console.log(results.facets); // Facet counts for filtering
|
|
327
|
+
|
|
328
|
+
for (const item of results.items) {
|
|
329
|
+
console.log(item.name);
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
if (results.has_more) {
|
|
333
|
+
// Fetch next page...
|
|
334
|
+
}
|
|
335
|
+
```
|
|
336
|
+
|
|
160
337
|
## Error Handling
|
|
161
338
|
|
|
162
339
|
The SDK provides typed errors for different failure scenarios:
|
package/dist/index.cjs
CHANGED
|
@@ -171,7 +171,7 @@ var HttpClient = class {
|
|
|
171
171
|
timeout: config.timeout ?? 3e4,
|
|
172
172
|
maxRetries: config.maxRetries ?? 3,
|
|
173
173
|
retryDelay: config.retryDelay ?? 1e3,
|
|
174
|
-
userAgent: config.userAgent ?? `@fhirfly/
|
|
174
|
+
userAgent: config.userAgent ?? `@fhirfly-io/terminology/0.7.1 Node.js/${process.version}`
|
|
175
175
|
};
|
|
176
176
|
}
|
|
177
177
|
/**
|
|
@@ -308,7 +308,7 @@ var HttpClient = class {
|
|
|
308
308
|
if (response.status === 429) {
|
|
309
309
|
const retryAfter = response.headers.get("retry-after");
|
|
310
310
|
if (retryAfter && attempt < this.config.maxRetries) {
|
|
311
|
-
await this.sleep(parseInt(retryAfter, 10) * 1e3);
|
|
311
|
+
await this.sleep(Math.min(parseInt(retryAfter, 10) * 1e3, 12e4));
|
|
312
312
|
continue;
|
|
313
313
|
}
|
|
314
314
|
await this.parseErrorResponse(response, endpoint);
|
|
@@ -387,7 +387,7 @@ var NdcEndpoint = class {
|
|
|
387
387
|
* @example
|
|
388
388
|
* ```ts
|
|
389
389
|
* const ndc = await client.ndc.lookup("0069-0151-01");
|
|
390
|
-
* console.log(ndc.data.
|
|
390
|
+
* console.log(ndc.data.brand_name); // "Lipitor"
|
|
391
391
|
* ```
|
|
392
392
|
*/
|
|
393
393
|
async lookup(code, options) {
|
|
@@ -409,15 +409,17 @@ var NdcEndpoint = class {
|
|
|
409
409
|
* ]);
|
|
410
410
|
*
|
|
411
411
|
* for (const item of results.results) {
|
|
412
|
-
* if (item.
|
|
413
|
-
* console.log(item.data.
|
|
412
|
+
* if (item.status === "ok") {
|
|
413
|
+
* console.log(item.data.brand_name);
|
|
414
414
|
* } else {
|
|
415
|
-
* console.log(`Not found: ${item.
|
|
415
|
+
* console.log(`Not found: ${item.input}`);
|
|
416
416
|
* }
|
|
417
417
|
* }
|
|
418
418
|
* ```
|
|
419
419
|
*/
|
|
420
420
|
async lookupMany(codes, options) {
|
|
421
|
+
if (codes.length === 0) throw new ValidationError("codes array must not be empty");
|
|
422
|
+
if (codes.length > 500) throw new ValidationError(`NDC batch lookup supports max 500 codes, got ${codes.length}`);
|
|
421
423
|
return this.http.post(
|
|
422
424
|
"/v1/ndc/_batch",
|
|
423
425
|
{ codes },
|
|
@@ -482,7 +484,7 @@ var NpiEndpoint = class {
|
|
|
482
484
|
/**
|
|
483
485
|
* Look up multiple NPIs in a single request.
|
|
484
486
|
*
|
|
485
|
-
* @param npis - Array of 10-digit NPI numbers (max
|
|
487
|
+
* @param npis - Array of 10-digit NPI numbers (max 100)
|
|
486
488
|
* @param options - Response shape, include, and batch options
|
|
487
489
|
* @returns Batch response with results for each NPI
|
|
488
490
|
*
|
|
@@ -495,6 +497,8 @@ var NpiEndpoint = class {
|
|
|
495
497
|
* ```
|
|
496
498
|
*/
|
|
497
499
|
async lookupMany(npis, options) {
|
|
500
|
+
if (npis.length === 0) throw new ValidationError("npis array must not be empty");
|
|
501
|
+
if (npis.length > 100) throw new ValidationError(`NPI batch lookup supports max 100 codes, got ${npis.length}`);
|
|
498
502
|
return this.http.post(
|
|
499
503
|
"/v1/npi/_batch",
|
|
500
504
|
{ codes: npis },
|
|
@@ -556,11 +560,13 @@ var RxNormEndpoint = class {
|
|
|
556
560
|
/**
|
|
557
561
|
* Look up multiple RxCUIs in a single request.
|
|
558
562
|
*
|
|
559
|
-
* @param rxcuis - Array of RxCUIs (max
|
|
563
|
+
* @param rxcuis - Array of RxCUIs (max 100)
|
|
560
564
|
* @param options - Response shape, include, and batch options
|
|
561
565
|
* @returns Batch response with results for each RxCUI
|
|
562
566
|
*/
|
|
563
567
|
async lookupMany(rxcuis, options) {
|
|
568
|
+
if (rxcuis.length === 0) throw new ValidationError("rxcuis array must not be empty");
|
|
569
|
+
if (rxcuis.length > 100) throw new ValidationError(`RxNorm batch lookup supports max 100 codes, got ${rxcuis.length}`);
|
|
564
570
|
return this.http.post(
|
|
565
571
|
"/v1/rxnorm/_batch",
|
|
566
572
|
{ codes: rxcuis },
|
|
@@ -619,11 +625,13 @@ var LoincEndpoint = class {
|
|
|
619
625
|
/**
|
|
620
626
|
* Look up multiple LOINC codes in a single request.
|
|
621
627
|
*
|
|
622
|
-
* @param loincNums - Array of LOINC numbers (max
|
|
628
|
+
* @param loincNums - Array of LOINC numbers (max 100)
|
|
623
629
|
* @param options - Response shape, include, and batch options
|
|
624
630
|
* @returns Batch response with results for each LOINC
|
|
625
631
|
*/
|
|
626
632
|
async lookupMany(loincNums, options) {
|
|
633
|
+
if (loincNums.length === 0) throw new ValidationError("loincNums array must not be empty");
|
|
634
|
+
if (loincNums.length > 100) throw new ValidationError(`LOINC batch lookup supports max 100 codes, got ${loincNums.length}`);
|
|
627
635
|
return this.http.post(
|
|
628
636
|
"/v1/loinc/_batch",
|
|
629
637
|
{ codes: loincNums },
|
|
@@ -693,11 +701,13 @@ var Icd10Endpoint = class {
|
|
|
693
701
|
*
|
|
694
702
|
* Codes can be a mix of CM (diagnoses) and PCS (procedures).
|
|
695
703
|
*
|
|
696
|
-
* @param codes - Array of ICD-10 codes (max
|
|
704
|
+
* @param codes - Array of ICD-10 codes (max 100)
|
|
697
705
|
* @param options - Response shape, include, and batch options
|
|
698
706
|
* @returns Batch response with results for each code
|
|
699
707
|
*/
|
|
700
708
|
async lookupMany(codes, options) {
|
|
709
|
+
if (codes.length === 0) throw new ValidationError("codes array must not be empty");
|
|
710
|
+
if (codes.length > 100) throw new ValidationError(`ICD-10 batch lookup supports max 100 codes, got ${codes.length}`);
|
|
701
711
|
return this.http.post(
|
|
702
712
|
"/v1/icd10/_batch",
|
|
703
713
|
{ codes },
|
|
@@ -760,11 +770,13 @@ var CvxEndpoint = class {
|
|
|
760
770
|
/**
|
|
761
771
|
* Look up multiple CVX codes in a single request.
|
|
762
772
|
*
|
|
763
|
-
* @param cvxCodes - Array of CVX codes (max
|
|
773
|
+
* @param cvxCodes - Array of CVX codes (max 100)
|
|
764
774
|
* @param options - Response shape, include, and batch options
|
|
765
775
|
* @returns Batch response with results for each code
|
|
766
776
|
*/
|
|
767
777
|
async lookupMany(cvxCodes, options) {
|
|
778
|
+
if (cvxCodes.length === 0) throw new ValidationError("cvxCodes array must not be empty");
|
|
779
|
+
if (cvxCodes.length > 100) throw new ValidationError(`CVX batch lookup supports max 100 codes, got ${cvxCodes.length}`);
|
|
768
780
|
return this.http.post(
|
|
769
781
|
"/v1/cvx/_batch",
|
|
770
782
|
{ codes: cvxCodes },
|
|
@@ -823,11 +835,13 @@ var MvxEndpoint = class {
|
|
|
823
835
|
/**
|
|
824
836
|
* Look up multiple MVX codes in a single request.
|
|
825
837
|
*
|
|
826
|
-
* @param mvxCodes - Array of MVX codes (max
|
|
838
|
+
* @param mvxCodes - Array of MVX codes (max 100)
|
|
827
839
|
* @param options - Response shape, include, and batch options
|
|
828
840
|
* @returns Batch response with results for each code
|
|
829
841
|
*/
|
|
830
842
|
async lookupMany(mvxCodes, options) {
|
|
843
|
+
if (mvxCodes.length === 0) throw new ValidationError("mvxCodes array must not be empty");
|
|
844
|
+
if (mvxCodes.length > 100) throw new ValidationError(`MVX batch lookup supports max 100 codes, got ${mvxCodes.length}`);
|
|
831
845
|
return this.http.post(
|
|
832
846
|
"/v1/mvx/_batch",
|
|
833
847
|
{ codes: mvxCodes },
|
|
@@ -909,10 +923,12 @@ var FdaLabelsEndpoint = class {
|
|
|
909
923
|
* Returns metadata only (no sections) for batch efficiency.
|
|
910
924
|
* Identifiers can be Set IDs, NDC codes, or RxCUIs (mixed).
|
|
911
925
|
*
|
|
912
|
-
* @param identifiers - Array of identifiers (max
|
|
926
|
+
* @param identifiers - Array of identifiers (max 50)
|
|
913
927
|
* @returns Batch response with results for each identifier
|
|
914
928
|
*/
|
|
915
929
|
async lookupMany(identifiers) {
|
|
930
|
+
if (identifiers.length === 0) throw new ValidationError("identifiers array must not be empty");
|
|
931
|
+
if (identifiers.length > 50) throw new ValidationError(`FDA Labels batch lookup supports max 50 identifiers, got ${identifiers.length}`);
|
|
916
932
|
return this.http.post(
|
|
917
933
|
"/v1/fda-label/_batch",
|
|
918
934
|
{ codes: identifiers }
|
|
@@ -1034,6 +1050,8 @@ var SnomedEndpoint = class {
|
|
|
1034
1050
|
* ```
|
|
1035
1051
|
*/
|
|
1036
1052
|
async lookupMany(conceptIds) {
|
|
1053
|
+
if (conceptIds.length === 0) throw new ValidationError("conceptIds array must not be empty");
|
|
1054
|
+
if (conceptIds.length > 100) throw new ValidationError(`SNOMED batch lookup supports max 100 codes, got ${conceptIds.length}`);
|
|
1037
1055
|
return this.http.post(
|
|
1038
1056
|
"/v1/snomed/_batch",
|
|
1039
1057
|
{ codes: conceptIds }
|
|
@@ -1100,6 +1118,168 @@ var SnomedEndpoint = class {
|
|
|
1100
1118
|
}
|
|
1101
1119
|
};
|
|
1102
1120
|
|
|
1121
|
+
// src/endpoints/claims.ts
|
|
1122
|
+
var ClaimsEndpoint = class {
|
|
1123
|
+
constructor(http) {
|
|
1124
|
+
this.http = http;
|
|
1125
|
+
}
|
|
1126
|
+
// ==========================================================================
|
|
1127
|
+
// NCCI PTP Edits
|
|
1128
|
+
// ==========================================================================
|
|
1129
|
+
/**
|
|
1130
|
+
* Validate whether two CPT/HCPCS codes can be billed together.
|
|
1131
|
+
*
|
|
1132
|
+
* @param code1 - First CPT/HCPCS code (4-5 alphanumeric characters)
|
|
1133
|
+
* @param code2 - Second CPT/HCPCS code (4-5 alphanumeric characters)
|
|
1134
|
+
* @param options - Optional filters (claim_type)
|
|
1135
|
+
* @returns Validation result with edit details and billing summary
|
|
1136
|
+
*
|
|
1137
|
+
* @example
|
|
1138
|
+
* ```ts
|
|
1139
|
+
* const result = await client.claims.validateNcci("99213", "99214");
|
|
1140
|
+
* if (!result.data.can_bill_together) {
|
|
1141
|
+
* console.log(result.data.summary);
|
|
1142
|
+
* for (const edit of result.data.edits) {
|
|
1143
|
+
* console.log(`${edit.claim_type}: modifier ${edit.modifier_allowed ? "allowed" : "not allowed"}`);
|
|
1144
|
+
* }
|
|
1145
|
+
* }
|
|
1146
|
+
* ```
|
|
1147
|
+
*/
|
|
1148
|
+
async validateNcci(code1, code2, options) {
|
|
1149
|
+
const params = {
|
|
1150
|
+
code1: encodeURIComponent(code1),
|
|
1151
|
+
code2: encodeURIComponent(code2)
|
|
1152
|
+
};
|
|
1153
|
+
if (options?.claim_type) {
|
|
1154
|
+
params.claim_type = options.claim_type;
|
|
1155
|
+
}
|
|
1156
|
+
const queryString = this.http.buildSearchQueryString(params);
|
|
1157
|
+
return this.http.get(`/v1/ncci/validate${queryString}`);
|
|
1158
|
+
}
|
|
1159
|
+
// ==========================================================================
|
|
1160
|
+
// MUE (Medically Unlikely Edits)
|
|
1161
|
+
// ==========================================================================
|
|
1162
|
+
/**
|
|
1163
|
+
* Look up MUE limits for a HCPCS/CPT code.
|
|
1164
|
+
*
|
|
1165
|
+
* @param hcpcs - HCPCS/CPT code (4-5 alphanumeric characters)
|
|
1166
|
+
* @param options - Optional filters (service_type)
|
|
1167
|
+
* @returns MUE limit data including max units per service type
|
|
1168
|
+
*
|
|
1169
|
+
* @example
|
|
1170
|
+
* ```ts
|
|
1171
|
+
* const mue = await client.claims.lookupMue("99213");
|
|
1172
|
+
* for (const limit of mue.data.limits) {
|
|
1173
|
+
* console.log(`${limit.service_type}: max ${limit.mue_value} units`);
|
|
1174
|
+
* }
|
|
1175
|
+
* ```
|
|
1176
|
+
*/
|
|
1177
|
+
async lookupMue(hcpcs, options) {
|
|
1178
|
+
const params = {};
|
|
1179
|
+
if (options?.service_type) {
|
|
1180
|
+
params.service_type = options.service_type;
|
|
1181
|
+
}
|
|
1182
|
+
const queryString = this.http.buildSearchQueryString(params);
|
|
1183
|
+
return this.http.get(
|
|
1184
|
+
`/v1/mue/${encodeURIComponent(hcpcs)}${queryString}`
|
|
1185
|
+
);
|
|
1186
|
+
}
|
|
1187
|
+
/**
|
|
1188
|
+
* Batch MUE lookup for multiple HCPCS codes.
|
|
1189
|
+
*
|
|
1190
|
+
* @param codes - Array of HCPCS/CPT codes (max 100)
|
|
1191
|
+
* @returns Batch results with per-code MUE limits
|
|
1192
|
+
*
|
|
1193
|
+
* @example
|
|
1194
|
+
* ```ts
|
|
1195
|
+
* const results = await client.claims.lookupMueMany(["99213", "99214", "99215"]);
|
|
1196
|
+
* for (const item of results.results) {
|
|
1197
|
+
* if (item.status === "ok") {
|
|
1198
|
+
* console.log(`${item.hcpcs_code}: ${item.data.limits.length} limits`);
|
|
1199
|
+
* }
|
|
1200
|
+
* }
|
|
1201
|
+
* ```
|
|
1202
|
+
*/
|
|
1203
|
+
async lookupMueMany(codes) {
|
|
1204
|
+
if (codes.length === 0) throw new ValidationError("codes array must not be empty");
|
|
1205
|
+
if (codes.length > 100) throw new ValidationError(`MUE batch lookup supports max 100 codes, got ${codes.length}`);
|
|
1206
|
+
return this.http.post("/v1/mue/_batch", { codes });
|
|
1207
|
+
}
|
|
1208
|
+
// ==========================================================================
|
|
1209
|
+
// PFS/RVU (Physician Fee Schedule / Relative Value Units)
|
|
1210
|
+
// ==========================================================================
|
|
1211
|
+
/**
|
|
1212
|
+
* Look up Physician Fee Schedule and RVU data for a HCPCS/CPT code.
|
|
1213
|
+
*
|
|
1214
|
+
* @param hcpcs - HCPCS/CPT code (4-5 alphanumeric characters)
|
|
1215
|
+
* @returns Fee schedule data including RVU breakdown and calculated payments
|
|
1216
|
+
*
|
|
1217
|
+
* @example
|
|
1218
|
+
* ```ts
|
|
1219
|
+
* const pfs = await client.claims.lookupPfs("99213");
|
|
1220
|
+
* console.log(`Description: ${pfs.data.description}`);
|
|
1221
|
+
* console.log(`Work RVU: ${pfs.data.rvu.work}`);
|
|
1222
|
+
* console.log(`Non-facility payment: $${pfs.data.calculated_payment.non_facility}`);
|
|
1223
|
+
* console.log(`Facility payment: $${pfs.data.calculated_payment.facility}`);
|
|
1224
|
+
* ```
|
|
1225
|
+
*/
|
|
1226
|
+
async lookupPfs(hcpcs) {
|
|
1227
|
+
return this.http.get(
|
|
1228
|
+
`/v1/pfs/${encodeURIComponent(hcpcs)}`
|
|
1229
|
+
);
|
|
1230
|
+
}
|
|
1231
|
+
/**
|
|
1232
|
+
* Batch PFS/RVU lookup for multiple HCPCS codes.
|
|
1233
|
+
*
|
|
1234
|
+
* @param codes - Array of HCPCS/CPT codes (max 100)
|
|
1235
|
+
* @returns Batch results with per-code fee schedule data
|
|
1236
|
+
*
|
|
1237
|
+
* @example
|
|
1238
|
+
* ```ts
|
|
1239
|
+
* const results = await client.claims.lookupPfsMany(["99213", "99214", "99215"]);
|
|
1240
|
+
* for (const item of results.results) {
|
|
1241
|
+
* if (item.status === "ok") {
|
|
1242
|
+
* console.log(`${item.hcpcs_code}: $${item.data.calculated_payment.non_facility}`);
|
|
1243
|
+
* }
|
|
1244
|
+
* }
|
|
1245
|
+
* ```
|
|
1246
|
+
*/
|
|
1247
|
+
async lookupPfsMany(codes) {
|
|
1248
|
+
if (codes.length === 0) throw new ValidationError("codes array must not be empty");
|
|
1249
|
+
if (codes.length > 100) throw new ValidationError(`PFS batch lookup supports max 100 codes, got ${codes.length}`);
|
|
1250
|
+
return this.http.post("/v1/pfs/_batch", { codes });
|
|
1251
|
+
}
|
|
1252
|
+
// ==========================================================================
|
|
1253
|
+
// LCD/NCD Coverage Determination
|
|
1254
|
+
// ==========================================================================
|
|
1255
|
+
/**
|
|
1256
|
+
* Check LCD/NCD coverage determinations linked to a HCPCS code.
|
|
1257
|
+
*
|
|
1258
|
+
* @param hcpcs - HCPCS/CPT code (4-5 alphanumeric characters)
|
|
1259
|
+
* @param options - Optional filters (active only)
|
|
1260
|
+
* @returns Coverage policies linked to the code
|
|
1261
|
+
*
|
|
1262
|
+
* @example
|
|
1263
|
+
* ```ts
|
|
1264
|
+
* const coverage = await client.claims.checkCoverage("99213");
|
|
1265
|
+
* console.log(`${coverage.data.policies_found} policies found`);
|
|
1266
|
+
* for (const policy of coverage.data.policies) {
|
|
1267
|
+
* console.log(`${policy.display_id}: ${policy.policy_title} [${policy.status}]`);
|
|
1268
|
+
* }
|
|
1269
|
+
* ```
|
|
1270
|
+
*/
|
|
1271
|
+
async checkCoverage(hcpcs, options) {
|
|
1272
|
+
const params = {
|
|
1273
|
+
hcpcs: encodeURIComponent(hcpcs)
|
|
1274
|
+
};
|
|
1275
|
+
if (options?.active !== void 0) {
|
|
1276
|
+
params.active = options.active.toString();
|
|
1277
|
+
}
|
|
1278
|
+
const queryString = this.http.buildSearchQueryString(params);
|
|
1279
|
+
return this.http.get(`/v1/coverage/check${queryString}`);
|
|
1280
|
+
}
|
|
1281
|
+
};
|
|
1282
|
+
|
|
1103
1283
|
// src/client.ts
|
|
1104
1284
|
var Fhirfly = class {
|
|
1105
1285
|
http;
|
|
@@ -1145,6 +1325,11 @@ var Fhirfly = class {
|
|
|
1145
1325
|
* Look up clinical concepts from the SNOMED CT IPS free set (~12K concepts, CC BY 4.0).
|
|
1146
1326
|
*/
|
|
1147
1327
|
snomed;
|
|
1328
|
+
/**
|
|
1329
|
+
* Claims Intelligence endpoints (requires `claims.read` scope).
|
|
1330
|
+
* NCCI PTP validation, MUE limits, PFS/RVU fee schedule, LCD/NCD coverage.
|
|
1331
|
+
*/
|
|
1332
|
+
claims;
|
|
1148
1333
|
/**
|
|
1149
1334
|
* Create a new FHIRfly client.
|
|
1150
1335
|
*
|
|
@@ -1192,6 +1377,7 @@ var Fhirfly = class {
|
|
|
1192
1377
|
this.fdaLabels = new FdaLabelsEndpoint(this.http);
|
|
1193
1378
|
this.connectivity = new ConnectivityEndpoint(this.http);
|
|
1194
1379
|
this.snomed = new SnomedEndpoint(this.http);
|
|
1380
|
+
this.claims = new ClaimsEndpoint(this.http);
|
|
1195
1381
|
}
|
|
1196
1382
|
};
|
|
1197
1383
|
|