mphapi 0.4.3__py3-none-any.whl → 1.9.1__py3-none-any.whl

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.
mphapi/claim.py CHANGED
@@ -60,6 +60,9 @@ class Provider(BaseModel):
60
60
  npi: str
61
61
  """National Provider Identifier of the provider (from NM109, required)"""
62
62
 
63
+ ccn: Optional[str] = None
64
+ """CMS Certification Number (optional)"""
65
+
63
66
  provider_tax_id: Annotated[Optional[str], field_name("providerTaxID")] = None
64
67
  """City of the provider (from N401, highly recommended)"""
65
68
 
@@ -195,8 +198,6 @@ class Service(BaseModel):
195
198
  procedure_code: Optional[str] = None
196
199
  """Procedure code (from SV101_02 / SV202_02)"""
197
200
 
198
- hipps_code: Optional[str] = None
199
-
200
201
  procedure_modifiers: Optional[list[str]] = None
201
202
  """Procedure modifiers (from SV101_03, 4, 5, 6 / SV202_03, 4, 5, 6)"""
202
203
 
@@ -334,12 +335,6 @@ class RateSheetService(BaseModel):
334
335
  procedure_modifiers: Optional[list[str]] = None
335
336
  """Procedure modifiers (from SV101_03, 4, 5, 6 / SV202_03, 4, 5, 6)"""
336
337
 
337
- billed_amount: Optional[float] = None
338
- """Billed charge for the service (from SV102 / SV203)"""
339
-
340
- allowed_amount: Optional[float] = None
341
- """Plan allowed amount for the service (non-EDI)"""
342
-
343
338
 
344
339
  class RateSheet(BaseModel):
345
340
  npi: str
@@ -375,14 +370,5 @@ class RateSheet(BaseModel):
375
370
  drg: Optional[str] = None
376
371
  """Diagnosis Related Group for inpatient services (from HI DR)"""
377
372
 
378
- billed_amount: Optional[float] = None
379
- """Billed amount from provider (from CLM02)"""
380
-
381
- allowed_amount: Optional[float] = None
382
- """Amount allowed by the plan for payment. Both member and plan responsibility (non-EDI)"""
383
-
384
- paid_amount: Optional[float] = None
385
- """Amount paid by the plan for the claim (non-EDI)"""
386
-
387
373
  services: Optional[list[RateSheetService]] = None
388
374
  """One or more services provided to the patient (from LX loop)"""
mphapi/client.py CHANGED
@@ -1,11 +1,11 @@
1
1
  import urllib.parse
2
- from typing import Any, Mapping, Sequence
2
+ from typing import Annotated, Any, Mapping, Optional, Sequence
3
3
 
4
4
  import requests
5
5
  from pydantic import BaseModel, StrictBool, TypeAdapter
6
6
 
7
7
  from .claim import Claim, RateSheet
8
- from .pricing import Pricing
8
+ from .fields import camel_case_model_config, field_name
9
9
  from .response import Response, Responses
10
10
 
11
11
  Header = Mapping[str, str | bytes | None]
@@ -14,49 +14,73 @@ Header = Mapping[str, str | bytes | None]
14
14
  class PriceConfig(BaseModel):
15
15
  """PriceConfig is used to configure the behavior of the pricing API"""
16
16
 
17
- price_zero_billed: StrictBool
17
+ model_config = camel_case_model_config
18
+
19
+ contract_ruleset: Optional[str] = None
20
+ """set to the name of the ruleset to use for contract pricing"""
21
+
22
+ price_zero_billed: Optional[StrictBool] = False
18
23
  """set to true to price claims with zero billed amounts (default is false)"""
19
24
 
20
- is_commercial: StrictBool
21
- """set to true to use commercial code crosswalks"""
25
+ is_commercial: Optional[StrictBool] = False
26
+ """set to true to crosswalk codes from commercial codes Medicare won't pay for to substitute codes they do pay for (e.g. 99201 to G0463)"""
22
27
 
