@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 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 (NDC, NPI, LOINC, RxNorm, ICD-10, CVX, MVX, FDA Labels).
3
+ Official FHIRfly SDK for Node.js — typed access to healthcare reference data APIs.
4
4
 
5
5
  [![npm version](https://img.shields.io/npm/v/@fhirfly-io/terminology.svg)](https://www.npmjs.com/package/@fhirfly-io/terminology)
6
6
  [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](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.product_name); // "Lipitor"
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
- - **All FHIRfly APIs**: NDC, NPI, RxNorm, LOINC, ICD-10, CVX, MVX, FDA Labels
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
- - **Designed for both human developers and programmatic agents**
40
+ - **Zero runtime dependencies**
39
41
 
40
- ## API Reference
42
+ ## Authentication
41
43
 
42
- ### Client Configuration
44
+ ### API Key (Simple)
43
45
 
44
46
  ```typescript
45
47
  const client = new Fhirfly({
46
- apiKey: "your-api-key", // Required
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.found) {
74
- console.log(item.data.product_name);
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.long_common_name);
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
- // ICD-10-CM (diagnoses)
111
- const diagnosis = await client.icd10.lookupCm("E11.9");
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
- // ICD-10-PCS (procedures)
114
- const procedure = await client.icd10.lookupPcs("0BJ08ZZ");
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
- // Batch lookups
117
- const cmResults = await client.icd10.lookupCmMany(["E11.9", "I10"]);
118
- const pcsResults = await client.icd10.lookupPcsMany(["0BJ08ZZ"]);
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.short_description);
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
- // By Set ID
139
- const label = await client.fdaLabels.lookup("set-id-here");
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
- // By NDC
142
- const label = await client.fdaLabels.lookupByNdc("0069-0151-01");
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 all available fields |
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/sdk/0.1.0 Node.js/${process.version}`
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.product_name); // "Lipitor"
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.found) {
413
- * console.log(item.data.product_name);
412
+ * if (item.status === "ok") {
413
+ * console.log(item.data.brand_name);
414
414
  * } else {
415
- * console.log(`Not found: ${item.code}`);
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 500)
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 500)
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 500)
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 500)
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 500)
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 500)
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 500)
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