23
- disable_cost_based_reimbursement: StrictBool
24
- """by default, the API will use cost-based reimbursement for MAC priced line-items. This is the best estimate we have for this proprietary pricing"""
28
+ disable_cost_based_reimbursement: Optional[StrictBool] = False
29
+ """set to true to disable cost-based reimbursement for line items paid as a percent of cost"""
25
30
 
26
- use_commercial_synthetic_for_not_allowed: StrictBool
31
+ use_commercial_synthetic_for_not_allowed: Optional[StrictBool] = False
27
32
  """set to true to use a synthetic Medicare price for line-items that are not allowed by Medicare"""
28
33
 
29
- use_drg_from_grouper: StrictBool
34
+ use_drg_from_grouper: Annotated[
35
+ Optional[StrictBool], field_name("useDRGFromGrouper")
36
+ ] = False
30
37
  """set to true to always use the DRG from the inpatient grouper"""
31
38
 
32
- use_best_drg_price: StrictBool
39
+ use_best_drg_price: Annotated[StrictBool, field_name("useBestDRGPrice")]
33
40
  """set to true to use the best DRG price between the price on the claim and the price from the grouper"""
34
41
 
35
- override_threshold: float
42
+ override_threshold: Optional[float] = 0
36
43
  """set to a value greater than 0 to allow the pricer flexibility to override NCCI edits and other overridable errors and return a price"""
37
44
 
38
- include_edits: StrictBool
45
+ include_edits: Optional[StrictBool] = False
39
46
  """set to true to include edit details in the response"""
40
47
 
41
- continue_on_edit_fail: StrictBool
48
+ continue_on_edit_fail: Optional[StrictBool] = False
42
49
  """set to true to continue to price the claim even if there are edit failures"""
43
50
 
44
- continue_on_provider_match_fail: StrictBool
51
+ continue_on_provider_match_fail: Optional[StrictBool] = False
45
52
  """set to true to continue with a average provider for the geographic area if the provider cannot be matched"""
46
53
 
47
- disable_machine_learning_estimates: StrictBool
54
+ disable_machine_learning_estimates: Optional[StrictBool] = False
48
55
  """set to true to disable machine learning estimates (applies to estimates only)"""
49
56
 
57
+ assume_impossible_anesthesia_units_are_minutes: Optional[StrictBool] = False
58
+ """set to true to divide impossible anesthesia units by 15 (max of 96 anesthesia units per day) (default is false)"""
59
+
60
+ fallback_to_max_anesthesia_units_per_day: Optional[StrictBool] = False
61
+ """set to true to fallback to the maximum anesthesia units per day (default is false which will error if there are more than 96 anesthesia units per day)"""
62
+
63
+ allow_partial_results: Optional[StrictBool] = False
64
+ """set to true to return partially repriced claims. This can be useful to get pricing on non-erroring line items, but should be used with caution"""
65
+
66
+
67
+ # It may be a bit jarring to see this import not at the top of the file. This is
68
+ # intentional as `.pricing` depends on `PriceConfig`.
69
+ from .pricing import Pricing # noqa: E402
70
+
50
71
 
51
72
  class Client:
52
73
  url: str
53
74
  headers: Header
54
75
 
55
- def __init__(self, apiKey: str, isTest: bool = False):
56
- if isTest:
57
- self.url = "https://api-test.myprice.health"
76
+ def __init__(self, apiKey: str, isTest: bool = False, url: str | None = None):
77
+ if url is None:
78
+ if isTest:
79
+ self.url = "https://api-test.myprice.health"
80
+ else:
81
+ self.url = "https://api.myprice.health"
58
82
  else:
59
- self.url = "https://api.myprice.health"
83
+ self.url = url
60
84
 
61
85
  self.headers = {"x-api-key": apiKey}
62
86
 
@@ -74,9 +98,7 @@ class Client:
74
98
  headers={**self.headers, **headers},
75
99
  )
76
100
 
77
- def _receive_response[
78
- Model: BaseModel
79
- ](
101
+ def _receive_response[Model: BaseModel](
80
102
  self,
81
103
  path: str,
82
104
  body: BaseModel,
@@ -105,9 +127,7 @@ class Client:
105
127
  .result()
106
128
  )
107
129
 
108
- def _receive_responses[
109
- Model: BaseModel
110
- ](
130
+ def _receive_responses[Model: BaseModel](
111
131
  self,
112
132
  path: str,
113
133
  body: Sequence[BaseModel],
@@ -215,7 +235,7 @@ class Client:
215
235
  if config.use_commercial_synthetic_for_not_allowed:
216
236
  headers["use-commercial-synthetic-for-not-allowed"] = "true"
217
237
 
218
- if config.override_threshold > 0:
238
+ if config.override_threshold is not None and config.override_threshold > 0:
219
239
  headers["override-threshold"] = str(config.override_threshold)
220
240
 
221
241
  if config.include_edits:
mphapi/client_test.py ADDED
@@ -0,0 +1,38 @@
1
+ import json
2
+ import os
3
+
4
+ # This import annoys Pylance for some reason.
5
+ from pytest_snapshot.plugin import Snapshot # type: ignore
6
+
7
+ from . import Claim, Client, PriceConfig
8
+
9
+
10
+ def test_client(snapshot: Snapshot):
11
+ api_key = os.getenv("API_KEY")
12
+ if api_key is None:
13
+ raise EnvironmentError("API_KEY must be set")
14
+
15
+ api_url = os.getenv("API_URL")
16
+
17
+ client = Client(api_key, url=api_url)
18
+
19
+ config = PriceConfig(
20
+ is_commercial=True,
21
+ disable_cost_based_reimbursement=False,
22
+ use_commercial_synthetic_for_not_allowed=True,
23
+ use_drg_from_grouper=False,
24
+ use_best_drg_price=True,
25
+ override_threshold=300,
26
+ include_edits=True,
27
+ )
28
+
29
+ tests = ["hcfa", "inpatient", "outpatient"]
30
+
31
+ for test in tests:
32
+ with open(f"testdata/{test}.json", "r") as f:
33
+ data = json.load(f)
34
+
35
+ claim = Claim.model_validate(data)
36
+ pricing = client.price(config, claim)
37
+
38
+ snapshot.assert_match(pricing.model_dump_json(indent=4), f"{test}.json")
mphapi/pricing.py CHANGED
@@ -3,6 +3,7 @@ from typing import Annotated, Optional
3
3
 
4
4
  from pydantic import BaseModel, Field
5
5
 
6
+ from .client import PriceConfig
6
7
  from .fields import camel_case_model_config, field_name
7
8
  from .response import ResponseError
8
9
 
@@ -143,6 +144,25 @@ class OutpatientPriceDetail(BaseModel):
143
144
  """Wage index used for geographic adjustment"""
144
145
 
145
146
 
147
+ class AllowedRepricingFormula(BaseModel):
148
+ """The formula used to calculate the allowed amount"""
149
+
150
+ medicare_percent: Optional[float] = None
151
+ """Percentage of the Medicare amount used to calculate the allowed amount"""
152
+
153
+ billed_percent: Optional[float] = None
154
+ """Percentage of the billed amount used to calculate the allowed amount"""
155
+
156
+ fee_schedule: Optional[float] = None
157
+ """Fee schedule amount used as the allowed amount"""
158
+
159
+ fixed_amount: Optional[float] = None
160
+ """Fixed amount used as the allowed amount"""
161
+
162
+ per_diem: Optional[float] = None
163
+ """Per diem rate used to calculate the allowed amount"""
164
+
165
+
146
166
  class ProviderDetail(BaseModel):
147
167
  """
148
168
  ProviderDetail contains basic information about the provider and/or locality used for pricing.
@@ -161,6 +181,12 @@ class ProviderDetail(BaseModel):
161
181
  locality: Optional[int] = None
162
182
  """Geographic locality number used for pricing"""
163
183
 
184
+ geographic_cbsa: Annotated[Optional[int], field_name("geographicCBSA")] = None
185
+ """Core-Based Statistical Area (CBSA) number for provider ZIP"""
186
+
187
+ state_cbsa: Annotated[Optional[int], field_name("stateCBSA")] = None
188
+ """State Core-Based Statistical Area (CBSA) number"""
189
+
164
190
  rural_indicator: Optional[RuralIndicator] = None
165
191
  """Indicates whether provider is Rural (R), Super Rural (B), or Urban (blank)"""
166
192
 
@@ -224,9 +250,6 @@ class LineEdits(BaseModel):
224
250
 
225
251
  model_config = camel_case_model_config
226
252
 
227
- denial_or_rejection_text: Optional[str] = None
228
- """The overall explanation for why this line item was denied or rejected by the claim editor"""
229
-
230
253
  procedure_edits: Optional[list[str]] = None
231
254
  """Detailed description of each procedure code edit error (from outpatient editor)"""
232
255
 
@@ -251,9 +274,6 @@ class LineEdits(BaseModel):
251
274
  revenue_edits: Optional[list[str]] = None
252
275
  """Detailed description of each revenue code edit error (from outpatient editor)"""
253
276
 
254
- professional_edits: Optional[list[str]] = None
255
- """Detailed description of each professional claim edit error"""
256
-
257
277
 
258
278
  class PricedService(BaseModel):
259
279
  """PricedService contains the results of a pricing request for a single service line"""
@@ -278,12 +298,18 @@ class PricedService(BaseModel):
278
298
  medicare_repricing_note: Optional[str] = None
279
299
  """Note explaining approach for pricing or reason for error"""
280
300
 
301
+ network_code: Optional[str] = None
302
+ """Code describing the network used for allowed amount pricing"""
303
+
281
304
  allowed_repricing_code: Optional[LineRepricingCode] = None
282
305
  """Explains the methodology used to calculate allowed amount"""
283
306
 
284
307
  allowed_repricing_note: Optional[str] = None
285
308
  """Note explaining approach for pricing or reason for error"""
286
309
 
310
+ allowed_repricing_formula: Optional[AllowedRepricingFormula] = None
311
+ """Formula used to calculate the allowed amount"""
312
+
287
313
  technical_component_amount: Optional[float] = None
288
314
  """Amount Medicare would pay for the technical component"""
289
315
 
@@ -305,6 +331,30 @@ class PricedService(BaseModel):
305
331
  payment_indicator: Optional[str] = None
306
332
  """Text which explains the type of payment for Medicare"""
307
333
 
334
+ discount_formula: Optional[str] = None
335
+ """The multi-procedure discount formula used to calculate the allowed amount (outpatient only)"""
336
+
337
+ line_item_denial_or_rejection_flag: Optional[str] = None
338
+ """Identifies how a line item was denied or rejected and how the rejection can be overridden (outpatient only)"""
339
+
340
+ packaging_flag: Optional[str] = None
341
+ """Indicates if the service is packaged and the reason for packaging (outpatient only)"""
342
+
343
+ payment_adjustment_flag: Optional[str] = None
344
+ """Identifies special adjustments made to the payment (outpatient only)"""
345
+
346
+ payment_adjustment_flag2: Optional[str] = None
347
+ """Identifies special adjustments made to the payment (outpatient only)"""
348
+
349
+ payment_method_flag: Optional[str] = None
350
+ """The method used to calculate the allowed amount (outpatient only)"""
351
+
352
+ composite_adjustment_flag: Optional[str] = None
353
+ """Assists in composite APC determination (outpatient only)"""
354
+
355
+ hcpcs_apc: Annotated[Optional[str], field_name("hcpcsAPC")] = None
356
+ """Ambulatory Payment Classification code of the line item HCPCS (outpatient only)"""
357
+
308
358
  payment_apc: Annotated[Optional[str], field_name("paymentAPC")] = None
309
359
  """Ambulatory Payment Classification"""
310
360
 
@@ -332,6 +382,9 @@ class Pricing(BaseModel):
332
382
  medicare_repricing_note: Optional[str] = None
333
383
  """Note explaining approach for pricing or reason for error"""
334
384
 
385
+ network_code: Optional[str] = None
386
+ """Code describing the network used for allowed amount pricing"""
387
+
335
388
  allowed_repricing_code: Optional[ClaimRepricingCode] = None
336
389
  """Explains the methodology used to calculate allowed amount (CON, RBP, SCA, or IFO)"""
337
390
 
@@ -359,6 +412,9 @@ class Pricing(BaseModel):
359
412
  pricer_result: Optional[str] = None
360
413
  """Pricer return details"""
361
414
 
415
+ price_config: Optional[PriceConfig] = None
416
+ """The configuration used for pricing the claim"""
417
+
362
418
  services: list[PricedService] = Field(min_length=1)
363
419
  """Pricing for each service line on the claim"""
364
420
 
@@ -0,0 +1,75 @@
1
+ {
2
+ "claim_id": "1234",
3
+ "medicare_amount": 124.18,
4
+ "allowed_amount": null,
5
+ "medicare_repricing_code": "MED",
6
+ "medicare_repricing_note": null,
7
+ "network_code": null,
8
+ "allowed_repricing_code": null,
9
+ "allowed_repricing_note": null,
10
+ "medicare_std_dev": null,
11
+ "medicare_source": null,
12
+ "inpatient_price_detail": null,
13
+ "outpatient_price_detail": null,
14
+ "provider_detail": {
15
+ "ccn": null,
16
+ "mac": 4412,
17
+ "locality": 99,
18
+ "geographic_cbsa": 32580,
19
+ "state_cbsa": 45,
20
+ "rural_indicator": null,
21
+ "specialty_type": null,
22
+ "hospital_type": null
23
+ },
24
+ "edit_detail": null,
25
+ "pricer_result": null,
26
+ "price_config": {
27
+ "contract_ruleset": null,
28
+ "price_zero_billed": false,
29
+ "is_commercial": true,
30
+ "disable_cost_based_reimbursement": false,
31
+ "use_commercial_synthetic_for_not_allowed": true,
32
+ "use_drg_from_grouper": false,
33
+ "use_best_drg_price": true,
34
+ "override_threshold": 300.0,
35
+ "include_edits": true,
36
+ "continue_on_edit_fail": false,
37
+ "continue_on_provider_match_fail": false,
38
+ "disable_machine_learning_estimates": false,
39
+ "assume_impossible_anesthesia_units_are_minutes": false,
40
+ "fallback_to_max_anesthesia_units_per_day": false,
41
+ "allow_partial_results": false
42
+ },
43
+ "services": [
44
+ {
45
+ "line_number": "1",
46
+ "provider_detail": null,
47
+ "medicare_amount": 124.18,
48
+ "allowed_amount": null,
49
+ "medicare_repricing_code": "MED",
50
+ "medicare_repricing_note": null,
51
+ "network_code": null,
52
+ "allowed_repricing_code": null,
53
+ "allowed_repricing_note": null,
54
+ "allowed_repricing_formula": null,
55
+ "technical_component_amount": null,
56
+ "professional_component_amount": null,
57
+ "medicare_std_dev": null,
58
+ "medicare_source": "MPFS",
59
+ "pricer_result": "Processed",
60
+ "status_indicator": "A",
61
+ "payment_indicator": null,
62
+ "discount_formula": null,
63
+ "line_item_denial_or_rejection_flag": null,
64
+ "packaging_flag": null,
65
+ "payment_adjustment_flag": null,
66
+ "payment_adjustment_flag2": null,
67
+ "payment_method_flag": null,
68
+ "composite_adjustment_flag": null,
69
+ "hcpcs_apc": null,
70
+ "payment_apc": null,
71
+ "edit_detail": null
72
+ }
73
+ ],
74
+ "edit_error": null
75
+ }