From 5fd1a152fc5c9ddb2981c8db9693ddb2c57fccd7 Mon Sep 17 00:00:00 2001 From: Landon Shumway Date: Thu, 9 Jul 2026 10:34:17 -0700 Subject: [PATCH 01/41] Setting rollback start and end time pk query to same sub day value --- .../handlers/rollback_license_upload.py | 11 +++++-- .../function/test_rollback_license_upload.py | 33 +++++++++++++++++++ 2 files changed, 41 insertions(+), 3 deletions(-) diff --git a/backend/compact-connect/lambdas/python/disaster-recovery/handlers/rollback_license_upload.py b/backend/compact-connect/lambdas/python/disaster-recovery/handlers/rollback_license_upload.py index 6fc26dc4e8..0898017f80 100644 --- a/backend/compact-connect/lambdas/python/disaster-recovery/handlers/rollback_license_upload.py +++ b/backend/compact-connect/lambdas/python/disaster-recovery/handlers/rollback_license_upload.py @@ -429,9 +429,14 @@ def _query_gsi_for_affected_providers( """ affected_provider_ids = set() - # Generate list of year-month strings to query - current_date = start_datetime.replace(day=1) - end_month = end_datetime.replace(day=1) + # Generate list of year-month strings to query. + # NOTE: We must zero out the time-of-day components here, not just the day. Otherwise, if + # start_datetime's time-of-day is later than end_datetime's time-of-day (e.g. start=21:09:55, + # end=12:00:00), the initial current_date <= end_month comparison below can incorrectly evaluate + # to False even though both timestamps fall within the same month, causing this loop to produce + # zero year-months and silently skip the GSI query entirely. + current_date = start_datetime.replace(day=1, hour=0, minute=0, second=0, microsecond=0) + end_month = end_datetime.replace(day=1, hour=0, minute=0, second=0, microsecond=0) year_months = [] while current_date <= end_month: diff --git a/backend/compact-connect/lambdas/python/disaster-recovery/tests/function/test_rollback_license_upload.py b/backend/compact-connect/lambdas/python/disaster-recovery/tests/function/test_rollback_license_upload.py index 85ff565033..f3004caa2c 100644 --- a/backend/compact-connect/lambdas/python/disaster-recovery/tests/function/test_rollback_license_upload.py +++ b/backend/compact-connect/lambdas/python/disaster-recovery/tests/function/test_rollback_license_upload.py @@ -558,6 +558,39 @@ def test_provider_license_record_reset_to_prior_values_when_upload_reverted(self license_updates = provider_records.get_all_license_update_records() self.assertEqual(len(license_updates), 0, 'License update records should be deleted') + def test_provider_found_when_start_time_of_day_is_after_end_time_of_day(self): + """ + Regression test for a bug where _query_gsi_for_affected_providers computed the year-month range + to query by calling datetime.replace(day=1) without also zeroing out the time-of-day components. + + This meant that whenever startDateTime's time-of-day was later than endDateTime's time-of-day + (e.g. start='...T21:09:55Z', end='...T12:00:00Z'), the initial current_date <= end_month comparison + would incorrectly evaluate to False even though both timestamps fell within the same month, causing + the loop to produce zero year-months and silently skip the GSI query entirely (0 providers found). + """ + from handlers.rollback_license_upload import rollback_license_upload + + # start and end are in the same month, but start's time-of-day is later than end's time-of-day + start_datetime = datetime.fromisoformat('2025-10-20T21:09:55+00:00') + end_datetime = datetime.fromisoformat('2025-10-23T07:00:00+00:00') + upload_datetime = datetime.fromisoformat('2025-10-22T10:00:00+00:00') + + # Setup: License was updated during upload, but was first uploaded well before the window + self._when_provider_had_license_updated_from_upload( + upload_datetime=upload_datetime, + license_upload_datetime=start_datetime - timedelta(days=30), + ) + + event = self._generate_test_event() + event['startDateTime'] = start_datetime.isoformat() + event['endDateTime'] = end_datetime.isoformat() + + result = rollback_license_upload(event, Mock()) + + # Assert: Rollback still found and reverted the affected provider + self.assertEqual(result['rollbackStatus'], 'COMPLETE') + self.assertEqual(1, result['providersReverted']) + def test_provider_license_record_reverted_to_earliest_update_previous_values_when_multiple_updates(self): from handlers.rollback_license_upload import rollback_license_upload From 6172cae308c097eb0bebe84935d03aba85e3c185 Mon Sep 17 00:00:00 2001 From: Landon Shumway Date: Thu, 9 Jul 2026 12:50:13 -0700 Subject: [PATCH 02/41] Add previousSSN fields to ingest API spec --- backend/compact-connect/docs/README.md | 1 + .../cc_common/data_model/schema/common.py | 2 + .../data_model/schema/license/api.py | 4 ++ .../data_model/schema/license/ingest.py | 4 ++ .../test_schema/test_license.py | 53 +++++++++++++++++++ .../state_api_stack/v1_api/api_model.py | 7 +++ 6 files changed, 71 insertions(+) diff --git a/backend/compact-connect/docs/README.md b/backend/compact-connect/docs/README.md index 357b3ddd78..23d094f5bd 100644 --- a/backend/compact-connect/docs/README.md +++ b/backend/compact-connect/docs/README.md @@ -62,6 +62,7 @@ leave the field entirely empty. If some of your licenses are missing a required | middleName | Provider's middle name (optional) | String (max 100 chars) | Robert | | npi | National Provider Identifier (optional) | 10-digit number | 1234567890 | | phoneNumber | Provider's phone number (optional) | [ITU-T E.164 format](https://www.itu.int/rec/T-REC-E.164-201011-I/en) (must include country code, no spaces or dashes) | +12025550123 | +| previousSSN | The incorrect Social Security Number previously uploaded for this license (optional). Provide this along with the corrected `ssn` to fix a license record that was uploaded with the wrong SSN: the system moves the license record and any privileges purchased against it over to the provider associated with the corrected SSN. If the corrected license was the only license under the incorrect SSN, that duplicate provider account is removed and the practitioner is emailed to register again. | Format: XXX-XX-XXXX | 123-45-6789 | | suffix | Provider's name suffix (optional) | String (max 100 chars) | Jr. | ** This field is required by compact commission rule, however, to avoid making a breaking change for states that are already integrated, the API does not enforce this rule. States are responsible for enforcing the compact rule themselves. #### Example CSV diff --git a/backend/compact-connect/lambdas/python/common/cc_common/data_model/schema/common.py b/backend/compact-connect/lambdas/python/common/cc_common/data_model/schema/common.py index 6f83b4d7eb..61a51455a2 100644 --- a/backend/compact-connect/lambdas/python/common/cc_common/data_model/schema/common.py +++ b/backend/compact-connect/lambdas/python/common/cc_common/data_model/schema/common.py @@ -319,6 +319,8 @@ class UpdateCategory(CCEnum): # this is specific to privileges that are deactivated due to a state license deactivation LICENSE_DEACTIVATION = 'licenseDeactivation' EMAIL_CHANGE = 'emailChange' + # written when a state corrects a practitioner's SSN and their records are migrated to a new provider id + SSN_CORRECTION = 'ssnCorrection' # NOTE: this value should explicitly be used for license upload updates, not anywhere else # it is referenced in the event that an invalid license upload needs to be reverted. LICENSE_UPLOAD_UPDATE_OTHER = 'other' diff --git a/backend/compact-connect/lambdas/python/common/cc_common/data_model/schema/license/api.py b/backend/compact-connect/lambdas/python/common/cc_common/data_model/schema/license/api.py index 47aa8fd95a..ff6e4cedd3 100644 --- a/backend/compact-connect/lambdas/python/common/cc_common/data_model/schema/license/api.py +++ b/backend/compact-connect/lambdas/python/common/cc_common/data_model/schema/license/api.py @@ -74,6 +74,10 @@ class LicensePostRequestSchema(CCRequestSchema, StrictSchema): """ ssn = SocialSecurityNumber(required=True, allow_none=False) + # If provided, the system will migrate any records associated with this SSN over to the provider + # associated with the `ssn` field, to correct a previously-uploaded incorrect SSN. This value is + # stripped out before the license data leaves the SSN-scoped preprocessing path and is never persisted. + previousSSN = SocialSecurityNumber(required=False, allow_none=False) npi = NationalProviderIdentifier(required=False, allow_none=False) licenseNumber = String(required=False, allow_none=False, validate=Length(1, 100)) licenseStatusName = String(required=False, allow_none=False, validate=Length(1, 100)) diff --git a/backend/compact-connect/lambdas/python/common/cc_common/data_model/schema/license/ingest.py b/backend/compact-connect/lambdas/python/common/cc_common/data_model/schema/license/ingest.py index 94b6f54ad9..2b3f940bda 100644 --- a/backend/compact-connect/lambdas/python/common/cc_common/data_model/schema/license/ingest.py +++ b/backend/compact-connect/lambdas/python/common/cc_common/data_model/schema/license/ingest.py @@ -25,6 +25,10 @@ class LicenseIngestSchema(LicenseCommonSchema): ssnLastFour = String(required=True, allow_none=False, validate=Length(equal=4)) providerId = UUID(required=True, allow_none=False) + # Set by the license preprocessor when the upload carried a previousSSN that resolved to a different + # provider id. Its presence triggers an SSN-correction migration in the ingest handler; it is popped + # before the license record is persisted. + previousProviderId = UUID(required=False, allow_none=False) npi = NationalProviderIdentifier(required=False, allow_none=False) licenseNumber = String(required=False, allow_none=False, validate=Length(1, 100)) # This is used to calculate the actual 'licenseStatus' used by the system in addition diff --git a/backend/compact-connect/lambdas/python/common/tests/unit/test_data_model/test_schema/test_license.py b/backend/compact-connect/lambdas/python/common/tests/unit/test_data_model/test_schema/test_license.py index 64a515d572..1578fe3352 100644 --- a/backend/compact-connect/lambdas/python/common/tests/unit/test_data_model/test_schema/test_license.py +++ b/backend/compact-connect/lambdas/python/common/tests/unit/test_data_model/test_schema/test_license.py @@ -36,6 +36,39 @@ def test_compact_eligible_with_inactive_license_not_allowed(self): with self.assertRaises(ValidationError): LicensePostRequestSchema().load({'compact': 'aslp', 'jurisdiction': 'oh', **license_data}) + def test_validate_post_with_previous_ssn(self): + """previousSSN is an optional field used to trigger an SSN-correction migration.""" + from cc_common.data_model.schema.license.api import LicensePostRequestSchema + + with open('tests/resources/api/license-post.json') as f: + license_data = json.load(f) + license_data['previousSSN'] = '123-12-9876' + + result = LicensePostRequestSchema().load({'compact': 'aslp', 'jurisdiction': 'oh', **license_data}) + + self.assertEqual('123-12-9876', result['previousSSN']) + + def test_validate_post_without_previous_ssn(self): + """previousSSN must be optional - a standard upload omits it.""" + from cc_common.data_model.schema.license.api import LicensePostRequestSchema + + with open('tests/resources/api/license-post.json') as f: + license_data = json.load(f) + + result = LicensePostRequestSchema().load({'compact': 'aslp', 'jurisdiction': 'oh', **license_data}) + + self.assertNotIn('previousSSN', result) + + def test_invalid_previous_ssn_rejected(self): + from cc_common.data_model.schema.license.api import LicensePostRequestSchema + + with open('tests/resources/api/license-post.json') as f: + license_data = json.load(f) + license_data['previousSSN'] = '123129876' + + with self.assertRaises(ValidationError): + LicensePostRequestSchema().load({'compact': 'aslp', 'jurisdiction': 'oh', **license_data}) + class TestLicenseRecordSchema(TstLambdas): def test_serde(self): @@ -302,6 +335,26 @@ def test_compact_eligible_with_inactive_license_not_allowed(self): with self.assertRaises(ValidationError): LicenseIngestSchema().load({'compact': 'aslp', 'jurisdiction': 'oh', **license_record}) + def test_previous_provider_id_survives_load(self): + """The preprocessor forwards previousProviderId for SSN-correction migrations; the ingest schema must + preserve it through its load rather than dropping it as an unknown field. + """ + from cc_common.data_model.schema.license.ingest import LicenseIngestSchema + + with open('tests/resources/api/license-post.json') as f: + license_record = json.load(f) + + license_record['ssnLastFour'] = license_record['ssn'][-4:] + license_record['providerId'] = uuid4() + del license_record['ssn'] + + previous_provider_id = uuid4() + license_record['previousProviderId'] = previous_provider_id + + result = LicenseIngestSchema().load({'compact': 'aslp', 'jurisdiction': 'oh', **license_record}) + + self.assertEqual(previous_provider_id, result['previousProviderId']) + class TestLicenseGeneralResponseSchemaExpirationCheck(TstLambdas): """ diff --git a/backend/compact-connect/stacks/state_api_stack/v1_api/api_model.py b/backend/compact-connect/stacks/state_api_stack/v1_api/api_model.py index d0ca9992f4..29350d7845 100644 --- a/backend/compact-connect/stacks/state_api_stack/v1_api/api_model.py +++ b/backend/compact-connect/stacks/state_api_stack/v1_api/api_model.py @@ -207,6 +207,13 @@ def post_license_model(self) -> Model: description="The provider's social security number", pattern=cc_api.SSN_FORMAT, ), + 'previousSSN': JsonSchema( + type=JsonSchemaType.STRING, + description='The incorrect social security number previously uploaded for this ' + 'license. When provided, the system migrates the records uploaded under it over ' + 'to the provider associated with the corrected ssn.', + pattern=cc_api.SSN_FORMAT, + ), **self._common_license_properties, }, ), From 09de40e4d3d2dab4aad29637ec80cefe7519c938 Mon Sep 17 00:00:00 2001 From: Landon Shumway Date: Thu, 9 Jul 2026 12:50:24 -0700 Subject: [PATCH 03/41] Update ingest stack to reference provider user pool and email service --- .../compact-connect/pipeline/backend_stage.py | 1 + .../compact-connect/stacks/ingest_stack.py | 22 ++++++++++++++++--- 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/backend/compact-connect/pipeline/backend_stage.py b/backend/compact-connect/pipeline/backend_stage.py index 0f479de90d..488ce339d7 100644 --- a/backend/compact-connect/pipeline/backend_stage.py +++ b/backend/compact-connect/pipeline/backend_stage.py @@ -118,6 +118,7 @@ def __init__( environment_name=environment_name, standard_tags=standard_tags, persistent_stack=self.persistent_stack, + provider_users_stack=self.provider_users_stack, ) self.api_lambda_stack = ApiLambdaStack( diff --git a/backend/compact-connect/stacks/ingest_stack.py b/backend/compact-connect/stacks/ingest_stack.py index 5391597b19..9928978ae4 100644 --- a/backend/compact-connect/stacks/ingest_stack.py +++ b/backend/compact-connect/stacks/ingest_stack.py @@ -15,6 +15,7 @@ from constructs import Construct from stacks import persistent_stack as ps +from stacks.provider_users import ProviderUsersStack class IngestStack(AppStack): @@ -25,14 +26,20 @@ def __init__( *, environment_name: str, persistent_stack: ps.PersistentStack, + provider_users_stack: ProviderUsersStack, **kwargs, ): super().__init__(scope, construct_id, environment_name=environment_name, **kwargs) # We explicitly get the event bus arn from parameter store, to avoid issues with cross stack updates data_event_bus = SSMParameterUtility.load_data_event_bus_from_ssm_parameter(self) - self._add_v1_ingest_chain(persistent_stack, data_event_bus) + self._add_v1_ingest_chain(persistent_stack, provider_users_stack, data_event_bus) - def _add_v1_ingest_chain(self, persistent_stack: ps.PersistentStack, data_event_bus: EventBus): + def _add_v1_ingest_chain( + self, + persistent_stack: ps.PersistentStack, + provider_users_stack: ProviderUsersStack, + data_event_bus: EventBus, + ): ingest_handler = PythonFunction( self, 'V1IngestHandler', @@ -44,12 +51,20 @@ def _add_v1_ingest_chain(self, persistent_stack: ps.PersistentStack, data_event_ environment={ 'EVENT_BUS_NAME': data_event_bus.event_bus_name, 'PROVIDER_TABLE_NAME': persistent_stack.provider_table.table_name, + 'PROVIDER_USER_POOL_ID': provider_users_stack.provider_users.user_pool_id, + 'EMAIL_NOTIFICATION_SERVICE_LAMBDA_NAME': ( + persistent_stack.email_notification_service_lambda.function_name + ), **self.common_env_vars, }, alarm_topic=persistent_stack.alarm_topic, ) persistent_stack.provider_table.grant_read_write_data(ingest_handler) data_event_bus.grant_put_events_to(ingest_handler) + # The SSN-correction migration deletes the old provider's Cognito account on a full teardown and + # notifies the practitioner to re-register + provider_users_stack.provider_users.grant(ingest_handler, 'cognito-idp:AdminDeleteUser') + persistent_stack.email_notification_service_lambda.grant_invoke(ingest_handler) NagSuppressions.add_resource_suppressions_by_path( Stack.of(ingest_handler.role), @@ -59,7 +74,8 @@ def _add_v1_ingest_chain(self, persistent_stack: ps.PersistentStack, data_event_ 'id': 'AwsSolutions-IAM5', 'reason': """ This policy contains wild-carded actions and resources but they are scoped to the - specific actions, KMS key and Table that this lambda specifically needs access to. + specific actions, KMS key, Table, user pool, and lambda that this handler specifically + needs access to. """, }, ], From 1a85a2f1d132587dc8c6a7023a2923b6cae74f42 Mon Sep 17 00:00:00 2001 From: Landon Shumway Date: Thu, 9 Jul 2026 12:51:35 -0700 Subject: [PATCH 04/41] Update bulk upload to check for flag and new field --- .../common/cc_common/feature_flag_enum.py | 1 + .../lambdas/python/common/cc_common/utils.py | 16 ++++++ .../provider-data-v1/handlers/bulk_upload.py | 9 ++++ .../test_handlers/test_bulk_upload.py | 49 +++++++++++++++++++ 4 files changed, 75 insertions(+) diff --git a/backend/compact-connect/lambdas/python/common/cc_common/feature_flag_enum.py b/backend/compact-connect/lambdas/python/common/cc_common/feature_flag_enum.py index e6d2aa5b93..630d81d49d 100644 --- a/backend/compact-connect/lambdas/python/common/cc_common/feature_flag_enum.py +++ b/backend/compact-connect/lambdas/python/common/cc_common/feature_flag_enum.py @@ -13,3 +13,4 @@ class FeatureFlagEnum(StrEnum): # runtime flags DUPLICATE_SSN_UPLOAD_CHECK_FLAG = 'duplicate-ssn-upload-check-flag' HOME_JURISDICTION_CHANGE_NOTIFICATION_FLAG = 'home-jurisdiction-change-notification-flag' + LICENSE_SSN_CORRECTION_MIGRATION_FLAG = 'license-ssn-correction-migration-flag' diff --git a/backend/compact-connect/lambdas/python/common/cc_common/utils.py b/backend/compact-connect/lambdas/python/common/cc_common/utils.py index 516d5704dd..a2cbc6ebef 100644 --- a/backend/compact-connect/lambdas/python/common/cc_common/utils.py +++ b/backend/compact-connect/lambdas/python/common/cc_common/utils.py @@ -861,6 +861,22 @@ def sanitize_provider_data_based_on_caller_scopes(compact: str, provider: dict, return provider_read_general_schema.load(provider) +def strip_previous_ssn_if_migration_disabled(validated_license: dict, *, migration_flag_enabled: bool) -> dict: + """ + Remove the previousSSN field from a validated license when the SSN-correction migration feature is disabled. + + The presence of previousSSN downstream triggers a record migration, so the field must not leave the intake + layer (POST licenses / bulk upload) unless the feature flag is enabled. + + :param dict validated_license: A license loaded by LicensePostRequestSchema + :param bool migration_flag_enabled: Cached value of LICENSE_SSN_CORRECTION_MIGRATION_FLAG + :return: The validated license, without previousSSN if the feature is disabled + """ + if not migration_flag_enabled and validated_license.pop('previousSSN', None) is not None: + logger.info('previousSSN provided but the SSN-correction migration feature is disabled; ignoring the field') + return validated_license + + def send_licenses_to_preprocessing_queue(licenses_data: list[dict], event_time: str) -> list[str]: """ Send license data to the preprocessing queue in batches. diff --git a/backend/compact-connect/lambdas/python/provider-data-v1/handlers/bulk_upload.py b/backend/compact-connect/lambdas/python/provider-data-v1/handlers/bulk_upload.py index e12a0389b7..9ba87973e6 100644 --- a/backend/compact-connect/lambdas/python/provider-data-v1/handlers/bulk_upload.py +++ b/backend/compact-connect/lambdas/python/provider-data-v1/handlers/bulk_upload.py @@ -21,6 +21,7 @@ api_handler, authorize_compact_jurisdiction, send_licenses_to_preprocessing_queue, + strip_previous_ssn_if_migration_disabled, ) from license_csv_reader import LicenseCSVReader from marshmallow import ValidationError @@ -29,6 +30,10 @@ duplicate_ssn_check_flag_enabled = is_feature_enabled( FeatureFlagEnum.DUPLICATE_SSN_UPLOAD_CHECK_FLAG, fail_default=True ) +# this flag gates a record migration that deletes records, so we fail closed if the flag cannot be checked +ssn_correction_migration_flag_enabled = is_feature_enabled( + FeatureFlagEnum.LICENSE_SSN_CORRECTION_MIGRATION_FLAG, fail_default=False +) @api_handler @@ -181,6 +186,10 @@ def process_bulk_upload_file( # This will be raised, if `raw_license` includes compact and/or jurisdiction fields logger.error('License contains unsupported fields', fields=list(raw_license.keys()), exc_info=e) raise ValidationError('License contains unsupported fields') from e + validated_license = strip_previous_ssn_if_migration_disabled( + validated_license, + migration_flag_enabled=ssn_correction_migration_flag_enabled, + ) current_batch.append(schema.dump(validated_license)) # When batch is full, send to preprocessing queue diff --git a/backend/compact-connect/lambdas/python/provider-data-v1/tests/function/test_handlers/test_bulk_upload.py b/backend/compact-connect/lambdas/python/provider-data-v1/tests/function/test_handlers/test_bulk_upload.py index 8fed13c531..7c5b668843 100644 --- a/backend/compact-connect/lambdas/python/provider-data-v1/tests/function/test_handlers/test_bulk_upload.py +++ b/backend/compact-connect/lambdas/python/provider-data-v1/tests/function/test_handlers/test_bulk_upload.py @@ -115,6 +115,55 @@ def test_bulk_upload_processor_puts_messages_on_preprocessing_queue(self): message_data = json.loads(message.body) self.assertEqual(csv_licenses[message_data['licenseNumber']], message_data) + def _process_csv_with_previous_ssn(self) -> list: + """Upload a single-row CSV that carries a previousSSN column and return the resulting queue messages.""" + from handlers.bulk_upload import parse_bulk_upload_file + + csv_content = ( + 'ssn,previousSSN,licenseNumber,givenName,familyName,dateOfBirth,dateOfIssuance' + ',dateOfExpiration,licenseStatus,compactEligibility,homeAddressStreet1' + ',homeAddressCity,homeAddressState,homeAddressPostalCode,licenseType\n' + '123-45-6789,123-45-9876,LICENSE123,John,Doe,1990-01-01,2020-01-01' + ',2030-01-01,active,eligible,123 Main St' + ',Columbus,OH,43215,audiologist' + ) + + object_key = f'aslp/oh/{uuid4().hex}' + self._bucket.put_object(Key=object_key, Body=csv_content) + + with open('../common/tests/resources/put-event.json') as f: + event = json.load(f) + + event['Records'][0]['s3']['bucket'] = { + 'name': self._bucket.name, + 'arn': f'arn:aws:s3:::{self._bucket.name}', + 'ownerIdentity': {'principalId': 'ASDFG123'}, + } + event['Records'][0]['s3']['object']['key'] = object_key + + parse_bulk_upload_file(event, self.mock_context) + + return self._license_preprocessing_queue.receive_messages(MaxNumberOfMessages=10) + + def test_bulk_upload_passes_previous_ssn_through_when_flag_enabled(self): + # patch the module-level cached flag value directly, so this test is independent of module import order + with patch('handlers.bulk_upload.ssn_correction_migration_flag_enabled', True): + messages = self._process_csv_with_previous_ssn() + + self.assertEqual(1, len(messages)) + message_data = json.loads(messages[0].body) + self.assertEqual('123-45-9876', message_data['previousSSN']) + + def test_bulk_upload_strips_previous_ssn_when_flag_disabled(self): + with patch('handlers.bulk_upload.ssn_correction_migration_flag_enabled', False): + messages = self._process_csv_with_previous_ssn() + + self.assertEqual(1, len(messages)) + message_data = json.loads(messages[0].body) + self.assertNotIn('previousSSN', message_data) + # the rest of the license data must be unaffected + self.assertEqual('123-45-6789', message_data['ssn']) + def test_bulk_upload_strips_whitespace_from_string_fields(self): """Test that whitespace is stripped from all string fields in CSV data.""" from handlers.bulk_upload import parse_bulk_upload_file From 7e61065e3e92e1baf978eee0218a81272c5eb269 Mon Sep 17 00:00:00 2001 From: Landon Shumway Date: Thu, 9 Jul 2026 12:57:03 -0700 Subject: [PATCH 05/41] Update license API endpoint to check for flag and new field --- .../provider-data-v1/handlers/licenses.py | 20 +++++++-- .../function/test_handlers/test_licenses.py | 41 +++++++++++++++++++ 2 files changed, 58 insertions(+), 3 deletions(-) diff --git a/backend/compact-connect/lambdas/python/provider-data-v1/handlers/licenses.py b/backend/compact-connect/lambdas/python/provider-data-v1/handlers/licenses.py index 59d8bd64f7..53999dfbf1 100644 --- a/backend/compact-connect/lambdas/python/provider-data-v1/handlers/licenses.py +++ b/backend/compact-connect/lambdas/python/provider-data-v1/handlers/licenses.py @@ -5,18 +5,27 @@ from cc_common.data_model.schema.license.api import LicensePostRequestSchema from cc_common.exceptions import CCInternalException, CCInvalidRequestCustomResponseException, CCInvalidRequestException from cc_common.signature_auth import optional_signature_auth -from cc_common.utils import api_handler, authorize_compact_jurisdiction, send_licenses_to_preprocessing_queue +from cc_common.utils import ( + api_handler, + authorize_compact_jurisdiction, + send_licenses_to_preprocessing_queue, + strip_previous_ssn_if_migration_disabled, +) from marshmallow import ValidationError schema = LicensePostRequestSchema() -# initialize flag outside of handler so the flag is cached for the lifecycle of the execution environment +# initialize flags outside of handler so the flags are cached for the lifecycle of the execution environment from cc_common.feature_flag_client import FeatureFlagEnum, is_feature_enabled # noqa: E402 # low risk flag, so we default to enabled if failure detected duplicate_ssn_check_flag_enabled = is_feature_enabled( FeatureFlagEnum.DUPLICATE_SSN_UPLOAD_CHECK_FLAG, fail_default=True ) +# this flag gates a record migration that deletes records, so we fail closed if the flag cannot be checked +ssn_correction_migration_flag_enabled = is_feature_enabled( + FeatureFlagEnum.LICENSE_SSN_CORRECTION_MIGRATION_FLAG, fail_default=False +) @api_handler @@ -53,7 +62,12 @@ def post_licenses(event: dict, context: LambdaContext): # noqa: ARG001 unused-a else: license_entry = {**license_record, 'compact': compact, 'jurisdiction': jurisdiction} try: - licenses.append(schema.load(license_entry)) + licenses.append( + strip_previous_ssn_if_migration_disabled( + schema.load(license_entry), + migration_flag_enabled=ssn_correction_migration_flag_enabled, + ) + ) except ValidationError as e: logger.debug( 'invalid license record detected', diff --git a/backend/compact-connect/lambdas/python/provider-data-v1/tests/function/test_handlers/test_licenses.py b/backend/compact-connect/lambdas/python/provider-data-v1/tests/function/test_handlers/test_licenses.py index e6ce09d7d0..0aeefd78f5 100644 --- a/backend/compact-connect/lambdas/python/provider-data-v1/tests/function/test_handlers/test_licenses.py +++ b/backend/compact-connect/lambdas/python/provider-data-v1/tests/function/test_handlers/test_licenses.py @@ -94,6 +94,47 @@ def test_post_licenses_puts_expected_messages_on_the_queue(self): expected_message['eventTime'] = '2024-11-08T23:59:59+00:00' self.assertEqual(expected_message, json.loads(queue_messages[0].body)) + def _post_license_with_previous_ssn(self) -> list: + """POST a single license carrying a previousSSN and return the resulting queue messages.""" + from handlers.licenses import post_licenses + + with open('../common/tests/resources/api-event.json') as f: + event = json.load(f) + + # The user has write permission for aslp/oh + event['requestContext']['authorizer']['claims']['scope'] = 'openid email aslp/readGeneral oh/aslp.write' + event['pathParameters'] = {'compact': 'aslp', 'jurisdiction': 'oh'} + with open('../common/tests/resources/api/license-post.json') as f: + license_data = json.load(f) + license_data['previousSSN'] = '123-12-9876' + event['body'] = json.dumps([license_data]) + + event = self._create_signed_event(event) + + resp = post_licenses(event, self.mock_context) + self.assertEqual(200, resp['statusCode']) + + return self._license_preprocessing_queue.receive_messages(MaxNumberOfMessages=10) + + def test_post_licenses_passes_previous_ssn_through_when_flag_enabled(self): + # patch the module-level cached flag value directly, so this test is independent of module import order + with patch('handlers.licenses.ssn_correction_migration_flag_enabled', True): + queue_messages = self._post_license_with_previous_ssn() + + self.assertEqual(1, len(queue_messages)) + message = json.loads(queue_messages[0].body) + self.assertEqual('123-12-9876', message['previousSSN']) + + def test_post_licenses_strips_previous_ssn_when_flag_disabled(self): + with patch('handlers.licenses.ssn_correction_migration_flag_enabled', False): + queue_messages = self._post_license_with_previous_ssn() + + self.assertEqual(1, len(queue_messages)) + message = json.loads(queue_messages[0].body) + self.assertNotIn('previousSSN', message) + # the rest of the license data must be unaffected + self.assertEqual('123-12-1234', message['ssn']) + def test_post_licenses_does_not_let_request_body_override_path_parameters(self): from handlers.licenses import post_licenses From f433081f976f28333cc06022330912d98419eb8d Mon Sep 17 00:00:00 2001 From: Landon Shumway Date: Thu, 9 Jul 2026 13:30:33 -0700 Subject: [PATCH 06/41] Perform migration of records from old SSN to new SSN --- .../cc_common/data_model/data_client.py | 325 ++++++++++++++++++ .../data_model/provider_record_util.py | 57 +++ .../common/cc_common/email_service_client.py | 25 ++ .../tests/unit/test_email_service_client.py | 25 ++ .../provider-data-v1/handlers/ingest.py | 98 +++++- .../function/test_handlers/test_ingest.py | 215 +++++++++++- .../tests/unit/test_handlers/test_ingest.py | 59 ++++ 7 files changed, 800 insertions(+), 4 deletions(-) diff --git a/backend/compact-connect/lambdas/python/common/cc_common/data_model/data_client.py b/backend/compact-connect/lambdas/python/common/cc_common/data_model/data_client.py index 367707b9b4..0324b3da5d 100644 --- a/backend/compact-connect/lambdas/python/common/cc_common/data_model/data_client.py +++ b/backend/compact-connect/lambdas/python/common/cc_common/data_model/data_client.py @@ -1,4 +1,5 @@ import time +from dataclasses import dataclass from datetime import date, datetime from datetime import time as dtime from urllib.parse import quote @@ -53,6 +54,24 @@ from cc_common.utils import logger_inject_kwargs +@dataclass +class SsnCorrectionMigrationResult: + """ + Outcome of an SSN-correction migration. + + :param migration_performed: False when there was nothing to migrate (spurious previousSSN or a replay of an + already-completed migration) + :param full_teardown: True when the corrected license was the old provider's only license, so the old + provider was deleted entirely. The caller must delete the old Cognito user and notify the practitioner. + :param old_provider_registered_email: The old provider's registered email address, present only on a full + teardown of a registered provider + """ + + migration_performed: bool + full_teardown: bool = False + old_provider_registered_email: str | None = None + + class DataClient: """Client interface for license data dynamodb queries""" @@ -2835,6 +2854,312 @@ def update_provider_home_state_jurisdiction( ) raise CCInternalException('Failed to update provider home state jurisdiction') from e + @logger_inject_kwargs(logger, 'compact', 'previous_provider_id', 'new_provider_id', 'jurisdiction') + def migrate_provider_for_ssn_correction( + self, + *, + compact: str, + previous_provider_id: str, + new_provider_id: str, + jurisdiction: str, + license_type: str, + new_ssn_last_four: str, + ) -> 'SsnCorrectionMigrationResult': + """ + Migrate a license (and its dependent records) from one provider id to another after a state corrected + the SSN on a license upload. + + The migration is scoped to the single (jurisdiction, license type) license of the corrected upload row. + That license, the privileges purchased against it, and their adverse action / investigation / update + history records are always moved to the new provider id. Person-level records (military affiliations, + provider update history) are never carried over: the practitioner re-registers under the corrected + provider id and re-uploads any documents. What happens to the rest of the old provider depends on + whether the corrected license was its only license record: + + - Full teardown (sole license): the old provider's entire partition, including its person-level records + and top-level provider record, is deleted. The caller is responsible for deleting the old Cognito + user and notifying the practitioner. + - Partial (other licenses remain): the old provider keeps its person-level records and its top-level + record is repopulated from its remaining licenses. The old provider id remains a valid (partial) + practitioner until its remaining licenses are also corrected. + + Concurrency: the write against the old top-level provider record is conditioned on the dateOfUpdate + read at the start of the migration and executed in the first transaction batch. A concurrent migration + for the same old provider will fail that condition before writing anything, and its SQS retry re-reads + current state. The targeted license's delete is executed in the last batch so a crash mid-migration + leaves the license in place for the replay's idempotency guard to find; all other writes are idempotent. + + Records already under the new provider id are never modified or deleted; a top-level provider record is + created for the new provider only when it does not already have one. + + :param compact: The compact name + :param previous_provider_id: Provider id the incorrect SSN resolved to + :param new_provider_id: Provider id the corrected SSN resolved to + :param jurisdiction: Jurisdiction of the corrected license upload + :param license_type: License type of the corrected license upload + :param new_ssn_last_four: Last four digits of the corrected SSN + :return: SsnCorrectionMigrationResult describing what the migration did + """ + try: + old_provider_records = self.get_provider_user_records( + compact=compact, + provider_id=previous_provider_id, + consistent_read=True, + include_update_tier=UpdateTierEnum.TIER_THREE, + ) + except CCNotFoundException: + # The previousSSN resolved to a provider id with no records (e.g. it was never actually uploaded) + logger.info('Previous provider id has no records; nothing to migrate') + return SsnCorrectionMigrationResult(migration_performed=False) + + # Idempotency guard: if the targeted license is not on the old provider, it was either never there or + # a previous run already migrated it + records_to_move = old_provider_records.get_records_associated_with_license(jurisdiction, license_type) + if not records_to_move: + logger.info('Previous provider has no license matching the corrected upload; nothing to migrate') + return SsnCorrectionMigrationResult(migration_performed=False) + + try: + old_provider_data = old_provider_records.get_provider_record() + except CCInternalException: + # The top-level provider record was already deleted by a partially-completed teardown that is now + # being replayed; continue the migration without the concurrency fence + logger.warning('Old provider record not found; continuing replay of a partially-completed migration') + old_provider_data = None + + # The corrected license was the old provider's only license: tear the old provider down entirely + full_teardown = len(old_provider_records.get_license_records()) == 1 + + target_license = next(record for record in records_to_move if record.type == ProviderRecordType.LICENSE) + target_license_key = self._provider_record_key(target_license) + + transaction_items = [] + + # 1. The concurrency fence: a conditioned write against the old top-level provider record, executed in + # the first batch so a competing migration fails before writing anything + if old_provider_data is not None: + transaction_items.append( + self._build_conditioned_old_provider_transaction_item( + old_provider_data=old_provider_data, + old_provider_records=old_provider_records, + full_teardown=full_teardown, + jurisdiction=jurisdiction, + license_type=license_type, + ) + ) + + # 2. Re-keyed puts for every migrated record. The targeted license also picks up the corrected + # ssnLastFour so the new partition is internally consistent + rekeyed_target_license = None + rekeyed_privileges = [] + for record in records_to_move: + extra_updates = {'ssnLastFour': new_ssn_last_four} if record is target_license else None + rekeyed_record = self._rekey_record_to_provider(record, new_provider_id, extra_updates) + if record is target_license: + rekeyed_target_license = rekeyed_record + elif record.type == ProviderRecordType.PRIVILEGE: + rekeyed_privileges.append(rekeyed_record) + transaction_items.append(self._build_put_transaction_item(rekeyed_record)) + + # 3. Deletes for the moved records, except the targeted license (deleted last) and the top-level + # provider record (handled by the fence above). On a full teardown the person-level records + # (military affiliations, provider update history) are deleted with the partition + records_to_delete = list(records_to_move) + if full_teardown: + records_to_delete.extend(old_provider_records.get_person_level_records()) + for record in records_to_delete: + if record is target_license: + continue + transaction_items.append(self._build_delete_transaction_item(self._provider_record_key(record))) + + # 4. The ssnCorrection provider update record, written on the new provider. Skipped on a replay that + # lost the old provider record: the run that deleted it already wrote this record + if old_provider_data is not None: + ssn_correction_update = ProviderUpdateData.create_new( + { + 'type': ProviderRecordType.PROVIDER_UPDATE, + 'updateType': UpdateCategory.SSN_CORRECTION, + 'providerId': new_provider_id, + 'compact': compact, + 'previous': old_provider_data.to_dict(), + 'createDate': config.current_standard_datetime, + 'updatedValues': {'ssnLastFour': new_ssn_last_four}, + } + ) + transaction_items.append(self._build_put_transaction_item(ssn_correction_update)) + + # 5. Create a top-level provider record for the new provider only if it does not already have one; a + # pre-existing record is never modified + new_provider_record = self._build_new_provider_record_if_absent( + compact=compact, + new_provider_id=new_provider_id, + rekeyed_target_license=rekeyed_target_license, + rekeyed_privileges=rekeyed_privileges, + ) + if new_provider_record is not None: + transaction_items.append(self._build_put_transaction_item(new_provider_record)) + + # 6. The targeted license delete goes last: until it commits, a replay of this migration re-runs in full + transaction_items.append(self._build_delete_transaction_item(target_license_key)) + + self._execute_batched_transactions(transaction_items) + + return SsnCorrectionMigrationResult( + migration_performed=True, + full_teardown=full_teardown, + old_provider_registered_email=( + old_provider_data.to_dict().get('compactConnectRegisteredEmailAddress') + if full_teardown and old_provider_data is not None + else None + ), + ) + + @staticmethod + def _provider_record_key(record: CCDataClass) -> dict[str, str]: + """Get the current pk/sk of a record, as regenerated by its schema.""" + serialized = record.serialize_to_database_record() + return {'pk': serialized['pk'], 'sk': serialized['sk']} + + def _build_put_transaction_item(self, record: CCDataClass) -> dict: + return { + 'Put': { + 'TableName': self.config.provider_table_name, + 'Item': TypeSerializer().serialize(record.serialize_to_database_record())['M'], + } + } + + def _build_delete_transaction_item(self, record_key: dict[str, str]) -> dict: + return { + 'Delete': { + 'TableName': self.config.provider_table_name, + 'Key': {'pk': {'S': record_key['pk']}, 'sk': {'S': record_key['sk']}}, + } + } + + @staticmethod + def _rekey_record_to_provider( + record: CCDataClass, new_provider_id: str, extra_updates: dict | None = None + ) -> CCDataClass: + """ + Build a copy of a record re-keyed under a new provider id. + + Because the provider id appears only in the pk (and in derived GSI keys), re-serializing the record + with the new provider id regenerates all of its database keys. Update records embed a snapshot of the + record they describe, so any providerId inside 'previous' is re-keyed as well. + """ + record_data = record.to_dict() + record_data['providerId'] = new_provider_id + if isinstance(record_data.get('previous'), dict) and 'providerId' in record_data['previous']: + record_data['previous']['providerId'] = new_provider_id + if extra_updates: + record_data.update(extra_updates) + return type(record).create_new(record_data) + + def _build_conditioned_old_provider_transaction_item( + self, + *, + old_provider_data: ProviderData, + old_provider_records: ProviderUserRecords, + full_teardown: bool, + jurisdiction: str, + license_type: str, + ) -> dict: + """ + Build the write against the old top-level provider record: a delete on full teardown, or a repopulation + from the remaining licenses on a partial migration. Either way the write is conditioned on the + dateOfUpdate read at the start of the migration, so concurrent migrations of the same old provider + serialize via SQS retry instead of both reading the same stale state. + """ + condition = { + 'ConditionExpression': 'attribute_exists(pk) AND dateOfUpdate = :dateOfUpdate', + 'ExpressionAttributeValues': {':dateOfUpdate': {'S': old_provider_data.dateOfUpdate.isoformat()}}, + } + if full_teardown: + old_provider_key = self._provider_record_key(old_provider_data) + return { + 'Delete': { + 'TableName': self.config.provider_table_name, + 'Key': {'pk': {'S': old_provider_key['pk']}, 'sk': {'S': old_provider_key['sk']}}, + **condition, + } + } + + repopulated_old_provider = self._repopulate_provider_record_from_remaining_records( + old_provider_data=old_provider_data, + old_provider_records=old_provider_records, + migrated_jurisdiction=jurisdiction, + migrated_license_type=license_type, + ) + return { + 'Put': { + 'TableName': self.config.provider_table_name, + 'Item': TypeSerializer().serialize(repopulated_old_provider.serialize_to_database_record())['M'], + **condition, + } + } + + @staticmethod + def _repopulate_provider_record_from_remaining_records( + *, + old_provider_data: ProviderData, + old_provider_records: ProviderUserRecords, + migrated_jurisdiction: str, + migrated_license_type: str, + ) -> ProviderData: + """Rebuild the old top-level provider record from the licenses/privileges that are not being migrated.""" + remaining_licenses = old_provider_records.get_license_records( + filter_condition=lambda license_data: ( + not ( + license_data.jurisdiction == migrated_jurisdiction + and license_data.licenseType == migrated_license_type + ) + ) + ) + remaining_privileges = old_provider_records.get_privilege_records( + filter_condition=lambda privilege_data: ( + not ( + privilege_data.licenseJurisdiction == migrated_jurisdiction + and privilege_data.licenseType == migrated_license_type + ) + ) + ) + + best_remaining_license = ProviderRecordUtility.find_best_license( + [license_data.to_dict() for license_data in remaining_licenses], + old_provider_data.to_dict().get('currentHomeJurisdiction'), + ) + # Strip privilegeJurisdictions from the current record so the utility only carries jurisdictions + # over from the privileges that actually remain + current_provider_dict = old_provider_data.to_dict() + current_provider_dict.pop('privilegeJurisdictions', None) + return ProviderRecordUtility.populate_provider_record( + current_provider_record=ProviderData.create_new(current_provider_dict), + license_record=best_remaining_license, + privilege_records=[privilege_data.to_dict() for privilege_data in remaining_privileges], + ) + + def _build_new_provider_record_if_absent( + self, + *, + compact: str, + new_provider_id: str, + rekeyed_target_license: LicenseData, + rekeyed_privileges: list[PrivilegeData], + ) -> ProviderData | None: + """ + Build a top-level provider record for the new provider from the migrated license/privileges, or return + None if the new provider already has one (a pre-existing record is never modified). + """ + try: + self.get_provider_top_level_record(compact=compact, provider_id=new_provider_id) + return None + except CCNotFoundException: + return ProviderRecordUtility.populate_provider_record( + current_provider_record=None, + license_record=rekeyed_target_license.to_dict(), + privilege_records=[privilege_data.to_dict() for privilege_data in rekeyed_privileges], + ) + def _execute_batched_transactions(self, transaction_items: list[dict]) -> None: """ Execute transaction items in batches of 100 (DynamoDB limit). diff --git a/backend/compact-connect/lambdas/python/common/cc_common/data_model/provider_record_util.py b/backend/compact-connect/lambdas/python/common/cc_common/data_model/provider_record_util.py index 9a7b1461ec..a4d3783cc5 100644 --- a/backend/compact-connect/lambdas/python/common/cc_common/data_model/provider_record_util.py +++ b/backend/compact-connect/lambdas/python/common/cc_common/data_model/provider_record_util.py @@ -13,6 +13,7 @@ from cc_common.data_model.schema.common import ( ActiveInactiveStatus, AdverseActionAgainstEnum, + CCDataClass, CompactEligibilityStatus, HomeJurisdictionChangeStatusEnum, PrivilegeEncumberedStatusEnum, @@ -904,6 +905,62 @@ def get_update_records_for_privilege( and (filter_condition is None or filter_condition(record)) ] + def get_records_associated_with_license(self, jurisdiction: str, license_type: str) -> list[CCDataClass]: + """ + Get the license record for the given jurisdiction/license type along with every record that depends on it: + the privileges whose home license it is, and the adverse action, investigation, and update history + records for both the license and those privileges. + + Returns an empty list if this provider has no license for the given jurisdiction/license type. + + :param jurisdiction: The jurisdiction of the license + :param license_type: The license type (full name, not abbreviation) + :return: The license record and all of its dependent records + """ + license_record = next( + ( + record + for record in self._license_records + if record.jurisdiction == jurisdiction and record.licenseType == license_type + ), + None, + ) + if license_record is None: + return [] + + license_type_abbreviation = license_record.licenseTypeAbbreviation + + associated_records: list[CCDataClass] = [license_record] + associated_records.extend(self.get_adverse_action_records_for_license(jurisdiction, license_type_abbreviation)) + associated_records.extend( + self.get_investigation_records_for_license(jurisdiction, license_type_abbreviation, include_closed=True) + ) + associated_records.extend(self.get_update_records_for_license(jurisdiction, license_type)) + + privileges = self.get_privileges_associated_with_license(jurisdiction, license_type_abbreviation) + associated_records.extend(privileges) + for privilege in privileges: + associated_records.extend( + self.get_adverse_action_records_for_privilege(privilege.jurisdiction, license_type_abbreviation) + ) + associated_records.extend( + self.get_investigation_records_for_privilege( + privilege.jurisdiction, license_type_abbreviation, include_closed=True + ) + ) + associated_records.extend( + self.get_update_records_for_privilege(privilege.jurisdiction, privilege.licenseType) + ) + + return associated_records + + def get_person_level_records(self) -> list[CCDataClass]: + """ + Get the records tied to the person rather than to any particular license: military affiliation records + and provider update history records. + """ + return [*self._military_affiliation_records, *self._provider_update_records] + def generate_api_response_object(self) -> dict: """ Assemble a list of provider records into a single object used by the provider details api. diff --git a/backend/compact-connect/lambdas/python/common/cc_common/email_service_client.py b/backend/compact-connect/lambdas/python/common/cc_common/email_service_client.py index ecd8ac644d..e592a80138 100644 --- a/backend/compact-connect/lambdas/python/common/cc_common/email_service_client.py +++ b/backend/compact-connect/lambdas/python/common/cc_common/email_service_client.py @@ -297,6 +297,31 @@ def send_provider_multiple_registration_attempt_email( return self._invoke_lambda(payload) + def send_provider_ssn_correction_reregistration_email( + self, + compact: str, + provider_email: str, + ) -> dict[str, str]: + """ + Notify a practitioner that their state corrected the SSN on their license record and that they must + register again under the corrected record (their previous account was removed). + + :param compact: Compact name + :param provider_email: Email address the provider had registered with + :return: Response from the email notification service + """ + payload = { + 'compact': compact, + 'template': 'ssnCorrectionReregistrationNotification', + 'recipientType': 'SPECIFIC', + 'specificEmails': [ + provider_email, + ], + 'templateVariables': {}, + } + + return self._invoke_lambda(payload) + def send_license_encumbrance_provider_notification_email( self, *, diff --git a/backend/compact-connect/lambdas/python/common/tests/unit/test_email_service_client.py b/backend/compact-connect/lambdas/python/common/tests/unit/test_email_service_client.py index f8cc8262fc..0d210ce09e 100644 --- a/backend/compact-connect/lambdas/python/common/tests/unit/test_email_service_client.py +++ b/backend/compact-connect/lambdas/python/common/tests/unit/test_email_service_client.py @@ -51,6 +51,31 @@ def test_privilege_deactivation_provider_notification_should_invoke_lambda_clien ), ) + def test_ssn_correction_reregistration_notification_should_invoke_lambda_client_with_expected_parameters(self): + mock_lambda_client = MagicMock() + test_model = self._generate_test_model(mock_lambda_client) + + test_model.send_provider_ssn_correction_reregistration_email( + compact=TEST_COMPACT, + provider_email='test@test.com', + ) + + mock_lambda_client.invoke.assert_called_once_with( + FunctionName='test-lambda-name', + InvocationType='RequestResponse', + Payload=json.dumps( + { + 'compact': TEST_COMPACT, + 'template': 'ssnCorrectionReregistrationNotification', + 'recipientType': 'SPECIFIC', + 'specificEmails': [ + 'test@test.com', + ], + 'templateVariables': {}, + } + ), + ) + def test_privilege_deactivation_jurisdiction_notification_should_invoke_lambda_client_with_expected_parameters( self, ): diff --git a/backend/compact-connect/lambdas/python/provider-data-v1/handlers/ingest.py b/backend/compact-connect/lambdas/python/provider-data-v1/handlers/ingest.py index 552f741273..e13fc6ae7e 100644 --- a/backend/compact-connect/lambdas/python/provider-data-v1/handlers/ingest.py +++ b/backend/compact-connect/lambdas/python/provider-data-v1/handlers/ingest.py @@ -25,16 +25,19 @@ def preprocess_license_ingest(message: dict): This reduces the attack surface by ensuring full SSNs don't reach the event bus. For each message: - 1. Extract the SSN + 1. Extract the SSN (and previousSSN, if the upload is an SSN correction) 2. Get or create the provider ID using the SSN 3. Replace the full SSN with just the last 4 digits - 4. Send the modified message to the event bus + 4. If a previousSSN resolves to a different provider id, forward that id as previousProviderId + 5. Send the modified message to the event bus """ # Extract necessary fields compact = message['compact'] jurisdiction = message['jurisdiction'] ssn = message.pop('ssn') # Remove SSN from the detail + # Remove previousSSN (if present) from the detail; it must never reach the event bus + previous_ssn = message.pop('previousSSN', None) with logger.append_context_keys(compact=compact, jurisdiction=jurisdiction): try: @@ -44,8 +47,24 @@ def preprocess_license_ingest(message: dict): # Add the last 4 digits of SSN to the detail message['ssnLastFour'] = ssn[-4:] - # delete the ssn value from memory so it can be cleaned up as soon as we are done with it + + if previous_ssn is not None and previous_ssn != ssn: + # The state is correcting a previously-uploaded SSN. Resolve the previous SSN to its provider id + # so the ingest handler (which has no SSN access) can migrate that provider's records. If the + # previous SSN was never uploaded, this creates a mapping that simply resolves to a provider with + # no records, which the ingest handler treats as a no-op. + previous_provider_id = config.data_client.get_or_create_provider_id(compact=compact, ssn=previous_ssn) + if previous_provider_id != provider_id: + message['previousProviderId'] = previous_provider_id + logger.info( + 'SSN correction detected; forwarding previous provider id', + new_provider_id=provider_id, + previous_provider_id=previous_provider_id, + ) + + # delete the ssn values from memory so they can be cleaned up as soon as we are done with them del ssn + del previous_ssn # Send the sanitized license data to the event bus with logger.append_context_keys(provider_id=provider_id): @@ -99,11 +118,25 @@ def ingest_license_message(message: dict): compact = license_ingest_message['compact'] jurisdiction = license_ingest_message['jurisdiction'] provider_id = license_ingest_message['providerId'] + # Transient migration routing data set by the preprocessor for SSN corrections; must never be persisted + previous_provider_id = license_ingest_message.pop('previousProviderId', None) with logger.append_context_keys(compact=compact, jurisdiction=jurisdiction): with logger.append_context_keys(provider_id=provider_id): logger.info('Ingesting license data') + if previous_provider_id is not None and previous_provider_id != provider_id: + # The state corrected this practitioner's SSN: move the records uploaded under the + # incorrect SSN's provider id over to this one before the normal license write below + _perform_ssn_correction_migration( + compact=compact, + previous_provider_id=str(previous_provider_id), + new_provider_id=str(provider_id), + jurisdiction=jurisdiction, + license_type=license_ingest_message['licenseType'], + new_ssn_last_four=license_ingest_message['ssnLastFour'], + ) + # Start preparing our db transactions data_events = [] @@ -336,3 +369,62 @@ def _populate_update_record(*, existing_license: dict, updated_values: dict, rem **({'removedValues': sorted(removed_values)} if removed_values else {}), } ) + + +def _perform_ssn_correction_migration( + *, + compact: str, + previous_provider_id: str, + new_provider_id: str, + jurisdiction: str, + license_type: str, + new_ssn_last_four: str, +): + """ + Orchestrate the migration of a practitioner's records after a state corrected the SSN on a license upload. + + The DynamoDB migration runs first; the Cognito user deletion and re-registration email follow, each + idempotent so an SQS retry of a partially-completed migration converges. A concurrency conflict inside the + migration raises, letting SQS redeliver the message after the visibility timeout. + """ + logger.info('Performing SSN correction migration', previous_provider_id=previous_provider_id) + + result = config.data_client.migrate_provider_for_ssn_correction( + compact=compact, + previous_provider_id=previous_provider_id, + new_provider_id=new_provider_id, + jurisdiction=jurisdiction, + license_type=license_type, + new_ssn_last_four=new_ssn_last_four, + ) + if not result.migration_performed: + logger.info('No records to migrate for previous provider id; proceeding with normal ingest') + return + + if result.full_teardown and result.old_provider_registered_email is not None: + _delete_old_cognito_user_and_send_reregistration_email( + compact=compact, + old_registered_email=result.old_provider_registered_email, + ) + + +def _delete_old_cognito_user_and_send_reregistration_email(*, compact: str, old_registered_email: str): + """Delete the old provider's Cognito user and email the practitioner to re-register. + + The email is only sent when a user was actually deleted in this run, so an SQS retry of a + partially-completed migration does not send a duplicate notification. + """ + try: + config.cognito_client.admin_delete_user( + UserPoolId=config.provider_user_pool_id, + Username=old_registered_email, + ) + except config.cognito_client.exceptions.UserNotFoundException: + logger.info('Old Cognito user not found (already deleted); skipping re-registration email') + return + + logger.info('Deleted old Cognito user after SSN correction; sending re-registration email') + config.email_service_client.send_provider_ssn_correction_reregistration_email( + compact=compact, + provider_email=old_registered_email, + ) diff --git a/backend/compact-connect/lambdas/python/provider-data-v1/tests/function/test_handlers/test_ingest.py b/backend/compact-connect/lambdas/python/provider-data-v1/tests/function/test_handlers/test_ingest.py index 8614193acf..b21904df5d 100644 --- a/backend/compact-connect/lambdas/python/provider-data-v1/tests/function/test_handlers/test_ingest.py +++ b/backend/compact-connect/lambdas/python/provider-data-v1/tests/function/test_handlers/test_ingest.py @@ -1,6 +1,6 @@ import json from datetime import date, datetime -from unittest.mock import patch +from unittest.mock import MagicMock, patch from cc_common.data_model.update_tier_enum import UpdateTierEnum from moto import mock_aws @@ -897,3 +897,216 @@ def test_multiple_license_types_different_jurisdictions(self): self.assertEqual('ky', provider_data['licenseJurisdiction']) self.assertEqual('Audrey', provider_data['givenName']) self.assertEqual('Guðmundsdóttir', provider_data['familyName']) + + +@mock_aws +@patch('cc_common.config._Config.current_standard_datetime', datetime.fromisoformat('2024-11-08T23:59:59+00:00')) +class TestIngestSsnCorrection(TstFunction): + """Function tests for the SSN-correction migration orchestration in ingest_license_message. + + The old (incorrect-SSN) provider uses the test-data-generator default provider id; the corrected SSN + resolves to NEW_PROVIDER_ID. + """ + + OLD_PROVIDER_ID = '89a6377e-c3a5-40e5-bca5-317ec854c570' + NEW_PROVIDER_ID = 'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee' + NEW_SSN_LAST_FOUR = '6789' + OLD_REGISTERED_EMAIL = 'old-provider@example.com' + # firstUploadDate tracks when a license was first uploaded; migration must carry it forward unchanged + LICENSE_FIRST_UPLOAD_DATE = datetime.fromisoformat('2020-01-01T00:00:00+00:00') + + def setUp(self): + super().setUp() + # patch the email client method at class level: the handler module may hold a config instance from an + # earlier test module import, so instance-level mocking would not be seen + email_patcher = patch( + 'cc_common.email_service_client.EmailServiceClient.send_provider_ssn_correction_reregistration_email', + MagicMock(return_value={'message': 'Email message sent'}), + ) + self._mock_send_reregistration_email = email_patcher.start() + self.addCleanup(email_patcher.stop) + + def _put_old_provider_records(self, *, with_second_license: bool = False) -> list: + """Store the old provider's records and return the stored data class instances.""" + stored_records = [ + self.test_data_generator.put_default_provider_record_in_provider_table( + {'compactConnectRegisteredEmailAddress': self.OLD_REGISTERED_EMAIL} + ), + self.test_data_generator.put_default_license_record_in_provider_table( + {'firstUploadDate': self.LICENSE_FIRST_UPLOAD_DATE} + ), + self.test_data_generator.put_default_privilege_record_in_provider_table(), + self.test_data_generator.put_default_military_affiliation_in_provider_table(), + ] + if with_second_license: + stored_records.append( + self.test_data_generator.put_default_license_record_in_provider_table({'licenseType': 'audiologist'}) + ) + return stored_records + + def _create_old_cognito_user(self): + self.config.cognito_client.admin_create_user( + UserPoolId=self.config.provider_user_pool_id, + Username=self.OLD_REGISTERED_EMAIL, + UserAttributes=[{'Name': 'email', 'Value': self.OLD_REGISTERED_EMAIL}], + ) + + def _when_old_cognito_user_exists(self) -> bool: + from botocore.exceptions import ClientError + + try: + self.config.cognito_client.admin_get_user( + UserPoolId=self.config.provider_user_pool_id, + Username=self.OLD_REGISTERED_EMAIL, + ) + return True + except ClientError as e: + if e.response['Error']['Code'] == 'UserNotFoundException': + return False + raise + + def _run_ingest_with_previous_provider_id(self): + from handlers.ingest import ingest_license_message + + with open('../common/tests/resources/ingest/event-bridge-message.json') as f: + message = json.load(f) + + message['detail']['providerId'] = self.NEW_PROVIDER_ID + message['detail']['previousProviderId'] = self.OLD_PROVIDER_ID + message['detail']['ssnLastFour'] = self.NEW_SSN_LAST_FOUR + + event = {'Records': [{'messageId': '123', 'body': json.dumps(message)}]} + return ingest_license_message(event, self.mock_context) + + def _get_provider_records(self, provider_id: str) -> list[dict]: + from boto3.dynamodb.conditions import Key + + return self.config.provider_table.query(KeyConditionExpression=Key('pk').eq(f'aslp#PROVIDER#{provider_id}'))[ + 'Items' + ] + + def _get_api_response_snapshot_for_provider(self, provider_id: str) -> dict: + """Load a provider's records and JSON-cast their api response object, matching the format of the + expected snapshots built by generate_default_provider_detail_response. + """ + from cc_common.utils import ResponseEncoder + + provider_user_records = self.config.data_client.get_provider_user_records( + compact='aslp', + provider_id=provider_id, + ) + return json.loads(json.dumps(provider_user_records.generate_api_response_object(), cls=ResponseEncoder)) + + def test_full_teardown_migration_moves_records_under_new_provider_id(self): + old_provider_record_items = self._put_old_provider_records() + self._create_old_cognito_user() + + # snapshot the old provider's full state before the migration runs + expected_old_provider_snapshot = self.test_data_generator.generate_default_provider_detail_response( + old_provider_record_items + ) + self.assertEqual( + expected_old_provider_snapshot, + self._get_api_response_snapshot_for_provider(self.OLD_PROVIDER_ID), + ) + + resp = self._run_ingest_with_previous_provider_id() + self.assertEqual({'batchItemFailures': []}, resp) + + # the migrated license (refreshed by the ingested upload, carrying the corrected ssnLastFour) and its + # privilege now live under the new provider id, with a newly-created provider record. The new provider is + # not registered: the practitioner must register again under the corrected account + expected_new_provider_snapshot = self.test_data_generator.generate_default_provider_detail_response( + [ + self.test_data_generator.generate_default_provider( + value_overrides={ + 'providerId': self.NEW_PROVIDER_ID, + 'ssnLastFour': self.NEW_SSN_LAST_FOUR, + }, + is_registered=False, + ), + self.test_data_generator.generate_default_license( + value_overrides={ + 'providerId': self.NEW_PROVIDER_ID, + 'ssnLastFour': self.NEW_SSN_LAST_FOUR, + 'firstUploadDate': self.LICENSE_FIRST_UPLOAD_DATE, + } + ), + self.test_data_generator.generate_default_privilege( + value_overrides={'providerId': self.NEW_PROVIDER_ID} + ), + ] + ) + self.assertEqual( + expected_new_provider_snapshot, + self._get_api_response_snapshot_for_provider(self.NEW_PROVIDER_ID), + ) + + def test_full_teardown_migration_deletes_cognito_user(self): + self._put_old_provider_records() + self._create_old_cognito_user() + + resp = self._run_ingest_with_previous_provider_id() + self.assertEqual({'batchItemFailures': []}, resp) + + self.assertFalse(self._when_old_cognito_user_exists()) + + def test_full_teardown_migration_sends_reregistration_email(self): + self._put_old_provider_records() + self._create_old_cognito_user() + + resp = self._run_ingest_with_previous_provider_id() + self.assertEqual({'batchItemFailures': []}, resp) + + self._mock_send_reregistration_email.assert_called_once_with( + compact='aslp', + provider_email=self.OLD_REGISTERED_EMAIL, + ) + + def test_partial_migration_keeps_cognito_user_and_sends_no_email(self): + self._put_old_provider_records(with_second_license=True) + self._create_old_cognito_user() + + resp = self._run_ingest_with_previous_provider_id() + self.assertEqual({'batchItemFailures': []}, resp) + + # the old provider still exists with its remaining license and its person-level records + old_records = self._get_provider_records(self.OLD_PROVIDER_ID) + old_record_types = {record['type'] for record in old_records} + self.assertIn('provider', old_record_types) + self.assertIn('license', old_record_types) + self.assertIn('militaryAffiliation', old_record_types) + + # person-level records are not copied to the new provider + new_record_types = {record['type'] for record in self._get_provider_records(self.NEW_PROVIDER_ID)} + self.assertNotIn('militaryAffiliation', new_record_types) + + # the old Cognito user remains and no re-registration email was sent + self.assertTrue(self._when_old_cognito_user_exists()) + self._mock_send_reregistration_email.assert_not_called() + + def test_no_op_migration_still_ingests_license_normally(self): + # the previousSSN resolved to a provider id with no records at all + resp = self._run_ingest_with_previous_provider_id() + self.assertEqual({'batchItemFailures': []}, resp) + + new_records = self._get_provider_records(self.NEW_PROVIDER_ID) + new_record_types = {record['type'] for record in new_records} + self.assertEqual({'license', 'provider'}, new_record_types) + + # previousProviderId is transient migration routing data and must never be persisted + for record in new_records: + self.assertNotIn('previousProviderId', record) + + self._mock_send_reregistration_email.assert_not_called() + + def test_full_teardown_with_unregistered_old_provider_sends_no_email(self): + # the old provider never registered: no Cognito user, no registered email on the provider record + self.test_data_generator.put_default_provider_record_in_provider_table(is_registered=False) + self.test_data_generator.put_default_license_record_in_provider_table() + + resp = self._run_ingest_with_previous_provider_id() + self.assertEqual({'batchItemFailures': []}, resp) + + self.assertEqual([], self._get_provider_records(self.OLD_PROVIDER_ID)) + self._mock_send_reregistration_email.assert_not_called() diff --git a/backend/compact-connect/lambdas/python/provider-data-v1/tests/unit/test_handlers/test_ingest.py b/backend/compact-connect/lambdas/python/provider-data-v1/tests/unit/test_handlers/test_ingest.py index 28f8fff4d7..21b8467f62 100644 --- a/backend/compact-connect/lambdas/python/provider-data-v1/tests/unit/test_handlers/test_ingest.py +++ b/backend/compact-connect/lambdas/python/provider-data-v1/tests/unit/test_handlers/test_ingest.py @@ -45,3 +45,62 @@ def test_preprocess_license_ingest_removes_ssn_from_record(self, mock_config): } ] ) + + def _run_preprocessor_with_previous_ssn(self, mock_config, *, ssn: str, previous_ssn: str) -> dict: + """Run preprocess_license_ingest on a message carrying a previousSSN and return the published event detail.""" + from handlers.ingest import preprocess_license_ingest + + with open('../common/tests/resources/ingest/preprocessor-sqs-message.json') as f: + message = json.load(f) + message['ssn'] = ssn + message['previousSSN'] = previous_ssn + + event = {'Records': [{'messageId': '123', 'body': json.dumps(message)}]} + + resp = preprocess_license_ingest(event, self.mock_context) + self.assertEqual({'batchItemFailures': []}, resp) + + entries = mock_config.events_client.put_events.call_args.kwargs['Entries'] + self.assertEqual(1, len(entries)) + self.assertEqual('license.ingest', entries[0]['DetailType']) + return json.loads(entries[0]['Detail']) + + @patch('handlers.ingest.config', autospec=False) + def test_preprocess_license_ingest_forwards_previous_provider_id_and_never_the_ssns(self, mock_config): + new_provider_id = 'new-provider-id' + previous_provider_id = 'previous-provider-id' + # the first call resolves the current ssn, the second call resolves the previous ssn + mock_config.data_client.get_or_create_provider_id.side_effect = [new_provider_id, previous_provider_id] + + detail = self._run_preprocessor_with_previous_ssn(mock_config, ssn='123-12-1234', previous_ssn='123-12-9876') + + self.assertEqual(new_provider_id, detail['providerId']) + self.assertEqual(previous_provider_id, detail['previousProviderId']) + self.assertEqual('1234', detail['ssnLastFour']) + # neither SSN may ever reach the event bus + self.assertNotIn('ssn', detail) + self.assertNotIn('previousSSN', detail) + + @patch('handlers.ingest.config', autospec=False) + def test_preprocess_license_ingest_omits_previous_provider_id_when_it_resolves_to_same_provider(self, mock_config): + # both SSNs resolve to the same provider id, so there is nothing to migrate + mock_config.data_client.get_or_create_provider_id.side_effect = ['same-provider-id', 'same-provider-id'] + + detail = self._run_preprocessor_with_previous_ssn(mock_config, ssn='123-12-1234', previous_ssn='123-12-9876') + + self.assertEqual('same-provider-id', detail['providerId']) + self.assertNotIn('previousProviderId', detail) + self.assertNotIn('ssn', detail) + self.assertNotIn('previousSSN', detail) + + @patch('handlers.ingest.config', autospec=False) + def test_preprocess_license_ingest_ignores_previous_ssn_equal_to_current_ssn(self, mock_config): + mock_config.data_client.get_or_create_provider_id.return_value = 'provider-id' + + detail = self._run_preprocessor_with_previous_ssn(mock_config, ssn='123-12-1234', previous_ssn='123-12-1234') + + # only the current ssn should have been resolved; a matching previousSSN is a no-op + mock_config.data_client.get_or_create_provider_id.assert_called_once_with(compact='aslp', ssn='123-12-1234') + self.assertNotIn('previousProviderId', detail) + self.assertNotIn('ssn', detail) + self.assertNotIn('previousSSN', detail) From 9c2eaf68dd48ab38856cd096d25e5a6f93601605 Mon Sep 17 00:00:00 2001 From: Landon Shumway Date: Thu, 9 Jul 2026 13:38:35 -0700 Subject: [PATCH 07/41] Add email template for re-registration --- .../email-notification-service/lambda.ts | 9 ++++ .../lib/email/email-notification-service.ts | 32 ++++++++++++ .../tests/email-notification-service.test.ts | 51 +++++++++++++++++++ 3 files changed, 92 insertions(+) diff --git a/backend/compact-connect/lambdas/nodejs/email-notification-service/lambda.ts b/backend/compact-connect/lambdas/nodejs/email-notification-service/lambda.ts index 6d82e0c7dd..1981f07a26 100644 --- a/backend/compact-connect/lambdas/nodejs/email-notification-service/lambda.ts +++ b/backend/compact-connect/lambdas/nodejs/email-notification-service/lambda.ts @@ -327,6 +327,15 @@ export class Lambda implements LambdaInterface { event.specificEmails ); break; + case 'ssnCorrectionReregistrationNotification': + if (!event.specificEmails?.length) { + throw new Error('No recipients found for ssn correction reregistration notification email'); + } + await this.emailService.sendSsnCorrectionReregistrationNotificationEmail( + event.compact, + event.specificEmails + ); + break; case 'providerEmailVerificationCode': if (!event.specificEmails?.length) { throw new Error('No recipients found for provider email verification code email'); diff --git a/backend/compact-connect/lambdas/nodejs/lib/email/email-notification-service.ts b/backend/compact-connect/lambdas/nodejs/lib/email/email-notification-service.ts index ed59ea024d..73881d02d3 100644 --- a/backend/compact-connect/lambdas/nodejs/lib/email/email-notification-service.ts +++ b/backend/compact-connect/lambdas/nodejs/lib/email/email-notification-service.ts @@ -427,6 +427,38 @@ export class EmailNotificationService extends BaseEmailService { await this.sendEmail({ htmlContent, subject, recipients, errorMessage: 'Unable to send multiple registration attempt notification email' }); } + /** + * Sends an email notification to a practitioner whose state corrected the SSN on their license record, + * letting them know their previous account was removed and they need to register again + * @param compact - The compact name + * @param specificEmails - The email address the practitioner had registered with + */ + public async sendSsnCorrectionReregistrationNotificationEmail( + compact: string, + specificEmails: string[] = [] + ): Promise { + this.logger.info('Sending ssn correction reregistration notification email', { compact: compact, recipients: specificEmails }); + + const recipients = specificEmails; + + if (recipients.length === 0) { + throw new Error(`No recipients found for ssn correction reregistration notification email`); + } + + const report = this.getNewEmailTemplate(); + const subject = `Action Required: Registration Update - CompactConnect`; + const registrationUrl = `${environmentVariableService.getUiBasePathUrl()}/register`; + const bodyText = `Your state licensing board recently corrected the information on one of your license records in the CompactConnect system. As part of this correction, your previous CompactConnect account was removed.\n\nTo continue using CompactConnect, please register again using the link below:\n\n${registrationUrl}\n\nIf you have any questions, please contact your state licensing board.`; + + this.insertHeader(report, 'Registration Update Required'); + this.insertBody(report, bodyText, 'center', true); + this.insertFooter(report); + + const htmlContent = this.renderTemplate(report); + + await this.sendEmail({ htmlContent, subject, recipients, errorMessage: 'Unable to send ssn correction reregistration notification email' }); + } + /** * Sends a verification code to a provider's new email address during email change process * @param compact - The compact name diff --git a/backend/compact-connect/lambdas/nodejs/tests/email-notification-service.test.ts b/backend/compact-connect/lambdas/nodejs/tests/email-notification-service.test.ts index 313925bffe..08d39bc56f 100644 --- a/backend/compact-connect/lambdas/nodejs/tests/email-notification-service.test.ts +++ b/backend/compact-connect/lambdas/nodejs/tests/email-notification-service.test.ts @@ -800,6 +800,57 @@ describe('EmailNotificationServiceLambda', () => { }); }); + describe('SSN Correction Reregistration Notification', () => { + const SAMPLE_SSN_CORRECTION_REREGISTRATION_NOTIFICATION_EVENT: EmailNotificationEvent = { + template: 'ssnCorrectionReregistrationNotification', + recipientType: 'SPECIFIC', + compact: 'aslp', + specificEmails: ['user@example.com'], + templateVariables: {} + }; + + it('should successfully send ssn correction reregistration notification email', async () => { + const response = await lambda.handler(SAMPLE_SSN_CORRECTION_REREGISTRATION_NOTIFICATION_EVENT, {} as any); + + expect(response).toEqual({ + message: 'Email message sent' + }); + + // Verify email was sent with correct parameters + expect(mockSESClient).toHaveReceivedCommandWith(SendEmailCommand, { + Destination: { + ToAddresses: ['user@example.com'] + }, + Content: { + Simple: { + Body: { + Html: { + Charset: 'UTF-8', + Data: expect.stringContaining('') + } + }, + Subject: { + Charset: 'UTF-8', + Data: 'Action Required: Registration Update - CompactConnect' + } + } + }, + FromEmailAddress: 'CompactConnect ' + }); + }); + + it('should throw error when no recipients found', async () => { + const eventWithNoRecipients: EmailNotificationEvent = { + ...SAMPLE_SSN_CORRECTION_REREGISTRATION_NOTIFICATION_EVENT, + specificEmails: [] + }; + + await expect(lambda.handler(eventWithNoRecipients, {} as any)) + .rejects + .toThrow('No recipients found for ssn correction reregistration notification email'); + }); + }); + describe('License Encumbrance Provider Notification', () => { const SAMPLE_LICENSE_ENCUMBRANCE_PROVIDER_NOTIFICATION_EVENT: EmailNotificationEvent = { template: 'licenseEncumbranceProviderNotification', From 3da82b1b78fbfe12c6f5205e103f20cc620a7c8c Mon Sep 17 00:00:00 2001 From: Landon Shumway Date: Thu, 9 Jul 2026 14:34:37 -0700 Subject: [PATCH 08/41] Moving objects in provider s3 bucket from old to new keyspace --- .../cc_common/data_model/data_client.py | 45 ++++--- .../provider-data-v1/handlers/ingest.py | 60 ++++++++- .../function/test_handlers/test_ingest.py | 115 +++++++++++++++++- .../compact-connect/stacks/ingest_stack.py | 12 +- 4 files changed, 203 insertions(+), 29 deletions(-) diff --git a/backend/compact-connect/lambdas/python/common/cc_common/data_model/data_client.py b/backend/compact-connect/lambdas/python/common/cc_common/data_model/data_client.py index 0324b3da5d..8a82d89877 100644 --- a/backend/compact-connect/lambdas/python/common/cc_common/data_model/data_client.py +++ b/backend/compact-connect/lambdas/python/common/cc_common/data_model/data_client.py @@ -2871,14 +2871,15 @@ def migrate_provider_for_ssn_correction( The migration is scoped to the single (jurisdiction, license type) license of the corrected upload row. That license, the privileges purchased against it, and their adverse action / investigation / update - history records are always moved to the new provider id. Person-level records (military affiliations, - provider update history) are never carried over: the practitioner re-registers under the corrected - provider id and re-uploads any documents. What happens to the rest of the old provider depends on - whether the corrected license was its only license record: - - - Full teardown (sole license): the old provider's entire partition, including its person-level records - and top-level provider record, is deleted. The caller is responsible for deleting the old Cognito - user and notifying the practitioner. + history records are always moved to the new provider id. What happens to the rest of the old provider + depends on whether the corrected license was its only license record: + + - Full teardown (sole license): the person-level records (military affiliations, provider update + history) are moved to the new provider id as well, with military document keys re-pointed at the new + provider id's keyspace, and the old provider's partition, including its top-level provider record, is + deleted. The caller is responsible for moving the practitioner's S3 documents (by listing the old + provider id's keyspace directly, not by relying on any single record type's tracked keys), deleting + the old Cognito user, and notifying the practitioner. - Partial (other licenses remain): the old provider keeps its person-level records and its top-level record is repopulated from its remaining licenses. The old provider id remains a valid (partial) practitioner until its remaining licenses are also corrected. @@ -2930,6 +2931,12 @@ def migrate_provider_for_ssn_correction( # The corrected license was the old provider's only license: tear the old provider down entirely full_teardown = len(old_provider_records.get_license_records()) == 1 + # Person-level records (military affiliations, provider update history) follow the practitioner only + # when the old provider is torn down entirely; on a partial migration they stay with the old provider, + # which still represents them for their remaining licenses + person_level_records = old_provider_records.get_person_level_records() if full_teardown else [] + records_to_move = [*records_to_move, *person_level_records] + target_license = next(record for record in records_to_move if record.type == ProviderRecordType.LICENSE) target_license_key = self._provider_record_key(target_license) @@ -2949,11 +2956,21 @@ def migrate_provider_for_ssn_correction( ) # 2. Re-keyed puts for every migrated record. The targeted license also picks up the corrected - # ssnLastFour so the new partition is internally consistent + # ssnLastFour, and military affiliation records pick up document keys under the new provider id, so + # the new partition is internally consistent. The caller is responsible for moving the S3 objects rekeyed_target_license = None rekeyed_privileges = [] for record in records_to_move: - extra_updates = {'ssnLastFour': new_ssn_last_four} if record is target_license else None + extra_updates = None + if record is target_license: + extra_updates = {'ssnLastFour': new_ssn_last_four} + elif record.type == ProviderRecordType.MILITARY_AFFILIATION: + extra_updates = { + 'documentKeys': [ + document_key.replace(str(previous_provider_id), str(new_provider_id)) + for document_key in record.documentKeys + ] + } rekeyed_record = self._rekey_record_to_provider(record, new_provider_id, extra_updates) if record is target_license: rekeyed_target_license = rekeyed_record @@ -2962,12 +2979,8 @@ def migrate_provider_for_ssn_correction( transaction_items.append(self._build_put_transaction_item(rekeyed_record)) # 3. Deletes for the moved records, except the targeted license (deleted last) and the top-level - # provider record (handled by the fence above). On a full teardown the person-level records - # (military affiliations, provider update history) are deleted with the partition - records_to_delete = list(records_to_move) - if full_teardown: - records_to_delete.extend(old_provider_records.get_person_level_records()) - for record in records_to_delete: + # provider record (handled by the fence above) + for record in records_to_move: if record is target_license: continue transaction_items.append(self._build_delete_transaction_item(self._provider_record_key(record))) diff --git a/backend/compact-connect/lambdas/python/provider-data-v1/handlers/ingest.py b/backend/compact-connect/lambdas/python/provider-data-v1/handlers/ingest.py index e13fc6ae7e..647aceac5a 100644 --- a/backend/compact-connect/lambdas/python/provider-data-v1/handlers/ingest.py +++ b/backend/compact-connect/lambdas/python/provider-data-v1/handlers/ingest.py @@ -2,6 +2,7 @@ from copy import deepcopy from boto3.dynamodb.types import TypeSerializer +from botocore.exceptions import ClientError from cc_common.config import config, logger from cc_common.data_model.provider_record_util import ProviderRecordType, ProviderRecordUtility from cc_common.data_model.schema import LicenseRecordSchema @@ -383,9 +384,10 @@ def _perform_ssn_correction_migration( """ Orchestrate the migration of a practitioner's records after a state corrected the SSN on a license upload. - The DynamoDB migration runs first; the Cognito user deletion and re-registration email follow, each - idempotent so an SQS retry of a partially-completed migration converges. A concurrency conflict inside the - migration raises, letting SQS redeliver the message after the visibility timeout. + The DynamoDB migration runs first; on a full teardown the S3 document move, Cognito user deletion, and + re-registration email follow, each idempotent so an SQS retry of a partially-completed migration converges. + A concurrency conflict inside the migration raises, letting SQS redeliver the message after the visibility + timeout. """ logger.info('Performing SSN correction migration', previous_provider_id=previous_provider_id) @@ -401,11 +403,57 @@ def _perform_ssn_correction_migration( logger.info('No records to migrate for previous provider id; proceeding with normal ingest') return - if result.full_teardown and result.old_provider_registered_email is not None: - _delete_old_cognito_user_and_send_reregistration_email( + if result.full_teardown: + _move_provider_documents_to_new_keyspace( compact=compact, - old_registered_email=result.old_provider_registered_email, + previous_provider_id=previous_provider_id, + new_provider_id=new_provider_id, ) + if result.old_provider_registered_email is not None: + _delete_old_cognito_user_and_send_reregistration_email( + compact=compact, + old_registered_email=result.old_provider_registered_email, + ) + + +def _move_provider_documents_to_new_keyspace(*, compact: str, previous_provider_id: str, new_provider_id: str): + """Move every object under the old provider id's S3 keyspace to the new provider id's. + + Rather than relying on any single record type's tracked document keys, this lists everything under the + old provider's keyspace prefix (`compact/{compact}/provider/{provider_id}/`) and moves it, changing only + the provider id segment of each key. This picks up every document type a provider might have uploaded, + including ones this migration logic has no other knowledge of. + + Runs after the DynamoDB migration (whose migrated militaryAffiliation records already reference the new + keys). Best-effort per object: a copy/delete failure is logged and skipped rather than failing the + migration, and re-running against an already-moved key is a no-op (the source object is simply absent). + """ + old_prefix = f'compact/{compact}/provider/{previous_provider_id}/' + new_prefix = f'compact/{compact}/provider/{new_provider_id}/' + + paginator = config.s3_client.get_paginator('list_objects_v2') + for page in paginator.paginate(Bucket=config.provider_user_bucket_name, Prefix=old_prefix): + for s3_object in page.get('Contents', []): + old_key = s3_object['Key'] + new_key = new_prefix + old_key[len(old_prefix) :] + _move_s3_object(old_key=old_key, new_key=new_key) + + +def _move_s3_object(*, old_key: str, new_key: str): + try: + config.s3_client.copy_object( + Bucket=config.provider_user_bucket_name, + CopySource={'Bucket': config.provider_user_bucket_name, 'Key': old_key}, + Key=new_key, + ) + config.s3_client.delete_object(Bucket=config.provider_user_bucket_name, Key=old_key) + except ClientError as e: + logger.error( + 'Failed to move provider document to the new keyspace', + old_key=old_key, + new_key=new_key, + error=str(e), + ) def _delete_old_cognito_user_and_send_reregistration_email(*, compact: str, old_registered_email: str): diff --git a/backend/compact-connect/lambdas/python/provider-data-v1/tests/function/test_handlers/test_ingest.py b/backend/compact-connect/lambdas/python/provider-data-v1/tests/function/test_handlers/test_ingest.py index b21904df5d..229522d047 100644 --- a/backend/compact-connect/lambdas/python/provider-data-v1/tests/function/test_handlers/test_ingest.py +++ b/backend/compact-connect/lambdas/python/provider-data-v1/tests/function/test_handlers/test_ingest.py @@ -926,6 +926,19 @@ def setUp(self): self._mock_send_reregistration_email = email_patcher.start() self.addCleanup(email_patcher.stop) + def build_resources(self): + import os + + import boto3 + + super().build_resources() + self._provider_user_bucket = boto3.resource('s3').create_bucket(Bucket=os.environ['PROVIDER_USER_BUCKET_NAME']) + + def delete_resources(self): + self._provider_user_bucket.objects.delete() + self._provider_user_bucket.delete() + super().delete_resources() + def _put_old_provider_records(self, *, with_second_license: bool = False) -> list: """Store the old provider's records and return the stored data class instances.""" stored_records = [ @@ -1013,9 +1026,11 @@ def test_full_teardown_migration_moves_records_under_new_provider_id(self): resp = self._run_ingest_with_previous_provider_id() self.assertEqual({'batchItemFailures': []}, resp) - # the migrated license (refreshed by the ingested upload, carrying the corrected ssnLastFour) and its - # privilege now live under the new provider id, with a newly-created provider record. The new provider is - # not registered: the practitioner must register again under the corrected account + # the migrated license (refreshed by the ingested upload, carrying the corrected ssnLastFour), its + # privilege, and the person-level military affiliation record now live under the new provider id, with + # a newly-created provider record. The new provider is not registered: the practitioner must register + # again under the corrected account + default_military_affiliation = self.test_data_generator.generate_default_military_affiliation() expected_new_provider_snapshot = self.test_data_generator.generate_default_provider_detail_response( [ self.test_data_generator.generate_default_provider( @@ -1035,6 +1050,16 @@ def test_full_teardown_migration_moves_records_under_new_provider_id(self): self.test_data_generator.generate_default_privilege( value_overrides={'providerId': self.NEW_PROVIDER_ID} ), + self.test_data_generator.generate_default_military_affiliation( + value_overrides={ + 'providerId': self.NEW_PROVIDER_ID, + # the migration re-points document keys at the new provider id's keyspace + 'documentKeys': [ + document_key.replace(self.OLD_PROVIDER_ID, self.NEW_PROVIDER_ID) + for document_key in default_military_affiliation.documentKeys + ], + } + ), ] ) self.assertEqual( @@ -1110,3 +1135,87 @@ def test_full_teardown_with_unregistered_old_provider_sends_no_email(self): self.assertEqual([], self._get_provider_records(self.OLD_PROVIDER_ID)) self._mock_send_reregistration_email.assert_not_called() + + def _s3_object_body(self, key: str) -> bytes | None: + """Return the body of an object in the provider user bucket, or None if it does not exist.""" + from botocore.exceptions import ClientError + + try: + return self._provider_user_bucket.Object(key).get()['Body'].read() + except ClientError as e: + if e.response['Error']['Code'] == 'NoSuchKey': + return None + raise + + def test_full_teardown_migration_moves_military_documents_to_new_provider_keyspace(self): + # the old provider has two military affiliation records, each with a document stored under the old + # provider id's keyspace in the provider user bucket + self.test_data_generator.put_default_provider_record_in_provider_table( + {'compactConnectRegisteredEmailAddress': self.OLD_REGISTERED_EMAIL} + ) + self.test_data_generator.put_default_license_record_in_provider_table() + + old_documents = { + f'compact/aslp/provider/{self.OLD_PROVIDER_ID}/document-type/military-affiliations' + f'/2024-07-08T23:59:59+00:00/1234#military-waiver.pdf': b'waiver-document-content', + f'compact/aslp/provider/{self.OLD_PROVIDER_ID}/document-type/military-affiliations' + f'/2024-08-08T23:59:59+00:00/5678#military-orders.pdf': b'orders-document-content', + } + for date_of_upload, (document_key, document_body) in zip( + ['2024-07-08T23:59:59+00:00', '2024-08-08T23:59:59+00:00'], old_documents.items(), strict=True + ): + self.test_data_generator.put_default_military_affiliation_in_provider_table( + { + 'dateOfUpload': datetime.fromisoformat(date_of_upload), + 'documentKeys': [document_key], + } + ) + self._provider_user_bucket.put_object(Key=document_key, Body=document_body) + + resp = self._run_ingest_with_previous_provider_id() + self.assertEqual({'batchItemFailures': []}, resp) + + # the old partition is gone; both military affiliation records now live under the new provider id, + # with their document keys re-pointed at the new provider's keyspace + self.assertEqual([], self._get_provider_records(self.OLD_PROVIDER_ID)) + new_military_records = [ + record + for record in self._get_provider_records(self.NEW_PROVIDER_ID) + if record['type'] == 'militaryAffiliation' + ] + self.assertEqual(2, len(new_military_records)) + for record in new_military_records: + self.assertEqual(self.NEW_PROVIDER_ID, record['providerId']) + self.assertEqual( + sorted(key.replace(self.OLD_PROVIDER_ID, self.NEW_PROVIDER_ID) for key in old_documents), + sorted(key for record in new_military_records for key in record['documentKeys']), + ) + + # the documents were moved in S3: old objects deleted, new objects present with the same content + for old_key, document_body in old_documents.items(): + self.assertIsNone(self._s3_object_body(old_key)) + new_key = old_key.replace(self.OLD_PROVIDER_ID, self.NEW_PROVIDER_ID) + self.assertEqual(document_body, self._s3_object_body(new_key)) + + def test_full_teardown_migration_moves_all_objects_under_old_provider_keyspace(self): + """The S3 move must be driven by listing the old provider id's keyspace directly, not by walking + DynamoDB records for known document types. This way any file under a provider's keyspace is carried + over on a full teardown, including document types the migration logic doesn't know about. + """ + self.test_data_generator.put_default_provider_record_in_provider_table( + {'compactConnectRegisteredEmailAddress': self.OLD_REGISTERED_EMAIL} + ) + self.test_data_generator.put_default_license_record_in_provider_table() + + # an object under the old provider's keyspace with no corresponding DynamoDB record referencing it + # (e.g. a future/unsupported document type) + untracked_key = f'compact/aslp/provider/{self.OLD_PROVIDER_ID}/document-type/some-future-type/file.pdf' + self._provider_user_bucket.put_object(Key=untracked_key, Body=b'untracked-document-content') + + resp = self._run_ingest_with_previous_provider_id() + self.assertEqual({'batchItemFailures': []}, resp) + + # only the provider id segment of the keyspace changes; everything after it is preserved verbatim + new_key = f'compact/aslp/provider/{self.NEW_PROVIDER_ID}/document-type/some-future-type/file.pdf' + self.assertIsNone(self._s3_object_body(untracked_key)) + self.assertEqual(b'untracked-document-content', self._s3_object_body(new_key)) diff --git a/backend/compact-connect/stacks/ingest_stack.py b/backend/compact-connect/stacks/ingest_stack.py index 9928978ae4..b0b7a8d26a 100644 --- a/backend/compact-connect/stacks/ingest_stack.py +++ b/backend/compact-connect/stacks/ingest_stack.py @@ -52,6 +52,7 @@ def _add_v1_ingest_chain( 'EVENT_BUS_NAME': data_event_bus.event_bus_name, 'PROVIDER_TABLE_NAME': persistent_stack.provider_table.table_name, 'PROVIDER_USER_POOL_ID': provider_users_stack.provider_users.user_pool_id, + 'PROVIDER_USER_BUCKET_NAME': persistent_stack.provider_users_bucket.bucket_name, 'EMAIL_NOTIFICATION_SERVICE_LAMBDA_NAME': ( persistent_stack.email_notification_service_lambda.function_name ), @@ -61,9 +62,12 @@ def _add_v1_ingest_chain( ) persistent_stack.provider_table.grant_read_write_data(ingest_handler) data_event_bus.grant_put_events_to(ingest_handler) - # The SSN-correction migration deletes the old provider's Cognito account on a full teardown and - # notifies the practitioner to re-register + # The SSN-correction migration deletes the old provider's Cognito account on a full teardown, moves + # the practitioner's documents from the old provider id's keyspace to the new one in the provider + # users bucket, and notifies the practitioner to re-register provider_users_stack.provider_users.grant(ingest_handler, 'cognito-idp:AdminDeleteUser') + persistent_stack.provider_users_bucket.grant_read_write(ingest_handler) + persistent_stack.provider_users_bucket.grant_delete(ingest_handler) persistent_stack.email_notification_service_lambda.grant_invoke(ingest_handler) NagSuppressions.add_resource_suppressions_by_path( @@ -74,8 +78,8 @@ def _add_v1_ingest_chain( 'id': 'AwsSolutions-IAM5', 'reason': """ This policy contains wild-carded actions and resources but they are scoped to the - specific actions, KMS key, Table, user pool, and lambda that this handler specifically - needs access to. + specific actions, KMS key, Table, user pool, bucket, and lambda that this handler + specifically needs access to. """, }, ], From 615fbc8ca018df5bf61816a7d0601cd6a276da27 Mon Sep 17 00:00:00 2001 From: Landon Shumway Date: Thu, 9 Jul 2026 15:02:28 -0700 Subject: [PATCH 09/41] Add feature flag for previousSSN field --- .../common/cc_common/feature_flag_enum.py | 1 - .../provider-data-v1/handlers/bulk_upload.py | 32 ++++++++---------- .../provider-data-v1/handlers/licenses.py | 33 ++++++++----------- .../stacks/feature_flag_stack/__init__.py | 20 ++--------- 4 files changed, 31 insertions(+), 55 deletions(-) diff --git a/backend/compact-connect/lambdas/python/common/cc_common/feature_flag_enum.py b/backend/compact-connect/lambdas/python/common/cc_common/feature_flag_enum.py index 630d81d49d..ab671782a8 100644 --- a/backend/compact-connect/lambdas/python/common/cc_common/feature_flag_enum.py +++ b/backend/compact-connect/lambdas/python/common/cc_common/feature_flag_enum.py @@ -11,6 +11,5 @@ class FeatureFlagEnum(StrEnum): # flag used by internal testing TEST_FLAG = 'test-flag' # runtime flags - DUPLICATE_SSN_UPLOAD_CHECK_FLAG = 'duplicate-ssn-upload-check-flag' HOME_JURISDICTION_CHANGE_NOTIFICATION_FLAG = 'home-jurisdiction-change-notification-flag' LICENSE_SSN_CORRECTION_MIGRATION_FLAG = 'license-ssn-correction-migration-flag' diff --git a/backend/compact-connect/lambdas/python/provider-data-v1/handlers/bulk_upload.py b/backend/compact-connect/lambdas/python/provider-data-v1/handlers/bulk_upload.py index 9ba87973e6..63341146c4 100644 --- a/backend/compact-connect/lambdas/python/provider-data-v1/handlers/bulk_upload.py +++ b/backend/compact-connect/lambdas/python/provider-data-v1/handlers/bulk_upload.py @@ -27,9 +27,6 @@ from marshmallow import ValidationError from marshmallow.exceptions import SCHEMA -duplicate_ssn_check_flag_enabled = is_feature_enabled( - FeatureFlagEnum.DUPLICATE_SSN_UPLOAD_CHECK_FLAG, fail_default=True -) # this flag gates a record migration that deletes records, so we fail closed if the flag cannot be checked ssn_correction_migration_flag_enabled = is_feature_enabled( FeatureFlagEnum.LICENSE_SSN_CORRECTION_MIGRATION_FLAG, fail_default=False @@ -167,21 +164,20 @@ def process_bulk_upload_file( validated_license = schema.load(dict(compact=compact, jurisdiction=jurisdiction, **raw_license)) # verify that this ssn/licenseType combination has not been used previously in the same batch ssn_key = (validated_license['ssn'], validated_license['licenseType']) - if duplicate_ssn_check_flag_enabled: - matched_ssn_index = ssns_in_file_upload.get(ssn_key) - if matched_ssn_index: - # format the validation error as dict so it can be processed by email handler downstream - raise ValidationError( - { - SCHEMA: [ - f'Duplicate License SSN detected for license type ' - f'{validated_license["licenseType"]}. SSN matches with record ' - f'{matched_ssn_index}. Every record must have a unique SSN per license type ' - f'within the same file.' - ] - } - ) - ssns_in_file_upload.update({ssn_key: i + 1}) + matched_ssn_index = ssns_in_file_upload.get(ssn_key) + if matched_ssn_index: + # format the validation error as dict so it can be processed by email handler downstream + raise ValidationError( + { + SCHEMA: [ + f'Duplicate License SSN detected for license type ' + f'{validated_license["licenseType"]}. SSN matches with record ' + f'{matched_ssn_index}. Every record must have a unique SSN per license type ' + f'within the same file.' + ] + } + ) + ssns_in_file_upload.update({ssn_key: i + 1}) except TypeError as e: # This will be raised, if `raw_license` includes compact and/or jurisdiction fields logger.error('License contains unsupported fields', fields=list(raw_license.keys()), exc_info=e) diff --git a/backend/compact-connect/lambdas/python/provider-data-v1/handlers/licenses.py b/backend/compact-connect/lambdas/python/provider-data-v1/handlers/licenses.py index 53999dfbf1..5779677441 100644 --- a/backend/compact-connect/lambdas/python/provider-data-v1/handlers/licenses.py +++ b/backend/compact-connect/lambdas/python/provider-data-v1/handlers/licenses.py @@ -15,13 +15,9 @@ schema = LicensePostRequestSchema() -# initialize flags outside of handler so the flags are cached for the lifecycle of the execution environment +# initialize flag outside of handler so the flag is cached for the lifecycle of the execution environment from cc_common.feature_flag_client import FeatureFlagEnum, is_feature_enabled # noqa: E402 -# low risk flag, so we default to enabled if failure detected -duplicate_ssn_check_flag_enabled = is_feature_enabled( - FeatureFlagEnum.DUPLICATE_SSN_UPLOAD_CHECK_FLAG, fail_default=True -) # this flag gates a record migration that deletes records, so we fail closed if the flag cannot be checked ssn_correction_migration_flag_enabled = is_feature_enabled( FeatureFlagEnum.LICENSE_SSN_CORRECTION_MIGRATION_FLAG, fail_default=False @@ -85,20 +81,19 @@ def post_licenses(event: dict, context: LambdaContext): # noqa: ARG001 unused-a 'errors': invalid_records, } ) - if duplicate_ssn_check_flag_enabled: - # verify that none of the SSN+LicenseType combinations are repeats within the same batch - license_keys = [(license_record['ssn'], license_record['licenseType']) for license_record in licenses] - if len(set(license_keys)) < len(license_keys): - logger.info('Duplicate SSNs detected in same request.', compact=compact, jurisdiction=jurisdiction) - raise CCInvalidRequestCustomResponseException( - response_body={ - 'message': 'Invalid license records in request. See errors for more detail.', - 'errors': { - 'SSN': 'Same SSN for the same license type detected on multiple rows. ' - 'Every record must have a unique SSN per license type within the same request.' - }, - } - ) + # verify that none of the SSN+LicenseType combinations are repeats within the same batch + license_keys = [(license_record['ssn'], license_record['licenseType']) for license_record in licenses] + if len(set(license_keys)) < len(license_keys): + logger.info('Duplicate SSNs detected in same request.', compact=compact, jurisdiction=jurisdiction) + raise CCInvalidRequestCustomResponseException( + response_body={ + 'message': 'Invalid license records in request. See errors for more detail.', + 'errors': { + 'SSN': 'Same SSN for the same license type detected on multiple rows. ' + 'Every record must have a unique SSN per license type within the same request.' + }, + } + ) event_time = config.current_standard_datetime diff --git a/backend/compact-connect/stacks/feature_flag_stack/__init__.py b/backend/compact-connect/stacks/feature_flag_stack/__init__.py index 1257a1f854..0ddee84a8b 100644 --- a/backend/compact-connect/stacks/feature_flag_stack/__init__.py +++ b/backend/compact-connect/stacks/feature_flag_stack/__init__.py @@ -113,25 +113,11 @@ def __init__( environment_name=environment_name, ) - self.duplicate_ssn_upload_check_flag = FeatureFlagResource( + self.license_ssn_correction_migration_flag = FeatureFlagResource( self, - 'DuplicateSsnUploadCheckFlag', + 'LicenseSsnCorrectionMigrationFlag', provider=self.provider, # Shared provider - flag_name='duplicate-ssn-upload-check-flag', - # Low risk update, we will automatically enable for every environment - auto_enable_envs=[ - FeatureFlagEnvironmentName.TEST, - FeatureFlagEnvironmentName.BETA, - FeatureFlagEnvironmentName.PROD, - ], - environment_name=environment_name, - ) - - self.home_jurisdiction_change_notification_flag = FeatureFlagResource( - self, - 'HomeJurisdictionChangeNotificationFlag', - provider=self.provider, # Shared provider - flag_name='home-jurisdiction-change-notification-flag', + flag_name='license-ssn-correction-migration-flag', # Automatically enable for every environment auto_enable_envs=[ FeatureFlagEnvironmentName.TEST, From b7292f0c53a91403fad7062a236e9d18648a2e33 Mon Sep 17 00:00:00 2001 From: Landon Shumway Date: Thu, 9 Jul 2026 15:17:02 -0700 Subject: [PATCH 10/41] Remove old feature flag for home jurisdiction change notification --- .../common/cc_common/feature_flag_enum.py | 1 - .../handlers/provider_users.py | 18 ++++------ ...provider_users_home_jurisdiction_change.py | 33 +------------------ 3 files changed, 8 insertions(+), 44 deletions(-) diff --git a/backend/compact-connect/lambdas/python/common/cc_common/feature_flag_enum.py b/backend/compact-connect/lambdas/python/common/cc_common/feature_flag_enum.py index ab671782a8..064e107cef 100644 --- a/backend/compact-connect/lambdas/python/common/cc_common/feature_flag_enum.py +++ b/backend/compact-connect/lambdas/python/common/cc_common/feature_flag_enum.py @@ -11,5 +11,4 @@ class FeatureFlagEnum(StrEnum): # flag used by internal testing TEST_FLAG = 'test-flag' # runtime flags - HOME_JURISDICTION_CHANGE_NOTIFICATION_FLAG = 'home-jurisdiction-change-notification-flag' LICENSE_SSN_CORRECTION_MIGRATION_FLAG = 'license-ssn-correction-migration-flag' diff --git a/backend/compact-connect/lambdas/python/provider-data-v1/handlers/provider_users.py b/backend/compact-connect/lambdas/python/provider-data-v1/handlers/provider_users.py index c8c711d4a6..6a275d24b2 100644 --- a/backend/compact-connect/lambdas/python/provider-data-v1/handlers/provider_users.py +++ b/backend/compact-connect/lambdas/python/provider-data-v1/handlers/provider_users.py @@ -126,17 +126,13 @@ def _put_provider_home_jurisdiction(event: dict, context: LambdaContext): # noq # this is a no-op and we skip sending notifications if previous_home_jurisdiction != selected_jurisdiction: try: - # Publish event for notification processing if feature flag is enabled - from cc_common.feature_flag_client import FeatureFlagEnum, is_feature_enabled - - if is_feature_enabled(FeatureFlagEnum.HOME_JURISDICTION_CHANGE_NOTIFICATION_FLAG, fail_default=False): - config.event_bus_client.publish_home_jurisdiction_change_event( - source='org.compactconnect.provider-data', - compact=compact, - provider_id=provider_id, - previous_home_jurisdiction=previous_home_jurisdiction, - new_home_jurisdiction=selected_jurisdiction, - ) + config.event_bus_client.publish_home_jurisdiction_change_event( + source='org.compactconnect.provider-data', + compact=compact, + provider_id=provider_id, + previous_home_jurisdiction=previous_home_jurisdiction, + new_home_jurisdiction=selected_jurisdiction, + ) except ClientError as e: # Log the error and continue logger.error( diff --git a/backend/compact-connect/lambdas/python/provider-data-v1/tests/function/test_handlers/test_provider_users_home_jurisdiction_change.py b/backend/compact-connect/lambdas/python/provider-data-v1/tests/function/test_handlers/test_provider_users_home_jurisdiction_change.py index 35d77a8fe1..78341244c1 100644 --- a/backend/compact-connect/lambdas/python/provider-data-v1/tests/function/test_handlers/test_provider_users_home_jurisdiction_change.py +++ b/backend/compact-connect/lambdas/python/provider-data-v1/tests/function/test_handlers/test_provider_users_home_jurisdiction_change.py @@ -1047,16 +1047,11 @@ def test_put_provider_home_jurisdiction_deactivates_privileges_if_new_jurisdicti self.assertEqual(test_current_license_record.dateOfExpiration, stored_privilege_data.dateOfExpiration) self.assertEqual(test_current_license_record.jurisdiction, stored_privilege_data.licenseJurisdiction) - # TODO - remove flag mock when flag is removed #noqa: FIX002 @patch('cc_common.event_bus_client.EventBusClient.publish_home_jurisdiction_change_event') - @patch('cc_common.feature_flag_client.is_feature_enabled') - def test_put_provider_home_jurisdiction_handler_publishes_event(self, mock_is_feature_enabled, mock_publish_event): + def test_put_provider_home_jurisdiction_handler_publishes_event(self, mock_publish_event): """Test that provider home jurisdiction handler publishes the correct event.""" from handlers.provider_users import provider_users_api_handler - # Mock feature flag to return True - mock_is_feature_enabled.return_value = True - (test_provider_record, test_current_license_record, test_privilege_record) = ( self._when_provider_has_one_license_and_privilege() ) @@ -1076,29 +1071,3 @@ def test_put_provider_home_jurisdiction_handler_publishes_event(self, mock_is_fe previous_home_jurisdiction=STARTING_JURISDICTION, new_home_jurisdiction=NEW_JURISDICTION, ) - - # TODO - remove test when feature flag is removed #noqa: FIX002 - @patch('cc_common.event_bus_client.EventBusClient.publish_home_jurisdiction_change_event') - @patch('cc_common.feature_flag_client.is_feature_enabled') - def test_put_provider_home_jurisdiction_handler_does_not_publish_event_with_flag_off( - self, mock_is_feature_enabled, mock_publish_event - ): - """Test that provider home jurisdiction handler publishes the correct event.""" - from handlers.provider_users import provider_users_api_handler - - # Mock feature flag to return False - mock_is_feature_enabled.return_value = False - - (test_provider_record, test_current_license_record, test_privilege_record) = ( - self._when_provider_has_one_license_and_privilege() - ) - - # Create a license in the new jurisdiction - self._when_provider_has_license_in_new_home_state() - event = self._when_testing_put_provider_home_jurisdiction(NEW_JURISDICTION, test_provider_record) - - response = provider_users_api_handler(event, self.mock_context) - self.assertEqual(200, response['statusCode']) - - # Verify event was published with correct details - mock_publish_event.assert_not_called() From 1b426ed1a44e6016e135320c1cb9f1e5f9bcbd33 Mon Sep 17 00:00:00 2001 From: Landon Shumway Date: Thu, 9 Jul 2026 15:41:33 -0700 Subject: [PATCH 11/41] Feedback - mask practitioner emails in logs --- .../nodejs/lib/email/base-email-service.ts | 13 +++++++ .../lib/email/email-notification-service.ts | 27 +++++++++---- .../lib/email/base-email-service.test.ts | 39 +++++++++++++++++++ 3 files changed, 72 insertions(+), 7 deletions(-) diff --git a/backend/compact-connect/lambdas/nodejs/lib/email/base-email-service.ts b/backend/compact-connect/lambdas/nodejs/lib/email/base-email-service.ts index de04bbdf6f..61bda5be31 100644 --- a/backend/compact-connect/lambdas/nodejs/lib/email/base-email-service.ts +++ b/backend/compact-connect/lambdas/nodejs/lib/email/base-email-service.ts @@ -67,6 +67,19 @@ export abstract class BaseEmailService { return `${environmentVariableService.getUiBasePathUrl()}/img/email`; } + protected maskEmail(email: string): string { + const at = email.indexOf('@'); + if (at <= 0) { + return '***'; + } + + return `${email[0]}***${email.slice(at)}`; + } + + protected maskEmails(emails: string[]): string[] { + return emails.map((email) => this.maskEmail(email)); + } + protected async sendEmail({ htmlContent, subject, recipients, errorMessage }: {htmlContent: string, subject: string, recipients: string[], errorMessage: string}) { try { diff --git a/backend/compact-connect/lambdas/nodejs/lib/email/email-notification-service.ts b/backend/compact-connect/lambdas/nodejs/lib/email/email-notification-service.ts index 73881d02d3..2ba4c8a34e 100644 --- a/backend/compact-connect/lambdas/nodejs/lib/email/email-notification-service.ts +++ b/backend/compact-connect/lambdas/nodejs/lib/email/email-notification-service.ts @@ -348,13 +348,14 @@ export class EmailNotificationService extends BaseEmailService { }[], specificEmails: string[] = [] ): Promise { - this.logger.info('Sending provider privilege purchase notification email', { providerEmail: specificEmails[0] }); - + const recipients = specificEmails; - + if (recipients.length === 0) { throw new Error(`No recipients found`); } + + this.logger.info('Sending provider privilege purchase notification email', { providerEmail: this.maskEmail(specificEmails[0]) }); const emailContent = this.getNewEmailTemplate(); const headerText = `Privilege Purchase Confirmation`; @@ -405,7 +406,10 @@ export class EmailNotificationService extends BaseEmailService { compact: string, specificEmails: string[] = [] ): Promise { - this.logger.info('Sending multiple registration attempt notification email', { compact: compact, recipients: specificEmails }); + this.logger.info('Sending multiple registration attempt notification email', { + compact: compact, + recipients: this.maskEmails(specificEmails), + }); const recipients = specificEmails; @@ -437,7 +441,10 @@ export class EmailNotificationService extends BaseEmailService { compact: string, specificEmails: string[] = [] ): Promise { - this.logger.info('Sending ssn correction reregistration notification email', { compact: compact, recipients: specificEmails }); + this.logger.info('Sending ssn correction reregistration notification email', { + compact: compact, + recipients: this.maskEmails(specificEmails), + }); const recipients = specificEmails; @@ -470,7 +477,10 @@ export class EmailNotificationService extends BaseEmailService { providerEmail: string, verificationCode: string ): Promise { - this.logger.info('Sending provider email verification code', { compact: compact, providerEmail: providerEmail }); + this.logger.info('Sending provider email verification code', { + compact: compact, + providerEmail: this.maskEmail(providerEmail), + }); const recipients = [providerEmail]; @@ -498,7 +508,10 @@ export class EmailNotificationService extends BaseEmailService { oldEmailAddress: string, newEmailAddress: string ): Promise { - this.logger.info('Sending provider email change notification', { compact: compact, oldEmailAddress: oldEmailAddress }); + this.logger.info('Sending provider email change notification', { + compact: compact, + oldEmailAddress: this.maskEmail(oldEmailAddress), + }); const recipients = [oldEmailAddress]; diff --git a/backend/compact-connect/lambdas/nodejs/tests/lib/email/base-email-service.test.ts b/backend/compact-connect/lambdas/nodejs/tests/lib/email/base-email-service.test.ts index 0a064a2336..8de3d84c1a 100644 --- a/backend/compact-connect/lambdas/nodejs/tests/lib/email/base-email-service.test.ts +++ b/backend/compact-connect/lambdas/nodejs/tests/lib/email/base-email-service.test.ts @@ -25,6 +25,16 @@ class TestEmailService extends BaseEmailService { } } +class MaskingTestEmailService extends BaseEmailService { + public testMaskEmail(email: string): string { + return this.maskEmail(email); + } + + public testMaskEmails(emails: string[]): string[] { + return this.maskEmails(emails); + } +} + describe('BaseEmailService Environment Banner', () => { let emailService: TestEmailService; let mockSESClient: ReturnType; @@ -113,3 +123,32 @@ describe('BaseEmailService Environment Banner', () => { testEnvironment(undefined, false, 'should NOT include environment banner and footer when environment name is undefined'); }); }); + +describe('BaseEmailService email masking', () => { + let maskingService: MaskingTestEmailService; + + beforeEach(() => { + maskingService = new MaskingTestEmailService({ + logger: new Logger({ serviceName: 'test' }), + sesClient: asSESClient(mockClient(SESv2Client)), + s3Client: asS3Client(mockClient(S3Client)), + compactConfigurationClient: {} as any, + jurisdictionClient: {} as any + }); + }); + + it('masks the local part of a valid email address', () => { + expect(maskingService.testMaskEmail('user@example.com')).toBe('u***@example.com'); + }); + + it('returns a placeholder for invalid email addresses', () => { + expect(maskingService.testMaskEmail('invalid-email')).toBe('***'); + }); + + it('masks each address in a recipient list', () => { + expect(maskingService.testMaskEmails(['user@example.com', 'admin@state.gov'])).toEqual([ + 'u***@example.com', + 'a***@state.gov' + ]); + }); +}); From 6dfbf4c1ba85f6b34faf6d6d3f6cc4ba1dd5accf Mon Sep 17 00:00:00 2001 From: Landon Shumway Date: Thu, 9 Jul 2026 15:48:34 -0700 Subject: [PATCH 12/41] node linter fixes --- .../lambdas/nodejs/lib/email/base-email-service.ts | 1 + .../lambdas/nodejs/tests/lib/email/base-email-service.test.ts | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/backend/compact-connect/lambdas/nodejs/lib/email/base-email-service.ts b/backend/compact-connect/lambdas/nodejs/lib/email/base-email-service.ts index 61bda5be31..14a5938b08 100644 --- a/backend/compact-connect/lambdas/nodejs/lib/email/base-email-service.ts +++ b/backend/compact-connect/lambdas/nodejs/lib/email/base-email-service.ts @@ -69,6 +69,7 @@ export abstract class BaseEmailService { protected maskEmail(email: string): string { const at = email.indexOf('@'); + if (at <= 0) { return '***'; } diff --git a/backend/compact-connect/lambdas/nodejs/tests/lib/email/base-email-service.test.ts b/backend/compact-connect/lambdas/nodejs/tests/lib/email/base-email-service.test.ts index 8de3d84c1a..b79e8ef80c 100644 --- a/backend/compact-connect/lambdas/nodejs/tests/lib/email/base-email-service.test.ts +++ b/backend/compact-connect/lambdas/nodejs/tests/lib/email/base-email-service.test.ts @@ -4,7 +4,7 @@ import { Logger } from '@aws-lambda-powertools/logger'; import { SESv2Client } from '@aws-sdk/client-sesv2'; import { S3Client } from '@aws-sdk/client-s3'; import { BaseEmailService } from '../../../lib/email/base-email-service'; -import { describe, it, beforeEach, jest } from '@jest/globals'; +import { describe, it, beforeEach, jest, expect } from '@jest/globals'; const asSESClient = (mock: ReturnType) => mock as unknown as SESv2Client; From 2bd5ea7d350ec7b36ab793dcea1c14605142a3d6 Mon Sep 17 00:00:00 2001 From: Landon Shumway Date: Thu, 9 Jul 2026 17:02:44 -0700 Subject: [PATCH 13/41] Break out migration transactions for replay-ability --- .../cc_common/data_model/data_client.py | 126 +++-- .../test_data_client_ssn_correction.py | 478 ++++++++++++++++++ 2 files changed, 559 insertions(+), 45 deletions(-) create mode 100644 backend/compact-connect/lambdas/python/common/tests/function/test_data_client_ssn_correction.py diff --git a/backend/compact-connect/lambdas/python/common/cc_common/data_model/data_client.py b/backend/compact-connect/lambdas/python/common/cc_common/data_model/data_client.py index 8a82d89877..d876a3fed1 100644 --- a/backend/compact-connect/lambdas/python/common/cc_common/data_model/data_client.py +++ b/backend/compact-connect/lambdas/python/common/cc_common/data_model/data_client.py @@ -53,6 +53,9 @@ from cc_common.license_util import LicenseUtility from cc_common.utils import logger_inject_kwargs +# DynamoDB's hard limit on the number of items in a single TransactWriteItems call. +MAX_DYNAMODB_TRANSACTION_ITEMS = 100 + @dataclass class SsnCorrectionMigrationResult: @@ -2940,24 +2943,29 @@ def migrate_provider_for_ssn_correction( target_license = next(record for record in records_to_move if record.type == ProviderRecordType.LICENSE) target_license_key = self._provider_record_key(target_license) - transaction_items = [] - - # 1. The concurrency fence: a conditioned write against the old top-level provider record, executed in - # the first batch so a competing migration fails before writing anything - if old_provider_data is not None: - transaction_items.append( - self._build_conditioned_old_provider_transaction_item( - old_provider_data=old_provider_data, - old_provider_records=old_provider_records, - full_teardown=full_teardown, - jurisdiction=jurisdiction, - license_type=license_type, - ) - ) - - # 2. Re-keyed puts for every migrated record. The targeted license also picks up the corrected - # ssnLastFour, and military affiliation records pick up document keys under the new provider id, so - # the new partition is internally consistent. The caller is responsible for moving the S3 objects + # The migration's writes are grouped so they can be committed atomically when small, and replay-safely + # when large. Three groups are built: + # + # creates - put every migrated record (and the new top-level provider record) under the new provider + # id. Idempotent puts (stable pk/sk), so re-running them on replay is harmless. + # deletes - delete the moved records from the old provider, EXCEPT the target license and the old + # top-level provider record. Deleting an already-deleted item is a no-op, so re-running + # these on replay is also harmless. + # final - the ssnCorrection provider-update record, the conditioned teardown/repopulation of the + # old top-level provider record (the concurrency fence), and the target license delete. + # + # When everything fits in one DynamoDB transaction it is committed atomically (all-or-nothing, so + # there is no partial-write replay window). Otherwise the groups run as ordered phases: creates, then + # deletes, then the atomic `final` group. Keeping the old provider record and target license together + # in the atomic `final` group is what makes the large-migration case replay-safe: until it commits, a + # replay always finds both records, so it can re-read the old provider (including its registered email) + # and reliably drive the Cognito-deletion / re-registration path. The idempotency guard above + # short-circuits once the target license is gone (the final group committed). + + # creates: the migrated records + the new top-level provider record under the new provider id. The + # targeted license picks up the corrected ssnLastFour, and military affiliation records pick up + # document keys under the new provider id's keyspace, so the new partition is internally consistent. + create_transaction_items = [] rekeyed_target_license = None rekeyed_privileges = [] for record in records_to_move: @@ -2976,17 +2984,32 @@ def migrate_provider_for_ssn_correction( rekeyed_target_license = rekeyed_record elif record.type == ProviderRecordType.PRIVILEGE: rekeyed_privileges.append(rekeyed_record) - transaction_items.append(self._build_put_transaction_item(rekeyed_record)) + create_transaction_items.append(self._build_put_transaction_item(rekeyed_record)) - # 3. Deletes for the moved records, except the targeted license (deleted last) and the top-level - # provider record (handled by the fence above) - for record in records_to_move: - if record is target_license: - continue - transaction_items.append(self._build_delete_transaction_item(self._provider_record_key(record))) + # Create a top-level provider record for the new provider only if it does not already have one; a + # pre-existing record is never modified + new_provider_record = self._build_new_provider_record_if_absent( + compact=compact, + new_provider_id=new_provider_id, + rekeyed_target_license=rekeyed_target_license, + rekeyed_privileges=rekeyed_privileges, + ) + if new_provider_record is not None: + create_transaction_items.append(self._build_put_transaction_item(new_provider_record)) + + # deletes: the moved records on the old provider, except the target license and the top-level provider + # record (both handled in the final group). + delete_transaction_items = [ + self._build_delete_transaction_item(self._provider_record_key(record)) + for record in records_to_move + if record is not target_license + ] - # 4. The ssnCorrection provider update record, written on the new provider. Skipped on a replay that - # lost the old provider record: the run that deleted it already wrote this record + # final: bounded to at most three items (ssnCorrection put, old provider fence, target license delete). + # On a defensive replay that already lost the old provider record, old_provider_data is None: the + # ssnCorrection record and fence are skipped (the run that deleted the record already wrote the + # ssnCorrection), leaving only the target license delete. + final_transaction_items = [] if old_provider_data is not None: ssn_correction_update = ProviderUpdateData.create_new( { @@ -2999,23 +3022,37 @@ def migrate_provider_for_ssn_correction( 'updatedValues': {'ssnLastFour': new_ssn_last_four}, } ) - transaction_items.append(self._build_put_transaction_item(ssn_correction_update)) - - # 5. Create a top-level provider record for the new provider only if it does not already have one; a - # pre-existing record is never modified - new_provider_record = self._build_new_provider_record_if_absent( - compact=compact, - new_provider_id=new_provider_id, - rekeyed_target_license=rekeyed_target_license, - rekeyed_privileges=rekeyed_privileges, - ) - if new_provider_record is not None: - transaction_items.append(self._build_put_transaction_item(new_provider_record)) - - # 6. The targeted license delete goes last: until it commits, a replay of this migration re-runs in full - transaction_items.append(self._build_delete_transaction_item(target_license_key)) + final_transaction_items.append(self._build_put_transaction_item(ssn_correction_update)) + final_transaction_items.append( + self._build_conditioned_old_provider_transaction_item( + old_provider_data=old_provider_data, + old_provider_records=old_provider_records, + full_teardown=full_teardown, + jurisdiction=jurisdiction, + license_type=license_type, + ) + ) + final_transaction_items.append(self._build_delete_transaction_item(target_license_key)) - self._execute_batched_transactions(transaction_items) + all_transaction_items = [ + *create_transaction_items, + *delete_transaction_items, + *final_transaction_items, + ] + if len(all_transaction_items) <= MAX_DYNAMODB_TRANSACTION_ITEMS: + # Small migration: commit everything as one all-or-nothing transaction, with no cross-transaction + # replay window to reason about. The fence's dateOfUpdate condition failing rolls the whole + # transaction back and raises for SQS retry. + self._execute_batched_transactions(all_transaction_items) + else: + # Large migration: the operations cannot fit in a single atomic transaction, so run them as + # replay-safe phases. The final group is a single atomic transaction (<= 3 items) that can never + # split across a batch boundary, so the old provider record and target license are always torn + # down together. The fence's dateOfUpdate condition failing raises for SQS retry; the retry + # re-reads current state and takes the now-correct branch. + self._execute_batched_transactions(create_transaction_items) + self._execute_batched_transactions(delete_transaction_items) + self._execute_batched_transactions(final_transaction_items) return SsnCorrectionMigrationResult( migration_performed=True, @@ -3186,8 +3223,7 @@ def _execute_batched_transactions(self, transaction_items: list[dict]) -> None: logger.info('Executing batched transactions', total_items=len(transaction_items)) - # DynamoDB transaction limit is 100 items - batch_size = 100 + batch_size = MAX_DYNAMODB_TRANSACTION_ITEMS processed_batches = [] try: diff --git a/backend/compact-connect/lambdas/python/common/tests/function/test_data_client_ssn_correction.py b/backend/compact-connect/lambdas/python/common/tests/function/test_data_client_ssn_correction.py new file mode 100644 index 0000000000..9965630d77 --- /dev/null +++ b/backend/compact-connect/lambdas/python/common/tests/function/test_data_client_ssn_correction.py @@ -0,0 +1,478 @@ +# ruff: noqa: F403, F405 star import of test constants file +from datetime import date, datetime +from unittest.mock import patch + +from boto3.dynamodb.conditions import Key +from cc_common.exceptions import CCInternalException +from common_test.test_constants import * +from moto import mock_aws + +from tests.function import TstFunction + +# The provider id the corrected SSN resolves to. The old (incorrect-SSN) provider uses the +# generator default provider id. +NEW_PROVIDER_ID = 'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee' +NEW_SSN_LAST_FOUR = '6789' +# aslp compact license type that is not the default 'speech-language pathologist' +OTHER_LICENSE_TYPE = 'audiologist' + + +@mock_aws +@patch('cc_common.config._Config.current_standard_datetime', datetime.fromisoformat('2024-11-08T23:59:59+00:00')) +class TestMigrateProviderForSsnCorrection(TstFunction): + """Function tests for DataClient.migrate_provider_for_ssn_correction.""" + + def _migrate(self, **overrides): + kwargs = { + 'compact': DEFAULT_COMPACT, + 'previous_provider_id': DEFAULT_PROVIDER_ID, + 'new_provider_id': NEW_PROVIDER_ID, + 'jurisdiction': DEFAULT_LICENSE_JURISDICTION, + 'license_type': DEFAULT_LICENSE_TYPE, + 'new_ssn_last_four': NEW_SSN_LAST_FOUR, + } + kwargs.update(overrides) + return self.config.data_client.migrate_provider_for_ssn_correction(**kwargs) + + def _get_all_records_for_provider(self, provider_id: str) -> list[dict]: + return self.config.provider_table.query( + KeyConditionExpression=Key('pk').eq(f'{DEFAULT_COMPACT}#PROVIDER#{provider_id}') + )['Items'] + + def _get_records_of_type(self, provider_id: str, record_type: str) -> list[dict]: + return [record for record in self._get_all_records_for_provider(provider_id) if record['type'] == record_type] + + def _put_full_old_provider_records(self): + """Store a set of records for the old provider covering every migratable record type: the top-level + provider record, a license with a dependent privilege, license/privilege update history, adverse + actions and investigations against BOTH the license and the privilege, and the person-level military + affiliation and provider update records. + """ + self.test_data_generator.put_default_provider_record_in_provider_table() + self.test_data_generator.put_default_license_record_in_provider_table() + self.test_data_generator.put_default_privilege_record_in_provider_table() + self.test_data_generator.put_default_license_update_record_in_provider_table() + self.test_data_generator.put_default_privilege_update_record_in_provider_table() + # adverse actions against the license (jurisdiction oh) and the privilege (jurisdiction ne); the + # generator default is privilege-scoped, so the license-scoped one is added explicitly + self.test_data_generator.put_default_adverse_action_record_in_provider_table( + {'actionAgainst': 'license', 'jurisdiction': DEFAULT_LICENSE_JURISDICTION} + ) + self.test_data_generator.put_default_adverse_action_record_in_provider_table( + {'actionAgainst': 'privilege', 'jurisdiction': DEFAULT_PRIVILEGE_JURISDICTION} + ) + # investigations against the license and against the privilege + self.test_data_generator.put_default_investigation_record_in_provider_table( + {'investigationAgainst': 'license', 'jurisdiction': DEFAULT_LICENSE_JURISDICTION} + ) + self.test_data_generator.put_default_investigation_record_in_provider_table( + {'investigationAgainst': 'privilege', 'jurisdiction': DEFAULT_PRIVILEGE_JURISDICTION} + ) + self.test_data_generator.put_default_military_affiliation_in_provider_table() + self.test_data_generator.put_default_provider_update_record_in_provider_table() + + def test_full_teardown_migrates_all_records_and_empties_old_partition(self): + self._put_full_old_provider_records() + + result = self._migrate() + + self.assertTrue(result.migration_performed) + self.assertTrue(result.full_teardown) + self.assertEqual(DEFAULT_REGISTERED_EMAIL_ADDRESS, result.old_provider_registered_email) + + # the old partition must be completely empty + self.assertEqual([], self._get_all_records_for_provider(DEFAULT_PROVIDER_ID)) + + # the license-associated records and the person-level military affiliation record must now exist + # under the new provider id (two adverse actions and two investigations: one against the license and + # one against the privilege) + expected_counts_by_record_type = { + 'provider': 1, + 'license': 1, + 'privilege': 1, + 'licenseUpdate': 1, + 'privilegeUpdate': 1, + 'adverseAction': 2, + 'investigation': 2, + 'militaryAffiliation': 1, + } + for record_type, expected_count in expected_counts_by_record_type.items(): + records = self._get_records_of_type(NEW_PROVIDER_ID, record_type) + self.assertEqual( + expected_count, len(records), f'expected {expected_count} {record_type} record(s) on the new provider' + ) + for record in records: + self.assertEqual(NEW_PROVIDER_ID, record['providerId']) + + # the moved provider update history plus the new ssnCorrection update + provider_updates = self._get_records_of_type(NEW_PROVIDER_ID, 'providerUpdate') + self.assertEqual(2, len(provider_updates)) + self.assertEqual(1, len([update for update in provider_updates if update['updateType'] == 'ssnCorrection'])) + + # the migrated license must carry the corrected ssnLastFour + migrated_license = self._get_records_of_type(NEW_PROVIDER_ID, 'license')[0] + self.assertEqual(NEW_SSN_LAST_FOUR, migrated_license['ssnLastFour']) + + # the migrated military affiliation record must reference document keys under the new provider id. + # (The caller moves the underlying S3 objects by listing the old provider id's keyspace directly, not + # from this DynamoDB record, so it is not reflected in the migration result.) + migrated_military = self._get_records_of_type(NEW_PROVIDER_ID, 'militaryAffiliation')[0] + for document_key in migrated_military['documentKeys']: + self.assertIn(NEW_PROVIDER_ID, document_key) + self.assertNotIn(DEFAULT_PROVIDER_ID, document_key) + + def test_partial_migration_when_another_license_type_remains_in_same_state(self): + self._put_full_old_provider_records() + # a second license of another type in the same jurisdiction, with no privileges + self.test_data_generator.put_default_license_record_in_provider_table({'licenseType': OTHER_LICENSE_TYPE}) + + result = self._migrate() + + self.assertTrue(result.migration_performed) + self.assertFalse(result.full_teardown) + self.assertIsNone(result.old_provider_registered_email) + + # the targeted license and its dependent records moved off the old provider + old_licenses = self._get_records_of_type(DEFAULT_PROVIDER_ID, 'license') + self.assertEqual(1, len(old_licenses)) + self.assertEqual(OTHER_LICENSE_TYPE, old_licenses[0]['licenseType']) + self.assertEqual([], self._get_records_of_type(DEFAULT_PROVIDER_ID, 'privilege')) + self.assertEqual([], self._get_records_of_type(DEFAULT_PROVIDER_ID, 'licenseUpdate')) + self.assertEqual([], self._get_records_of_type(DEFAULT_PROVIDER_ID, 'privilegeUpdate')) + self.assertEqual([], self._get_records_of_type(DEFAULT_PROVIDER_ID, 'adverseAction')) + self.assertEqual([], self._get_records_of_type(DEFAULT_PROVIDER_ID, 'investigation')) + + # the old provider remains, repopulated from the remaining license, with its now-empty + # privilege jurisdictions cleared + old_provider_records = self._get_records_of_type(DEFAULT_PROVIDER_ID, 'provider') + self.assertEqual(1, len(old_provider_records)) + self.assertNotIn('privilegeJurisdictions', old_provider_records[0]) + + # person-level records stay with the old provider and are never copied to the new one + self.assertEqual(1, len(self._get_records_of_type(DEFAULT_PROVIDER_ID, 'militaryAffiliation'))) + self.assertEqual(1, len(self._get_records_of_type(DEFAULT_PROVIDER_ID, 'providerUpdate'))) + self.assertEqual([], self._get_records_of_type(NEW_PROVIDER_ID, 'militaryAffiliation')) + + # new provider has the migrated license/privilege, the ssnCorrection update, and a newly-created + # top-level provider record + self.assertEqual(1, len(self._get_records_of_type(NEW_PROVIDER_ID, 'license'))) + self.assertEqual(1, len(self._get_records_of_type(NEW_PROVIDER_ID, 'privilege'))) + new_provider_records = self._get_records_of_type(NEW_PROVIDER_ID, 'provider') + self.assertEqual(1, len(new_provider_records)) + new_provider_updates = self._get_records_of_type(NEW_PROVIDER_ID, 'providerUpdate') + self.assertEqual(1, len(new_provider_updates)) + self.assertEqual('ssnCorrection', new_provider_updates[0]['updateType']) + + def test_partial_migration_when_license_in_other_jurisdiction_remains(self): + self.test_data_generator.put_default_provider_record_in_provider_table() + self.test_data_generator.put_default_license_record_in_provider_table() + self.test_data_generator.put_default_license_record_in_provider_table({'jurisdiction': 'ky'}) + + result = self._migrate() + + self.assertTrue(result.migration_performed) + self.assertFalse(result.full_teardown) + + # the ky license remains under the old provider, whose top-level record now reflects it + old_licenses = self._get_records_of_type(DEFAULT_PROVIDER_ID, 'license') + self.assertEqual(1, len(old_licenses)) + self.assertEqual('ky', old_licenses[0]['jurisdiction']) + old_provider_record = self._get_records_of_type(DEFAULT_PROVIDER_ID, 'provider')[0] + self.assertEqual('ky', old_provider_record['licenseJurisdiction']) + + # the oh license moved to the new provider + new_licenses = self._get_records_of_type(NEW_PROVIDER_ID, 'license') + self.assertEqual(1, len(new_licenses)) + self.assertEqual(DEFAULT_LICENSE_JURISDICTION, new_licenses[0]['jurisdiction']) + + def test_migration_leaves_new_provider_pre_existing_records_untouched(self): + # the new provider already has records from another state + pre_existing_provider = self.test_data_generator.put_default_provider_record_in_provider_table( + {'providerId': NEW_PROVIDER_ID, 'licenseJurisdiction': 'ky', 'privilegeJurisdictions': set()} + ) + pre_existing_license = self.test_data_generator.put_default_license_record_in_provider_table( + {'providerId': NEW_PROVIDER_ID, 'jurisdiction': 'ky'} + ) + + # the old provider has the corrected license + self.test_data_generator.put_default_provider_record_in_provider_table() + self.test_data_generator.put_default_license_record_in_provider_table( + {'dateOfIssuance': date.fromisoformat('2024-01-01')} + ) + + result = self._migrate() + + self.assertTrue(result.migration_performed) + self.assertTrue(result.full_teardown) + + # both licenses now live under the new provider; the pre-existing one is untouched + new_licenses = self._get_records_of_type(NEW_PROVIDER_ID, 'license') + self.assertEqual(2, len(new_licenses)) + ky_license = next(record for record in new_licenses if record['jurisdiction'] == 'ky') + expected_ky_license = pre_existing_license.serialize_to_database_record() + self.assertEqual(expected_ky_license, ky_license) + + # the pre-existing top-level provider record is left completely untouched: the migration only + # creates one when none exists + new_provider_record = self._get_records_of_type(NEW_PROVIDER_ID, 'provider')[0] + self.assertEqual(pre_existing_provider.serialize_to_database_record(), new_provider_record) + + def test_no_op_when_old_provider_has_no_matching_license(self): + self.test_data_generator.put_default_provider_record_in_provider_table() + self.test_data_generator.put_default_license_record_in_provider_table({'licenseType': OTHER_LICENSE_TYPE}) + + old_records_before = self._get_all_records_for_provider(DEFAULT_PROVIDER_ID) + + result = self._migrate() + + self.assertFalse(result.migration_performed) + self.assertEqual(old_records_before, self._get_all_records_for_provider(DEFAULT_PROVIDER_ID)) + self.assertEqual([], self._get_all_records_for_provider(NEW_PROVIDER_ID)) + + def test_no_op_when_old_provider_partition_is_empty(self): + # the previousSSN resolved to a freshly-created provider id with no records (spurious mapping) + result = self._migrate() + + self.assertFalse(result.migration_performed) + self.assertEqual([], self._get_all_records_for_provider(NEW_PROVIDER_ID)) + + def test_migration_is_idempotent_on_replay(self): + self._put_full_old_provider_records() + + first_result = self._migrate() + self.assertTrue(first_result.migration_performed) + + new_records_after_first_run = self._get_all_records_for_provider(NEW_PROVIDER_ID) + + # replaying the same message must be a no-op + second_result = self._migrate() + + self.assertFalse(second_result.migration_performed) + self.assertEqual([], self._get_all_records_for_provider(DEFAULT_PROVIDER_ID)) + self.assertEqual(new_records_after_first_run, self._get_all_records_for_provider(NEW_PROVIDER_ID)) + + def test_update_record_migration_is_idempotent_across_replays(self): + """Update records (license/privilege/provider) put a change-hash in their sk that is derived from the + `previous` snapshot, which includes providerId. Re-keying therefore changes the hash relative to the + old record, but the re-keying is a deterministic pure function: re-running the create phase against the + same old records reproduces the exact same sks, so replays never accumulate duplicate update records + under the new provider id. + """ + self._put_full_old_provider_records() + + real_transact_write_items = self.config.dynamodb_client.transact_write_items + + def _fail_on_any_delete(**kwargs): + # let the create phase (pure puts) commit, then fail the delete phase so the old records stay in + # place and the create phase re-runs against unchanged data on the next replay + if any('Delete' in item for item in kwargs['TransactItems']): + raise RuntimeError('simulated delete-phase failure') + return real_transact_write_items(**kwargs) + + update_record_types = ('licenseUpdate', 'privilegeUpdate', 'providerUpdate') + + def _update_record_sks_under_new_provider(): + return sorted( + record['sk'] + for record in self._get_all_records_for_provider(NEW_PROVIDER_ID) + if record['type'] in update_record_types + ) + + sks_per_attempt = [] + for _ in range(3): + with ( + patch('cc_common.data_model.data_client.MAX_DYNAMODB_TRANSACTION_ITEMS', 3), + patch.object(self.config.dynamodb_client, 'transact_write_items', side_effect=_fail_on_any_delete), + ): + with self.assertRaises(CCInternalException): + self._migrate() + sks_per_attempt.append(_update_record_sks_under_new_provider()) + + # every replay reproduced the exact same set of update-record sks, so no duplicates accumulated + self.assertEqual(sks_per_attempt[0], sks_per_attempt[1]) + self.assertEqual(sks_per_attempt[0], sks_per_attempt[2]) + # one migrated record of each update type was exercised (no ssnCorrection yet, since it is written in + # the final transaction that never commits here) + self.assertEqual(3, len(sks_per_attempt[0])) + + def test_ssn_correction_provider_update_content(self): + self._put_full_old_provider_records() + + self._migrate() + + provider_updates = self._get_records_of_type(NEW_PROVIDER_ID, 'providerUpdate') + ssn_correction_updates = [update for update in provider_updates if update['updateType'] == 'ssnCorrection'] + self.assertEqual(1, len(ssn_correction_updates)) + ssn_correction = ssn_correction_updates[0] + + self.assertEqual(NEW_PROVIDER_ID, ssn_correction['providerId']) + # previous holds the snapshot of the old provider record, including the old ssnLastFour + self.assertEqual(DEFAULT_SSN_LAST_FOUR, ssn_correction['previous']['ssnLastFour']) + self.assertEqual(NEW_SSN_LAST_FOUR, ssn_correction['updatedValues']['ssnLastFour']) + + def test_migration_raises_when_old_provider_record_modified_concurrently(self): + self._put_full_old_provider_records() + + # capture the state a competing migration would have read + stale_old_records = self.config.data_client.get_provider_user_records( + compact=DEFAULT_COMPACT, + provider_id=DEFAULT_PROVIDER_ID, + ) + + # another migration for this provider commits first, refreshing the provider record's dateOfUpdate + self.test_data_generator.put_default_provider_record_in_provider_table( + date_of_update_override='2025-01-01T00:00:00+00:00' + ) + + real_get_provider_user_records = self.config.data_client.get_provider_user_records + + def _stale_read_for_old_provider(*, compact, provider_id, **kwargs): + if str(provider_id) == DEFAULT_PROVIDER_ID: + return stale_old_records + return real_get_provider_user_records(compact=compact, provider_id=provider_id, **kwargs) + + with patch.object( + self.config.data_client, 'get_provider_user_records', side_effect=_stale_read_for_old_provider + ): + with self.assertRaises(CCInternalException): + self._migrate() + + # This migration fits in a single atomic transaction, so the failed fence condition rolls the whole + # transaction back: nothing is written under the new provider, and the old provider is left fully + # intact so the migration can be safely retried. + self.assertEqual([], self._get_all_records_for_provider(NEW_PROVIDER_ID)) + self.assertEqual(1, len(self._get_records_of_type(DEFAULT_PROVIDER_ID, 'provider'))) + self.assertEqual(1, len(self._get_records_of_type(DEFAULT_PROVIDER_ID, 'license'))) + + @staticmethod + def _operation(item: dict) -> dict: + return next(iter(item.values())) + + @classmethod + def _key(cls, item: dict) -> dict: + operation = cls._operation(item) + return operation.get('Key', operation.get('Item', {})) + + def _spy_on_transactions(self) -> list: + """Patch the dynamodb client to record each transact_write_items call while still executing it.""" + executed_transactions = [] + real_transact_write_items = self.config.dynamodb_client.transact_write_items + + def _spy(**kwargs): + executed_transactions.append(kwargs['TransactItems']) + return real_transact_write_items(**kwargs) + + patcher = patch.object(self.config, 'dynamodb_client') + mock_dynamodb_client = patcher.start() + self.addCleanup(patcher.stop) + mock_dynamodb_client.transact_write_items.side_effect = _spy + return executed_transactions + + def test_small_migration_is_committed_as_single_atomic_transaction(self): + """A migration that fits within the DynamoDB transaction limit must be committed as exactly one + atomic transaction, not split into phases. + """ + self._put_full_old_provider_records() + + executed_transactions = self._spy_on_transactions() + self._migrate() + + self.assertEqual(1, len(executed_transactions)) + + def test_large_migration_creates_before_deletes_and_tears_down_critical_records_atomically(self): + """When a migration exceeds the DynamoDB transaction limit it must (a) create every new record before + deleting any old record, and (b) tear down the old top-level provider record (the fence) and the + target license together in a single atomic final transaction. This keeps replay safe: until the final + transaction commits, both critical records survive for the replay's idempotency guard to find, and the + old provider stays readable for the Cognito/email path. + """ + self._put_full_old_provider_records() + + executed_transactions = self._spy_on_transactions() + # force the multi-transaction path without needing to generate >100 records + with patch('cc_common.data_model.data_client.MAX_DYNAMODB_TRANSACTION_ITEMS', 3): + self._migrate() + + old_provider_pk = f'{DEFAULT_COMPACT}#PROVIDER#{DEFAULT_PROVIDER_ID}' + new_provider_pk = f'{DEFAULT_COMPACT}#PROVIDER#{NEW_PROVIDER_ID}' + + # the create phase (transactions that only put records under the new provider) must fully precede the + # delete phase (transactions that only delete records from the old provider); the mixed final + # transaction is neither and is checked separately below + create_transaction_indexes = [ + index + for index, transaction in enumerate(executed_transactions) + if all('Put' in item and self._key(item)['pk']['S'] == new_provider_pk for item in transaction) + ] + delete_transaction_indexes = [ + index + for index, transaction in enumerate(executed_transactions) + if all('Delete' in item and self._key(item)['pk']['S'] == old_provider_pk for item in transaction) + ] + self.assertTrue(create_transaction_indexes) + self.assertTrue(delete_transaction_indexes) + self.assertLess(max(create_transaction_indexes), min(delete_transaction_indexes)) + + # the final transaction is a single atomic transaction that both tears down the old top-level provider + # record (conditioned on its dateOfUpdate) and deletes the target license + final_transaction = executed_transactions[-1] + fence_item = next( + item + for item in final_transaction + if self._key(item)['pk']['S'] == old_provider_pk + and self._key(item)['sk']['S'] == f'{DEFAULT_COMPACT}#PROVIDER' + ) + self.assertIn('dateOfUpdate', self._operation(fence_item)['ConditionExpression']) + target_license_delete = next( + item + for item in final_transaction + if 'Delete' in item + and self._key(item)['pk']['S'] == old_provider_pk + and 'license/' in self._key(item)['sk']['S'] + ) + self.assertIn('Delete', target_license_delete) + + def test_large_migration_replays_cleanly_after_final_transaction_failure(self): + """The core replay guarantee for large migrations: if the atomic final transaction fails after the + create/delete phases have committed, the old top-level provider record and the target license must + survive, so a replay can re-read the old provider (including its registered email) and complete the + teardown — including the Cognito/email path that depends on that email. + """ + self._put_full_old_provider_records() + + real_transact_write_items = self.config.dynamodb_client.transact_write_items + + def _fail_on_target_license_delete(**kwargs): + # the final transaction is the one that deletes the target license off the old provider + deletes_target_license = any( + 'Delete' in item + and self._key(item)['pk']['S'] == f'{DEFAULT_COMPACT}#PROVIDER#{DEFAULT_PROVIDER_ID}' + and 'license/' in self._key(item)['sk']['S'] + for item in kwargs['TransactItems'] + ) + if deletes_target_license: + raise RuntimeError('simulated failure committing the final transaction') + return real_transact_write_items(**kwargs) + + # first attempt: force the phased path, and make the final transaction fail + with ( + patch('cc_common.data_model.data_client.MAX_DYNAMODB_TRANSACTION_ITEMS', 3), + patch.object( + self.config.dynamodb_client, 'transact_write_items', side_effect=_fail_on_target_license_delete + ), + ): + with self.assertRaises(CCInternalException): + self._migrate() + + # the old top-level provider record and target license must still be present for the replay + self.assertEqual(1, len(self._get_records_of_type(DEFAULT_PROVIDER_ID, 'provider'))) + self.assertEqual(1, len(self._get_records_of_type(DEFAULT_PROVIDER_ID, 'license'))) + + # replay: succeeds, tears the old provider down, and still reports the registered email for the + # Cognito/email path (the bug this ordering fixes was losing that email on replay) + result = self._migrate() + + self.assertTrue(result.migration_performed) + self.assertTrue(result.full_teardown) + self.assertEqual(DEFAULT_REGISTERED_EMAIL_ADDRESS, result.old_provider_registered_email) + self.assertEqual([], self._get_all_records_for_provider(DEFAULT_PROVIDER_ID)) From 010d093ad5a7739e3f285b33bb02084cf76bf8eb Mon Sep 17 00:00:00 2001 From: Landon Shumway Date: Mon, 13 Jul 2026 09:26:29 -0700 Subject: [PATCH 14/41] rename from teardown to migration --- .../cc_common/data_model/data_client.py | 46 ++++++++++--------- .../test_data_client_ssn_correction.py | 14 +++--- .../provider-data-v1/handlers/ingest.py | 16 +++---- .../function/test_handlers/test_ingest.py | 14 +++--- .../compact-connect/stacks/ingest_stack.py | 2 +- 5 files changed, 47 insertions(+), 45 deletions(-) diff --git a/backend/compact-connect/lambdas/python/common/cc_common/data_model/data_client.py b/backend/compact-connect/lambdas/python/common/cc_common/data_model/data_client.py index d876a3fed1..7634cd3f9a 100644 --- a/backend/compact-connect/lambdas/python/common/cc_common/data_model/data_client.py +++ b/backend/compact-connect/lambdas/python/common/cc_common/data_model/data_client.py @@ -64,14 +64,14 @@ class SsnCorrectionMigrationResult: :param migration_performed: False when there was nothing to migrate (spurious previousSSN or a replay of an already-completed migration) - :param full_teardown: True when the corrected license was the old provider's only license, so the old + :param full_migration: True when the corrected license was the old provider's only license, so the old provider was deleted entirely. The caller must delete the old Cognito user and notify the practitioner. :param old_provider_registered_email: The old provider's registered email address, present only on a full - teardown of a registered provider + migration of a registered provider """ migration_performed: bool - full_teardown: bool = False + full_migration: bool = False old_provider_registered_email: str | None = None @@ -2877,7 +2877,7 @@ def migrate_provider_for_ssn_correction( history records are always moved to the new provider id. What happens to the rest of the old provider depends on whether the corrected license was its only license record: - - Full teardown (sole license): the person-level records (military affiliations, provider update + - Full migration (sole license): the person-level records (military affiliations, provider update history) are moved to the new provider id as well, with military document keys re-pointed at the new provider id's keyspace, and the old provider's partition, including its top-level provider record, is deleted. The caller is responsible for moving the practitioner's S3 documents (by listing the old @@ -2926,18 +2926,19 @@ def migrate_provider_for_ssn_correction( try: old_provider_data = old_provider_records.get_provider_record() except CCInternalException: - # The top-level provider record was already deleted by a partially-completed teardown that is now - # being replayed; continue the migration without the concurrency fence + # The top-level provider record was already deleted by a partially-completed full migration that + # is now being replayed; continue the migration without the concurrency fence logger.warning('Old provider record not found; continuing replay of a partially-completed migration') old_provider_data = None - # The corrected license was the old provider's only license: tear the old provider down entirely - full_teardown = len(old_provider_records.get_license_records()) == 1 + # The corrected license was the old provider's only license: this is a full migration of the old + # provider (everything moves, and the old provider is deleted), as opposed to a partial migration + full_migration = len(old_provider_records.get_license_records()) == 1 # Person-level records (military affiliations, provider update history) follow the practitioner only - # when the old provider is torn down entirely; on a partial migration they stay with the old provider, - # which still represents them for their remaining licenses - person_level_records = old_provider_records.get_person_level_records() if full_teardown else [] + # on a full migration; on a partial migration they stay with the old provider, which still represents + # them for their remaining licenses + person_level_records = old_provider_records.get_person_level_records() if full_migration else [] records_to_move = [*records_to_move, *person_level_records] target_license = next(record for record in records_to_move if record.type == ProviderRecordType.LICENSE) @@ -2951,8 +2952,9 @@ def migrate_provider_for_ssn_correction( # deletes - delete the moved records from the old provider, EXCEPT the target license and the old # top-level provider record. Deleting an already-deleted item is a no-op, so re-running # these on replay is also harmless. - # final - the ssnCorrection provider-update record, the conditioned teardown/repopulation of the - # old top-level provider record (the concurrency fence), and the target license delete. + # final - the ssnCorrection provider-update record, the conditioned deletion (full migration) or + # repopulation (partial migration) of the old top-level provider record (the concurrency + # fence), and the target license delete. # # When everything fits in one DynamoDB transaction it is committed atomically (all-or-nothing, so # there is no partial-write replay window). Otherwise the groups run as ordered phases: creates, then @@ -3027,7 +3029,7 @@ def migrate_provider_for_ssn_correction( self._build_conditioned_old_provider_transaction_item( old_provider_data=old_provider_data, old_provider_records=old_provider_records, - full_teardown=full_teardown, + full_migration=full_migration, jurisdiction=jurisdiction, license_type=license_type, ) @@ -3056,10 +3058,10 @@ def migrate_provider_for_ssn_correction( return SsnCorrectionMigrationResult( migration_performed=True, - full_teardown=full_teardown, + full_migration=full_migration, old_provider_registered_email=( old_provider_data.to_dict().get('compactConnectRegisteredEmailAddress') - if full_teardown and old_provider_data is not None + if full_migration and old_provider_data is not None else None ), ) @@ -3110,21 +3112,21 @@ def _build_conditioned_old_provider_transaction_item( *, old_provider_data: ProviderData, old_provider_records: ProviderUserRecords, - full_teardown: bool, + full_migration: bool, jurisdiction: str, license_type: str, ) -> dict: """ - Build the write against the old top-level provider record: a delete on full teardown, or a repopulation - from the remaining licenses on a partial migration. Either way the write is conditioned on the - dateOfUpdate read at the start of the migration, so concurrent migrations of the same old provider - serialize via SQS retry instead of both reading the same stale state. + Build the write against the old top-level provider record: a delete on a full migration, or a + repopulation from the remaining licenses on a partial migration. Either way the write is conditioned + on the dateOfUpdate read at the start of the migration, so concurrent migrations of the same old + provider serialize via SQS retry instead of both reading the same stale state. """ condition = { 'ConditionExpression': 'attribute_exists(pk) AND dateOfUpdate = :dateOfUpdate', 'ExpressionAttributeValues': {':dateOfUpdate': {'S': old_provider_data.dateOfUpdate.isoformat()}}, } - if full_teardown: + if full_migration: old_provider_key = self._provider_record_key(old_provider_data) return { 'Delete': { diff --git a/backend/compact-connect/lambdas/python/common/tests/function/test_data_client_ssn_correction.py b/backend/compact-connect/lambdas/python/common/tests/function/test_data_client_ssn_correction.py index 9965630d77..c62679a2a7 100644 --- a/backend/compact-connect/lambdas/python/common/tests/function/test_data_client_ssn_correction.py +++ b/backend/compact-connect/lambdas/python/common/tests/function/test_data_client_ssn_correction.py @@ -71,13 +71,13 @@ def _put_full_old_provider_records(self): self.test_data_generator.put_default_military_affiliation_in_provider_table() self.test_data_generator.put_default_provider_update_record_in_provider_table() - def test_full_teardown_migrates_all_records_and_empties_old_partition(self): + def test_full_migration_moves_all_records_and_empties_old_partition(self): self._put_full_old_provider_records() result = self._migrate() self.assertTrue(result.migration_performed) - self.assertTrue(result.full_teardown) + self.assertTrue(result.full_migration) self.assertEqual(DEFAULT_REGISTERED_EMAIL_ADDRESS, result.old_provider_registered_email) # the old partition must be completely empty @@ -129,7 +129,7 @@ def test_partial_migration_when_another_license_type_remains_in_same_state(self) result = self._migrate() self.assertTrue(result.migration_performed) - self.assertFalse(result.full_teardown) + self.assertFalse(result.full_migration) self.assertIsNone(result.old_provider_registered_email) # the targeted license and its dependent records moved off the old provider @@ -171,7 +171,7 @@ def test_partial_migration_when_license_in_other_jurisdiction_remains(self): result = self._migrate() self.assertTrue(result.migration_performed) - self.assertFalse(result.full_teardown) + self.assertFalse(result.full_migration) # the ky license remains under the old provider, whose top-level record now reflects it old_licenses = self._get_records_of_type(DEFAULT_PROVIDER_ID, 'license') @@ -203,7 +203,7 @@ def test_migration_leaves_new_provider_pre_existing_records_untouched(self): result = self._migrate() self.assertTrue(result.migration_performed) - self.assertTrue(result.full_teardown) + self.assertTrue(result.full_migration) # both licenses now live under the new provider; the pre-existing one is untouched new_licenses = self._get_records_of_type(NEW_PROVIDER_ID, 'license') @@ -436,7 +436,7 @@ def test_large_migration_replays_cleanly_after_final_transaction_failure(self): """The core replay guarantee for large migrations: if the atomic final transaction fails after the create/delete phases have committed, the old top-level provider record and the target license must survive, so a replay can re-read the old provider (including its registered email) and complete the - teardown — including the Cognito/email path that depends on that email. + full migration — including the Cognito/email path that depends on that email. """ self._put_full_old_provider_records() @@ -473,6 +473,6 @@ def _fail_on_target_license_delete(**kwargs): result = self._migrate() self.assertTrue(result.migration_performed) - self.assertTrue(result.full_teardown) + self.assertTrue(result.full_migration) self.assertEqual(DEFAULT_REGISTERED_EMAIL_ADDRESS, result.old_provider_registered_email) self.assertEqual([], self._get_all_records_for_provider(DEFAULT_PROVIDER_ID)) diff --git a/backend/compact-connect/lambdas/python/provider-data-v1/handlers/ingest.py b/backend/compact-connect/lambdas/python/provider-data-v1/handlers/ingest.py index 647aceac5a..dbb2d99473 100644 --- a/backend/compact-connect/lambdas/python/provider-data-v1/handlers/ingest.py +++ b/backend/compact-connect/lambdas/python/provider-data-v1/handlers/ingest.py @@ -384,7 +384,7 @@ def _perform_ssn_correction_migration( """ Orchestrate the migration of a practitioner's records after a state corrected the SSN on a license upload. - The DynamoDB migration runs first; on a full teardown the S3 document move, Cognito user deletion, and + The DynamoDB migration runs first; on a full migration the S3 document move, Cognito user deletion, and re-registration email follow, each idempotent so an SQS retry of a partially-completed migration converges. A concurrency conflict inside the migration raises, letting SQS redeliver the message after the visibility timeout. @@ -403,7 +403,7 @@ def _perform_ssn_correction_migration( logger.info('No records to migrate for previous provider id; proceeding with normal ingest') return - if result.full_teardown: + if result.full_migration: _move_provider_documents_to_new_keyspace( compact=compact, previous_provider_id=previous_provider_id, @@ -448,12 +448,12 @@ def _move_s3_object(*, old_key: str, new_key: str): ) config.s3_client.delete_object(Bucket=config.provider_user_bucket_name, Key=old_key) except ClientError as e: - logger.error( - 'Failed to move provider document to the new keyspace', - old_key=old_key, - new_key=new_key, - error=str(e), - ) + logger.error( + 'Failed to move provider document to the new keyspace', + old_key=old_key, + new_key=new_key, + error=str(e), + ) def _delete_old_cognito_user_and_send_reregistration_email(*, compact: str, old_registered_email: str): diff --git a/backend/compact-connect/lambdas/python/provider-data-v1/tests/function/test_handlers/test_ingest.py b/backend/compact-connect/lambdas/python/provider-data-v1/tests/function/test_handlers/test_ingest.py index 229522d047..1a82a2ff3c 100644 --- a/backend/compact-connect/lambdas/python/provider-data-v1/tests/function/test_handlers/test_ingest.py +++ b/backend/compact-connect/lambdas/python/provider-data-v1/tests/function/test_handlers/test_ingest.py @@ -1010,7 +1010,7 @@ def _get_api_response_snapshot_for_provider(self, provider_id: str) -> dict: ) return json.loads(json.dumps(provider_user_records.generate_api_response_object(), cls=ResponseEncoder)) - def test_full_teardown_migration_moves_records_under_new_provider_id(self): + def test_full_migration_moves_records_under_new_provider_id(self): old_provider_record_items = self._put_old_provider_records() self._create_old_cognito_user() @@ -1067,7 +1067,7 @@ def test_full_teardown_migration_moves_records_under_new_provider_id(self): self._get_api_response_snapshot_for_provider(self.NEW_PROVIDER_ID), ) - def test_full_teardown_migration_deletes_cognito_user(self): + def test_full_migration_deletes_cognito_user(self): self._put_old_provider_records() self._create_old_cognito_user() @@ -1076,7 +1076,7 @@ def test_full_teardown_migration_deletes_cognito_user(self): self.assertFalse(self._when_old_cognito_user_exists()) - def test_full_teardown_migration_sends_reregistration_email(self): + def test_full_migration_sends_reregistration_email(self): self._put_old_provider_records() self._create_old_cognito_user() @@ -1125,7 +1125,7 @@ def test_no_op_migration_still_ingests_license_normally(self): self._mock_send_reregistration_email.assert_not_called() - def test_full_teardown_with_unregistered_old_provider_sends_no_email(self): + def test_full_migration_with_unregistered_old_provider_sends_no_email(self): # the old provider never registered: no Cognito user, no registered email on the provider record self.test_data_generator.put_default_provider_record_in_provider_table(is_registered=False) self.test_data_generator.put_default_license_record_in_provider_table() @@ -1147,7 +1147,7 @@ def _s3_object_body(self, key: str) -> bytes | None: return None raise - def test_full_teardown_migration_moves_military_documents_to_new_provider_keyspace(self): + def test_full_migration_moves_military_documents_to_new_provider_keyspace(self): # the old provider has two military affiliation records, each with a document stored under the old # provider id's keyspace in the provider user bucket self.test_data_generator.put_default_provider_record_in_provider_table( @@ -1197,10 +1197,10 @@ def test_full_teardown_migration_moves_military_documents_to_new_provider_keyspa new_key = old_key.replace(self.OLD_PROVIDER_ID, self.NEW_PROVIDER_ID) self.assertEqual(document_body, self._s3_object_body(new_key)) - def test_full_teardown_migration_moves_all_objects_under_old_provider_keyspace(self): + def test_full_migration_moves_all_objects_under_old_provider_keyspace(self): """The S3 move must be driven by listing the old provider id's keyspace directly, not by walking DynamoDB records for known document types. This way any file under a provider's keyspace is carried - over on a full teardown, including document types the migration logic doesn't know about. + over on a full migration, including document types the migration logic doesn't know about. """ self.test_data_generator.put_default_provider_record_in_provider_table( {'compactConnectRegisteredEmailAddress': self.OLD_REGISTERED_EMAIL} diff --git a/backend/compact-connect/stacks/ingest_stack.py b/backend/compact-connect/stacks/ingest_stack.py index b0b7a8d26a..51c5cc6e0f 100644 --- a/backend/compact-connect/stacks/ingest_stack.py +++ b/backend/compact-connect/stacks/ingest_stack.py @@ -62,7 +62,7 @@ def _add_v1_ingest_chain( ) persistent_stack.provider_table.grant_read_write_data(ingest_handler) data_event_bus.grant_put_events_to(ingest_handler) - # The SSN-correction migration deletes the old provider's Cognito account on a full teardown, moves + # The SSN-correction migration deletes the old provider's Cognito account on a full migration, moves # the practitioner's documents from the old provider id's keyspace to the new one in the provider # users bucket, and notifies the practitioner to re-register provider_users_stack.provider_users.grant(ingest_handler, 'cognito-idp:AdminDeleteUser') From fd791f19d010b925dc06c0810eb173612de92e47 Mon Sep 17 00:00:00 2001 From: Landon Shumway Date: Mon, 13 Jul 2026 09:59:34 -0700 Subject: [PATCH 15/41] make feature flag scaffolding easier to remove --- .../lambdas/python/common/cc_common/utils.py | 16 ---------------- .../provider-data-v1/handlers/bulk_upload.py | 11 ++++++----- .../python/provider-data-v1/handlers/licenses.py | 12 ++++++------ .../function/test_handlers/test_bulk_upload.py | 3 +++ .../function/test_handlers/test_licenses.py | 3 +++ 5 files changed, 18 insertions(+), 27 deletions(-) diff --git a/backend/compact-connect/lambdas/python/common/cc_common/utils.py b/backend/compact-connect/lambdas/python/common/cc_common/utils.py index a2cbc6ebef..516d5704dd 100644 --- a/backend/compact-connect/lambdas/python/common/cc_common/utils.py +++ b/backend/compact-connect/lambdas/python/common/cc_common/utils.py @@ -861,22 +861,6 @@ def sanitize_provider_data_based_on_caller_scopes(compact: str, provider: dict, return provider_read_general_schema.load(provider) -def strip_previous_ssn_if_migration_disabled(validated_license: dict, *, migration_flag_enabled: bool) -> dict: - """ - Remove the previousSSN field from a validated license when the SSN-correction migration feature is disabled. - - The presence of previousSSN downstream triggers a record migration, so the field must not leave the intake - layer (POST licenses / bulk upload) unless the feature flag is enabled. - - :param dict validated_license: A license loaded by LicensePostRequestSchema - :param bool migration_flag_enabled: Cached value of LICENSE_SSN_CORRECTION_MIGRATION_FLAG - :return: The validated license, without previousSSN if the feature is disabled - """ - if not migration_flag_enabled and validated_license.pop('previousSSN', None) is not None: - logger.info('previousSSN provided but the SSN-correction migration feature is disabled; ignoring the field') - return validated_license - - def send_licenses_to_preprocessing_queue(licenses_data: list[dict], event_time: str) -> list[str]: """ Send license data to the preprocessing queue in batches. diff --git a/backend/compact-connect/lambdas/python/provider-data-v1/handlers/bulk_upload.py b/backend/compact-connect/lambdas/python/provider-data-v1/handlers/bulk_upload.py index 63341146c4..07b427c6f0 100644 --- a/backend/compact-connect/lambdas/python/provider-data-v1/handlers/bulk_upload.py +++ b/backend/compact-connect/lambdas/python/provider-data-v1/handlers/bulk_upload.py @@ -21,7 +21,6 @@ api_handler, authorize_compact_jurisdiction, send_licenses_to_preprocessing_queue, - strip_previous_ssn_if_migration_disabled, ) from license_csv_reader import LicenseCSVReader from marshmallow import ValidationError @@ -182,10 +181,12 @@ def process_bulk_upload_file( # This will be raised, if `raw_license` includes compact and/or jurisdiction fields logger.error('License contains unsupported fields', fields=list(raw_license.keys()), exc_info=e) raise ValidationError('License contains unsupported fields') from e - validated_license = strip_previous_ssn_if_migration_disabled( - validated_license, - migration_flag_enabled=ssn_correction_migration_flag_enabled, - ) + # TODO - remove this flag once the feature is proven stable # noqa: FIX002 + if not ssn_correction_migration_flag_enabled: + logger.info( + 'SSN-correction migration feature is disabled. Ignoring the previousSSN field if present' + ) + validated_license.pop('previousSSN', None) current_batch.append(schema.dump(validated_license)) # When batch is full, send to preprocessing queue diff --git a/backend/compact-connect/lambdas/python/provider-data-v1/handlers/licenses.py b/backend/compact-connect/lambdas/python/provider-data-v1/handlers/licenses.py index 5779677441..ec0bfeac38 100644 --- a/backend/compact-connect/lambdas/python/provider-data-v1/handlers/licenses.py +++ b/backend/compact-connect/lambdas/python/provider-data-v1/handlers/licenses.py @@ -9,7 +9,6 @@ api_handler, authorize_compact_jurisdiction, send_licenses_to_preprocessing_queue, - strip_previous_ssn_if_migration_disabled, ) from marshmallow import ValidationError @@ -58,12 +57,13 @@ def post_licenses(event: dict, context: LambdaContext): # noqa: ARG001 unused-a else: license_entry = {**license_record, 'compact': compact, 'jurisdiction': jurisdiction} try: - licenses.append( - strip_previous_ssn_if_migration_disabled( - schema.load(license_entry), - migration_flag_enabled=ssn_correction_migration_flag_enabled, + # TODO - remove this flag once the feature is proven stable # noqa: FIX002 + if not ssn_correction_migration_flag_enabled: + logger.info( + 'SSN-correction migration feature is disabled. Ignoring the previousSSN field if present' ) - ) + license_entry.pop('previousSSN', None) + licenses.append(schema.load(license_entry)) except ValidationError as e: logger.debug( 'invalid license record detected', diff --git a/backend/compact-connect/lambdas/python/provider-data-v1/tests/function/test_handlers/test_bulk_upload.py b/backend/compact-connect/lambdas/python/provider-data-v1/tests/function/test_handlers/test_bulk_upload.py index 7c5b668843..e262dc406b 100644 --- a/backend/compact-connect/lambdas/python/provider-data-v1/tests/function/test_handlers/test_bulk_upload.py +++ b/backend/compact-connect/lambdas/python/provider-data-v1/tests/function/test_handlers/test_bulk_upload.py @@ -145,6 +145,8 @@ def _process_csv_with_previous_ssn(self) -> list: return self._license_preprocessing_queue.receive_messages(MaxNumberOfMessages=10) + # TODO - once LICENSE_SSN_CORRECTION_MIGRATION_FLAG is removed, remove `patch(...)` and rename test # noqa: FIX002 + # (previousSSN will always pass through) rather than removing this test outright def test_bulk_upload_passes_previous_ssn_through_when_flag_enabled(self): # patch the module-level cached flag value directly, so this test is independent of module import order with patch('handlers.bulk_upload.ssn_correction_migration_flag_enabled', True): @@ -154,6 +156,7 @@ def test_bulk_upload_passes_previous_ssn_through_when_flag_enabled(self): message_data = json.loads(messages[0].body) self.assertEqual('123-45-9876', message_data['previousSSN']) + # TODO - remove this test once the LICENSE_SSN_CORRECTION_MIGRATION_FLAG scaffolding is removed # noqa: FIX002 def test_bulk_upload_strips_previous_ssn_when_flag_disabled(self): with patch('handlers.bulk_upload.ssn_correction_migration_flag_enabled', False): messages = self._process_csv_with_previous_ssn() diff --git a/backend/compact-connect/lambdas/python/provider-data-v1/tests/function/test_handlers/test_licenses.py b/backend/compact-connect/lambdas/python/provider-data-v1/tests/function/test_handlers/test_licenses.py index 0aeefd78f5..43e8739f48 100644 --- a/backend/compact-connect/lambdas/python/provider-data-v1/tests/function/test_handlers/test_licenses.py +++ b/backend/compact-connect/lambdas/python/provider-data-v1/tests/function/test_handlers/test_licenses.py @@ -116,6 +116,8 @@ def _post_license_with_previous_ssn(self) -> list: return self._license_preprocessing_queue.receive_messages(MaxNumberOfMessages=10) + # TODO - once LICENSE_SSN_CORRECTION_MIGRATION_FLAG is removed, remove the patch and rename test # noqa: FIX002 + # (previousSSN will always pass through) rather than removing this test outright def test_post_licenses_passes_previous_ssn_through_when_flag_enabled(self): # patch the module-level cached flag value directly, so this test is independent of module import order with patch('handlers.licenses.ssn_correction_migration_flag_enabled', True): @@ -125,6 +127,7 @@ def test_post_licenses_passes_previous_ssn_through_when_flag_enabled(self): message = json.loads(queue_messages[0].body) self.assertEqual('123-12-9876', message['previousSSN']) + # TODO - remove this test once the LICENSE_SSN_CORRECTION_MIGRATION_FLAG scaffolding is removed # noqa: FIX002 def test_post_licenses_strips_previous_ssn_when_flag_disabled(self): with patch('handlers.licenses.ssn_correction_migration_flag_enabled', False): queue_messages = self._post_license_with_previous_ssn() From 28a166d70c7ff5576af3c7d89f7c1aa86c86f60e Mon Sep 17 00:00:00 2001 From: Landon Shumway Date: Mon, 13 Jul 2026 15:21:41 -0700 Subject: [PATCH 16/41] Add smoke test suite for SSN migration verification --- .../lib/email/email-notification-service.ts | 2 +- backend/compact-connect/tests/smoke/README.md | 4 + backend/compact-connect/tests/smoke/config.py | 19 + .../tests/smoke/smoke_tests_env_example.json | 3 + .../tests/smoke/ssn_migration_smoke_tests.py | 758 ++++++++++++++++++ 5 files changed, 785 insertions(+), 1 deletion(-) create mode 100644 backend/compact-connect/tests/smoke/ssn_migration_smoke_tests.py diff --git a/backend/compact-connect/lambdas/nodejs/lib/email/email-notification-service.ts b/backend/compact-connect/lambdas/nodejs/lib/email/email-notification-service.ts index 2ba4c8a34e..8a423346a4 100644 --- a/backend/compact-connect/lambdas/nodejs/lib/email/email-notification-service.ts +++ b/backend/compact-connect/lambdas/nodejs/lib/email/email-notification-service.ts @@ -455,7 +455,7 @@ export class EmailNotificationService extends BaseEmailService { const report = this.getNewEmailTemplate(); const subject = `Action Required: Registration Update - CompactConnect`; const registrationUrl = `${environmentVariableService.getUiBasePathUrl()}/register`; - const bodyText = `Your state licensing board recently corrected the information on one of your license records in the CompactConnect system. As part of this correction, your previous CompactConnect account was removed.\n\nTo continue using CompactConnect, please register again using the link below:\n\n${registrationUrl}\n\nIf you have any questions, please contact your state licensing board.`; + const bodyText = `Your state licensing board recently corrected the information on one of your license records in the CompactConnect system. As part of this correction, you will need to register again with your license record.\n\nTo continue using CompactConnect, please register again using the link below:\n\n${registrationUrl}\n\nIf you have any questions, please contact your state licensing board.`; this.insertHeader(report, 'Registration Update Required'); this.insertBody(report, bodyText, 'center', true); diff --git a/backend/compact-connect/tests/smoke/README.md b/backend/compact-connect/tests/smoke/README.md index b017c8bc41..041368df1b 100644 --- a/backend/compact-connect/tests/smoke/README.md +++ b/backend/compact-connect/tests/smoke/README.md @@ -115,6 +115,10 @@ Some tests require manual interaction: - **`practitioner_email_update_smoke_tests.py`**: Requires you to manually enter email verification codes sent to your email address. +### Tests With Account Requirements + +- **`ssn_migration_smoke_tests.py`**: Requires the `CC_TEST_PROVIDER_MOCK_SSN` and `CC_TEST_PROVIDER_ORIGINAL_PROVIDER_ID` env vars set to the SSN the test provider's records are currently stored under, and the `CC_TEST_PROVIDER_USER_BUCKET_NAME` env var set to the provider users S3 bucket name. The full migration test temporarily deletes and then restores the test provider's Cognito account (Cognito account deletion is part of the feature under test), so avoid running other provider-user tests concurrently. + ### Tests Creating Test Data Many tests create temporary test data (staff users, configurations, etc.) and clean it up automatically. However, if a test fails partway through, you may need to manually clean up test data. diff --git a/backend/compact-connect/tests/smoke/config.py b/backend/compact-connect/tests/smoke/config.py index b43b3e2d27..5402889182 100644 --- a/backend/compact-connect/tests/smoke/config.py +++ b/backend/compact-connect/tests/smoke/config.py @@ -84,6 +84,25 @@ def test_provider_user_username(self): def test_provider_user_password(self): return os.environ['CC_TEST_PROVIDER_USER_PASSWORD'] + @property + def test_provider_mock_ssn(self): + """The mock SSN the test provider user's license records are currently stored under.""" + return os.environ['CC_TEST_PROVIDER_MOCK_SSN'] + + @property + def test_provider_original_provider_id(self): + """The provider id the test provider's records live under when their SSN is CC_TEST_PROVIDER_MOCK_SSN. + + Used by the full migration smoke test to detect and recover from a prior run that was interrupted + after migrating the test provider off of this provider id but before migrating it back. + """ + return os.environ['CC_TEST_PROVIDER_ORIGINAL_PROVIDER_ID'] + + @property + def provider_user_bucket_name(self): + """The provider users S3 bucket, which holds practitioner-uploaded documents.""" + return os.environ['CC_TEST_PROVIDER_USER_BUCKET_NAME'] + @property def sandbox_authorize_net_api_login_id(self): return os.environ['SANDBOX_AUTHORIZE_NET_API_LOGIN_ID'] diff --git a/backend/compact-connect/tests/smoke/smoke_tests_env_example.json b/backend/compact-connect/tests/smoke/smoke_tests_env_example.json index 9b12be0369..f7b6cec2ad 100644 --- a/backend/compact-connect/tests/smoke/smoke_tests_env_example.json +++ b/backend/compact-connect/tests/smoke/smoke_tests_env_example.json @@ -16,6 +16,9 @@ "CC_TEST_COGNITO_PROVIDER_USER_POOL_CLIENT_ID": "72612345", "CC_TEST_PROVIDER_USER_USERNAME": "example@example.com", "CC_TEST_PROVIDER_USER_PASSWORD": "examplePassword", + "CC_TEST_PROVIDER_ORIGINAL_PROVIDER_ID": "e9f93c33-eba6-46b3-897d-4a5f23aead7e", + "CC_TEST_PROVIDER_MOCK_SSN": "000-00-0000", + "CC_TEST_PROVIDER_USER_BUCKET_NAME": "sandbox-persistentstack-providerusersbucket1234-abcd1234", "ENVIRONMENT_NAME": "sandboxEnvironmentNamePlaceholder", "SANDBOX_AUTHORIZE_NET_API_LOGIN_ID": "your_sandbox_api_login_id", "SANDBOX_AUTHORIZE_NET_TRANSACTION_KEY": "your_sandbox_transaction_key", diff --git a/backend/compact-connect/tests/smoke/ssn_migration_smoke_tests.py b/backend/compact-connect/tests/smoke/ssn_migration_smoke_tests.py new file mode 100644 index 0000000000..e35a9e6bca --- /dev/null +++ b/backend/compact-connect/tests/smoke/ssn_migration_smoke_tests.py @@ -0,0 +1,758 @@ +# ruff: noqa: T201 we use print statements for smoke testing +#!/usr/bin/env python3 +""" +Smoke tests for the SSN-correction migration feature (the optional 'previousSSN' license upload field). + +When a state uploads a license with a 'previousSSN', the system migrates the license record (and any +associated records) from the provider id the incorrect SSN resolved to over to the provider id of the +corrected SSN. If the corrected license was the old provider's only license, the entire old provider is +migrated (a "full migration"): person-level records and S3 documents move as well, the old partition is +deleted, and the old Cognito account is removed so the practitioner can re-register. + +These tests require: +- The 'license-ssn-correction-migration-flag' feature flag to be ENABLED in the target environment. +- A registered test provider user (CC_TEST_PROVIDER_USER_USERNAME) whose license records are stored under + the SSN configured in the CC_TEST_PROVIDER_MOCK_SSN env var. +- The CC_TEST_PROVIDER_USER_BUCKET_NAME env var set to the environment's provider users S3 bucket. + +License uploads are performed against the State API (CC_TEST_STATE_API_BASE_URL) using a Cognito +client-credentials app client, the same way state IT systems authenticate in production - see +'signature_auth_smoke_tests.py' for the pattern this follows. Provider lookups (query by name) go through +the general API (CC_TEST_API_BASE_URL) using a staff user, since 'providers/query' is a staff-facing +endpoint, not a state-facing one. + +Note that by design, developers do not have the ability to delete records from the SSN DynamoDB table, +so the SSN records created by these tests are left in place. The tests use fixed mock SSNs so repeated +runs reuse the same SSN -> provider id mappings rather than accumulating new ones. + +The full migration test intentionally deletes the test provider's Cognito account mid-test (that is part +of the feature) and restores it at the end, so the shared test provider account remains usable. +""" + +import json +import os +import time +from collections.abc import Callable + +import boto3 +import requests +from botocore.exceptions import ClientError +from config import config, logger +from military_affiliation_smoke_tests import test_military_affiliation_upload +from smoke_common import ( + SmokeTestFailureException, + call_provider_users_me_endpoint, + cleanup_test_provider_records, + create_test_app_client, + create_test_staff_user, + delete_test_app_client, + delete_test_staff_user, + get_api_base_url, + get_client_auth_headers, + get_provider_user_dynamodb_table, + get_staff_user_auth_headers, + load_smoke_test_env, + wait_for_provider_creation, +) + + +# If you test provider is in a different compact, change this value +TEST_COMPACT = 'coun' +# The corrected SSN the test provider is temporarily migrated to during the full migration roundtrip +FULL_MIGRATION_CORRECTED_SSN = '999-99-8877' + +# Partial migration test constants: a standalone mock practitioner with OT + OTA licenses in octp/ne +PARTIAL_MIGRATION_COMPACT = 'octp' +PARTIAL_MIGRATION_JURISDICTION = 'ne' +PARTIAL_MIGRATION_GIVEN_NAME = 'SsnMigration' +PARTIAL_MIGRATION_FAMILY_NAME = 'PartialSmokeTest' +PARTIAL_MIGRATION_ORIGINAL_SSN = '999-99-8888' +PARTIAL_MIGRATION_CORRECTED_SSN = '999-99-8899' +OT_LICENSE_TYPE = 'occupational therapist' +OTA_LICENSE_TYPE = 'occupational therapy assistant' + +TEST_STAFF_USER_EMAIL = 'testStaffUserSsnMigration@smokeTestFakeEmail.com' +TEST_APP_CLIENT_NAME = 'test-ssn-migration-client' + +# License fields that can be round-tripped from a provider's existing license record into an upload payload +_UPLOADABLE_LICENSE_FIELDS = ( + 'npi', + 'licenseNumber', + 'licenseStatusName', + 'givenName', + 'middleName', + 'familyName', + 'suffix', + 'dateOfBirth', + 'dateOfIssuance', + 'dateOfRenewal', + 'dateOfExpiration', + 'homeAddressStreet1', + 'homeAddressStreet2', + 'homeAddressCity', + 'homeAddressState', + 'homeAddressPostalCode', + 'emailAddress', + 'phoneNumber', + 'licenseType', +) + +# Record fields that legitimately change when a record is re-keyed to a new provider id, and so are +# excluded when comparing a provider's records before and after a migration. The provider id itself is +# normalized (not dropped) so that provider-id-derived fields still participate in the comparison. +_VOLATILE_RECORD_FIELDS = ('pk', 'sk', 'dateOfUpdate', 'providerDateOfUpdate', 'ssnLastFour') + +_MIGRATION_WAIT_SECONDS = 900 +_POLL_INTERVAL_SECONDS = 30 + + +# ------------------------------------------------------------------------------------------------- +# Shared helpers +# ------------------------------------------------------------------------------------------------- +def _upload_license_records(client_headers: dict, compact: str, jurisdiction: str, license_records: list[dict]): + """POST the given license records to the State API's synchronous license upload endpoint. + + This endpoint only exists on the State API (not the general API) and is authenticated with a state + IT-system client-credentials token, per 'client_headers' - see '_create_test_app_client_headers'. + """ + post_response = requests.post( + url=f'{config.state_api_base_url}/v1/compacts/{compact}/jurisdictions/{jurisdiction}/licenses', + headers=client_headers, + json=license_records, + timeout=30, + ) + if post_response.status_code != 200: + raise SmokeTestFailureException(f'Failed to POST license records. Response: {post_response.json()}') + print(f'Successfully uploaded {len(license_records)} license record(s) to {compact}/{jurisdiction}') + + +def _create_test_app_client_headers(client_name: str, compact: str, jurisdiction: str) -> tuple[dict, str]: + """Create a state IT-system test app client and return (auth headers, client_id) for later cleanup.""" + client_credentials = create_test_app_client(client_name, compact, jurisdiction) + client_headers = get_client_auth_headers( + client_credentials['client_id'], client_credentials['client_secret'], compact, jurisdiction + ) + return client_headers, client_credentials['client_id'] + + +def _get_provider_dynamo_records(compact: str, provider_id: str) -> list[dict]: + """Query DynamoDB directly for every record under the provider's partition.""" + dynamo_table = get_provider_user_dynamodb_table() + records = [] + last_evaluated_key = None + while True: + pagination = {'ExclusiveStartKey': last_evaluated_key} if last_evaluated_key else {} + query_response = dynamo_table.query( + KeyConditionExpression='pk = :pk', + ExpressionAttributeValues={':pk': f'{compact}#PROVIDER#{provider_id}'}, + ConsistentRead=True, + **pagination, + ) + records.extend(query_response.get('Items', [])) + last_evaluated_key = query_response.get('LastEvaluatedKey') + if not last_evaluated_key: + break + return records + + +def _list_provider_s3_keys(compact: str, provider_id: str) -> list[str]: + """List the S3 object keys under the provider's keyspace, without downloading the objects.""" + s3_client = boto3.client('s3') + provider_prefix = f'compact/{compact}/provider/{provider_id}/' + keys = [] + paginator = s3_client.get_paginator('list_objects_v2') + for page in paginator.paginate(Bucket=config.provider_user_bucket_name, Prefix=provider_prefix): + keys.extend(s3_object['Key'] for s3_object in page.get('Contents', [])) + return keys + + +def _get_provider_s3_objects(compact: str, provider_id: str) -> dict[str, bytes]: + """List every S3 object under the provider's keyspace, keyed relative to the provider prefix. + + Returns a dict of {key suffix after the provider prefix: object body bytes} so that objects can be + compared across provider ids. + """ + s3_client = boto3.client('s3') + provider_prefix = f'compact/{compact}/provider/{provider_id}/' + objects = {} + paginator = s3_client.get_paginator('list_objects_v2') + for page in paginator.paginate(Bucket=config.provider_user_bucket_name, Prefix=provider_prefix): + for s3_object in page.get('Contents', []): + object_body = s3_client.get_object(Bucket=config.provider_user_bucket_name, Key=s3_object['Key'])[ + 'Body' + ].read() + objects[s3_object['Key'][len(provider_prefix) :]] = object_body + return objects + + +def _normalized_migratable_records(records: list[dict], provider_id: str) -> dict[str, str]: + """Canonicalize a provider's records for comparison across provider ids. + + Each record is serialized with its provider id replaced by a placeholder (which also normalizes + provider-id-derived values such as document keys and embedded 'previous' snapshots) and with fields + that legitimately change during a migration removed. + + The top-level provider record is excluded: it is rebuilt (not moved) during a migration, and the + ssnCorrection provider update records created by each migration are also excluded since they are + additions rather than migrated records. + """ + normalized = {} + for record in records: + if record['type'] == 'provider' or ( + record['type'] == 'providerUpdate' and record.get('updateType') == 'ssnCorrection' + ): + continue + scrubbed = {key: value for key, value in record.items() if key not in _VOLATILE_RECORD_FIELDS} + canonical = json.dumps(scrubbed, sort_keys=True, default=str).replace(provider_id, '') + # key by something readable for failure messages + normalized[f'{record["type"]}: {record["sk"].replace(provider_id, "")}'] = canonical + return normalized + + +def _verify_all_records_migrated( + *, source_records: list[dict], source_provider_id: str, target_records: list[dict], target_provider_id: str +): + """Verify every migratable record captured from the source provider now exists under the target provider.""" + source_normalized = _normalized_migratable_records(source_records, source_provider_id) + target_normalized = set(_normalized_migratable_records(target_records, target_provider_id).values()) + + missing_records = [label for label, canonical in source_normalized.items() if canonical not in target_normalized] + if missing_records: + raise SmokeTestFailureException( + f'The following records were not migrated to provider {target_provider_id}: {missing_records}' + ) + print(f'Verified all {len(source_normalized)} migratable records now exist under provider {target_provider_id}') + + +def _verify_all_s3_objects_migrated(*, source_objects: dict[str, bytes], compact: str, target_provider_id: str): + """Verify every S3 object captured from the source keyspace exists, byte-for-byte, under the target keyspace.""" + target_objects = _get_provider_s3_objects(compact, target_provider_id) + for relative_key, source_body in source_objects.items(): + if relative_key not in target_objects: + raise SmokeTestFailureException( + f'S3 object {relative_key} was not migrated to provider {target_provider_id}' + ) + if target_objects[relative_key] != source_body: + raise SmokeTestFailureException( + f'S3 object {relative_key} under provider {target_provider_id} does not match the original content' + ) + print(f'Verified all {len(source_objects)} S3 objects migrated to provider {target_provider_id} keyspace') + + +def _wait_until(description: str, predicate: Callable, max_wait_seconds: int = _MIGRATION_WAIT_SECONDS): + """Poll the given predicate until it returns a truthy value, or raise after the wait limit.""" + start_time = time.time() + while time.time() - start_time < max_wait_seconds: + result = predicate() + if result: + print(f'✅ {description} (after {time.time() - start_time:.0f} seconds)') + return result + print(f'Waiting for {description}...') + time.sleep(_POLL_INTERVAL_SECONDS) + raise SmokeTestFailureException(f'Timed out after {max_wait_seconds} seconds waiting for {description}') + + +def _query_provider_ids_by_name(staff_headers: dict, compact: str, given_name: str, family_name: str) -> list[str]: + """Query the providers endpoint by name and return all matching provider ids.""" + query_response = requests.post( + url=f'{get_api_base_url()}/v1/compacts/{compact}/providers/query', + headers=staff_headers, + json={'query': {'familyName': family_name, 'givenName': given_name}}, + timeout=10, + ) + if query_response.status_code != 200: + logger.warning(f'Provider query failed with status {query_response.status_code}') + return [] + return [provider['providerId'] for provider in query_response.json().get('providers', [])] + + +# ------------------------------------------------------------------------------------------------- +# Full migration test (with roundtrip back to the original SSN) +# ------------------------------------------------------------------------------------------------- +def _build_correction_upload_from_existing_license( + provider_data: dict, license_record: dict, *, corrected_ssn: str, previous_ssn: str +) -> dict: + """Build a license upload payload that mirrors an existing license record, with a corrected SSN. + + Mirroring the existing license data keeps the post-migration license write a no-op content-wise, so the + migration is the only change under test. + """ + upload = { + field: license_record[field] for field in _UPLOADABLE_LICENSE_FIELDS if license_record.get(field) is not None + } + # dateOfBirth is not always returned on the license object itself; fall back to the provider record + upload.setdefault('dateOfBirth', provider_data['dateOfBirth']) + # these fields are stored under jurisdiction-uploaded names internally + upload['licenseStatus'] = license_record['jurisdictionUploadedLicenseStatus'] + upload['compactEligibility'] = license_record['jurisdictionUploadedCompactEligibility'] + upload['ssn'] = corrected_ssn + upload['previousSSN'] = previous_ssn + return upload + + +def _migrate_test_provider_to_ssn( + *, + staff_headers: dict, + client_headers: dict, + provider_data: dict, + license_record: dict, + current_provider_id: str, + corrected_ssn: str, + previous_ssn: str, +) -> str: + """Upload the test provider's license with a corrected SSN and wait for the migration to complete. + + :param staff_headers: Staff auth headers, used to query for the provider by name (general API) + :param client_headers: State IT-system client-credentials headers, used to upload the license (State API) + :return: The provider id the records were migrated to + """ + compact = provider_data['compact'] + upload = _build_correction_upload_from_existing_license( + provider_data, license_record, corrected_ssn=corrected_ssn, previous_ssn=previous_ssn + ) + _upload_license_records(client_headers, compact, license_record['jurisdiction'], [upload]) + + # The migration is complete once the provider's records resolve to a different provider id + def _find_new_provider_id(): + provider_ids = _query_provider_ids_by_name( + staff_headers, compact, provider_data['givenName'], provider_data['familyName'] + ) + return next((provider_id for provider_id in provider_ids if provider_id != current_provider_id), None) + + new_provider_id = _wait_until( + f'the test provider to migrate off of provider id {current_provider_id}', _find_new_provider_id + ) + + # The old partition is emptied by the migration, and the S3 objects move just after the DynamoDB records + _wait_until( + f'all DynamoDB records to be removed from old provider {current_provider_id}', + lambda: not _get_provider_dynamo_records(compact, current_provider_id), + max_wait_seconds=900, + ) + _wait_until( + f'all S3 objects to be removed from old provider {current_provider_id} keyspace', + lambda: not _list_provider_s3_keys(compact, current_provider_id), + max_wait_seconds=900, + ) + return new_provider_id + + +def _restore_test_provider_account(compact: str, provider_id: str, baseline_provider_record: dict): + """Restore the shared test provider account after a full migration deleted its Cognito user. + + Recreates the provider Cognito user pointed at the given provider id and restores the registration + fields on the top-level provider record, which are intentionally dropped by the migration (in real + usage the practitioner re-registers). + + If the Cognito user already exists, this is a no-op: 'custom:compact' and 'custom:providerId' are + immutable Cognito custom attributes, so an existing user cannot be re-pointed at a different provider + id. A pre-existing user only happens when the full migration never actually deleted it (e.g. the test + failed before reaching that step, or this is being called defensively after a failure), in which case + the account is already valid and does not need restoring. + """ + username = config.test_provider_user_username + try: + config.cognito_client.admin_create_user( + UserPoolId=config.cognito_provider_user_pool_id, + Username=username, + UserAttributes=[ + {'Name': 'custom:compact', 'Value': compact}, + {'Name': 'custom:providerId', 'Value': provider_id}, + {'Name': 'email', 'Value': username}, + {'Name': 'email_verified', 'Value': 'true'}, + ], + MessageAction='SUPPRESS', + ) + print(f'Recreated test provider Cognito user, pointed at provider id {provider_id}') + except ClientError as e: + if e.response['Error']['Code'] != 'UsernameExistsException': + raise + print( + f'Test provider Cognito user already exists; leaving it as-is (custom:providerId is immutable, ' + f'so it cannot be re-pointed at provider id {provider_id})' + ) + return + + config.cognito_client.admin_set_user_password( + UserPoolId=config.cognito_provider_user_pool_id, + Username=username, + Password=config.test_provider_user_password, + Permanent=True, + ) + # clear the cached provider token so the next /me call performs a fresh login against the restored user + os.environ.pop('TEST_PROVIDER_USER_ID_TOKEN', None) + + # restore the registration fields on the provider record, which populate_provider_record does not carry over + registration_fields = { + field: baseline_provider_record[field] + for field in ('compactConnectRegisteredEmailAddress', 'currentHomeJurisdiction') + if field in baseline_provider_record + } + if registration_fields: + get_provider_user_dynamodb_table().update_item( + Key={'pk': f'{compact}#PROVIDER#{provider_id}', 'sk': f'{compact}#PROVIDER'}, + UpdateExpression='SET ' + ', '.join(f'#{i} = :{i}' for i in range(len(registration_fields))), + ExpressionAttributeNames={f'#{i}': field for i, field in enumerate(registration_fields)}, + ExpressionAttributeValues={f':{i}': value for i, value in enumerate(registration_fields.values())}, + ) + print(f'Restored registration fields on provider record {provider_id}: {sorted(registration_fields)}') + + +def _cognito_user_exists(username: str) -> bool: + """Check whether a Cognito user currently exists in the provider user pool.""" + try: + config.cognito_client.admin_get_user(UserPoolId=config.cognito_provider_user_pool_id, Username=username) + return True + except ClientError as e: + if e.response['Error']['Code'] == 'UserNotFoundException': + return False + raise + + +def _recover_stranded_test_provider(): + """ + Detect and recover from a previous full migration test run that was interrupted after the shared test + provider's records were migrated off of their original provider id, but before they were migrated back + (e.g. the process was killed while waiting on a slow migration). In that state, the test provider's + Cognito account no longer exists (deleted by the full migration) and their records are stranded under + whatever provider id the intermediate corrected SSN currently resolves to. + + If the original provider id has no records AND the Cognito account does not exist, this prompts the + developer for the stranded (new) provider id, migrates the records back to the original provider id, + and recreates the Cognito account - so the test below can then proceed exactly as it would from a + clean starting state. Otherwise this is a no-op: either the records are already home, or the account + is already usable. + + Developers cannot look up provider ids via the SSN table, so the stranded provider id must be supplied + manually (e.g. from CloudWatch logs or a DynamoDB console query from the interrupted run). + """ + + if _get_provider_dynamo_records(TEST_COMPACT, config.test_provider_original_provider_id) or _cognito_user_exists( + config.test_provider_user_username + ): + return + + print( + 'Detected a stranded test provider from an interrupted previous run (no records under the original ' + 'provider id and no Cognito account). Recovering before running the test...' + ) + print( + 'Enter the provider id where the stranded records currently live ' + '(the id they were migrated to during the interrupted run).' + ) + stuck_provider_id = input('Stuck provider id: ').strip() + if not stuck_provider_id: + raise SmokeTestFailureException( + 'Test provider records are not under the original provider id and no Cognito account exists, ' + 'but no stuck provider id was provided. Re-run and supply the stranded provider id to recover.' + ) + + stuck_records = _get_provider_dynamo_records(TEST_COMPACT, stuck_provider_id) + stuck_license = next((record for record in stuck_records if record['type'] == 'license'), None) + stuck_provider_record = next((record for record in stuck_records if record['type'] == 'provider'), None) + if not stuck_license or not stuck_provider_record: + raise SmokeTestFailureException( + f'Could not find a stranded license and provider record under provider id {stuck_provider_id} ' + f'to recover from. Confirm the provider id is correct for compact {TEST_COMPACT}.' + ) + + client_headers, client_id = _create_test_app_client_headers( + TEST_APP_CLIENT_NAME, TEST_COMPACT, stuck_license['jurisdiction'] + ) + try: + upload = _build_correction_upload_from_existing_license( + stuck_provider_record, + stuck_license, + corrected_ssn=config.test_provider_mock_ssn, + previous_ssn=FULL_MIGRATION_CORRECTED_SSN, + ) + _upload_license_records(client_headers, TEST_COMPACT, stuck_license['jurisdiction'], [upload]) + finally: + delete_test_app_client(client_id) + + _wait_until( + 'the stranded test provider records to migrate back to the original provider id', + lambda: bool(_get_provider_dynamo_records(TEST_COMPACT, config.test_provider_original_provider_id)), + ) + + recovered_provider_record = next( + record + for record in _get_provider_dynamo_records(TEST_COMPACT, config.test_provider_original_provider_id) + if record['type'] == 'provider' + ) + _restore_test_provider_account( + TEST_COMPACT, config.test_provider_original_provider_id, recovered_provider_record + ) + print('Recovery complete: records and Cognito account are back under the original provider id.') + + +def test_full_ssn_migration_roundtrip(): + """ + Full migration: the test provider (single license, with military documentation) is migrated from their + current mock SSN to a corrected SSN, verified, then migrated back to the original SSN. + + Step 0: Recover from any prior interrupted run that left the test provider stranded (see + _recover_stranded_test_provider). + Step 1: Capture the test provider's baseline state (all DynamoDB records + all S3 objects). + Step 2: Upload a fresh military affiliation document so there is a recent document to migrate. + Step 3: Upload the provider's license with a corrected SSN and previousSSN set to their current mock SSN. + Step 4: Wait for the migration, then verify every record and S3 object moved to the new provider id. + Step 5: Migrate back to the original SSN (roundtrip) and verify everything returned to the original + provider id and the intermediate provider id was cleaned up. + Step 6: Restore the test provider's Cognito account (deleted by the full migration) and registration + fields so the shared test account remains usable. + """ + _recover_stranded_test_provider() + + provider_data = call_provider_users_me_endpoint() + compact = provider_data['compact'] + original_provider_id = provider_data['providerId'] + licenses = provider_data.get('licenses', []) + if len(licenses) != 1: + raise SmokeTestFailureException( + f'The full migration test expects the test provider to have exactly one license record; ' + f'found {len(licenses)}. A multi-license provider would only be partially migrated.' + ) + license_record = licenses[0] + print(f'Testing full SSN migration for provider {original_provider_id} in compact {compact}') + + # Step 1: capture the initial baseline directly from DynamoDB and S3 + baseline_records = _get_provider_dynamo_records(compact, original_provider_id) + baseline_s3_objects = _get_provider_s3_objects(compact, original_provider_id) + baseline_provider_record = next(record for record in baseline_records if record['type'] == 'provider') + print( + f'Captured baseline: {len(baseline_records)} DynamoDB records, {len(baseline_s3_objects)} S3 objects ' + f'under provider {original_provider_id}' + ) + + # Step 2: upload a fresh military affiliation document, then re-capture the pre-migration state so the + # new document (record + S3 object) is included in what we expect to migrate + test_military_affiliation_upload() + pre_migration_records = _get_provider_dynamo_records(compact, original_provider_id) + pre_migration_s3_objects = _get_provider_s3_objects(compact, original_provider_id) + print( + f'Pre-migration state after military upload: {len(pre_migration_records)} DynamoDB records, ' + f'{len(pre_migration_s3_objects)} S3 objects' + ) + + jurisdiction = license_record['jurisdiction'] + test_staff_user_sub = create_test_staff_user( + email=TEST_STAFF_USER_EMAIL, + compact=compact, + jurisdiction=jurisdiction, + permissions={'actions': {'admin'}, 'jurisdictions': {jurisdiction: {'write', 'admin'}}}, + ) + staff_headers = get_staff_user_auth_headers(TEST_STAFF_USER_EMAIL) + client_headers, test_app_client_id = _create_test_app_client_headers(TEST_APP_CLIENT_NAME, compact, jurisdiction) + try: + # Step 3 + 4: migrate to the corrected SSN and verify everything moved + migrated_provider_id = _migrate_test_provider_to_ssn( + staff_headers=staff_headers, + client_headers=client_headers, + provider_data=provider_data, + license_record=license_record, + current_provider_id=original_provider_id, + corrected_ssn=FULL_MIGRATION_CORRECTED_SSN, + previous_ssn=config.test_provider_mock_ssn, + ) + migrated_records = _get_provider_dynamo_records(compact, migrated_provider_id) + _verify_all_records_migrated( + source_records=pre_migration_records, + source_provider_id=original_provider_id, + target_records=migrated_records, + target_provider_id=migrated_provider_id, + ) + if not any( + record['type'] == 'providerUpdate' and record.get('updateType') == 'ssnCorrection' + for record in migrated_records + ): + raise SmokeTestFailureException('No ssnCorrection provider update record found after migration') + _verify_all_s3_objects_migrated( + source_objects=pre_migration_s3_objects, compact=compact, target_provider_id=migrated_provider_id + ) + + # Step 5: roundtrip back to the original SSN and verify everything returned home + returned_provider_id = _migrate_test_provider_to_ssn( + staff_headers=staff_headers, + client_headers=client_headers, + provider_data=provider_data, + license_record=license_record, + current_provider_id=migrated_provider_id, + corrected_ssn=config.test_provider_mock_ssn, + previous_ssn=FULL_MIGRATION_CORRECTED_SSN, + ) + if returned_provider_id != original_provider_id: + raise SmokeTestFailureException( + f'Roundtrip migration did not return to the original provider id. ' + f'Expected {original_provider_id}, got {returned_provider_id}' + ) + _verify_all_records_migrated( + source_records=pre_migration_records, + source_provider_id=original_provider_id, + target_records=_get_provider_dynamo_records(compact, original_provider_id), + target_provider_id=original_provider_id, + ) + _verify_all_s3_objects_migrated( + source_objects=pre_migration_s3_objects, compact=compact, target_provider_id=original_provider_id + ) + print('Roundtrip migration completed; all records and documents are back under the original provider id') + finally: + # Restore the shared test provider account no matter what state the test failed in: point the + # Cognito user at whichever provider id currently holds the provider's records + current_ids = _query_provider_ids_by_name( + staff_headers, compact, provider_data['givenName'], provider_data['familyName'] + ) + restore_provider_id = current_ids[0] if current_ids else original_provider_id + _restore_test_provider_account(compact, restore_provider_id, baseline_provider_record) + delete_test_staff_user(TEST_STAFF_USER_EMAIL, user_sub=test_staff_user_sub, compact=compact) + delete_test_app_client(test_app_client_id) + + # Final verification: the restored account can log in and sees the original provider id + restored_provider_data = call_provider_users_me_endpoint() + if restored_provider_data['providerId'] != original_provider_id: + raise SmokeTestFailureException( + f'Restored test provider account resolves to provider id {restored_provider_data["providerId"]}; ' + f'expected {original_provider_id}' + ) + print('Test provider account restored and verified. Full migration roundtrip smoke test passed.') + + +# ------------------------------------------------------------------------------------------------- +# Partial migration test +# ------------------------------------------------------------------------------------------------- +def _build_partial_test_license(license_type: str, ssn: str, previous_ssn: str | None = None) -> dict: + license_record = { + 'ssn': ssn, + 'npi': '2222222222', + 'licenseNumber': f'SSN-MIG-{("OT" if license_type == OT_LICENSE_TYPE else "OTA")}', + 'givenName': PARTIAL_MIGRATION_GIVEN_NAME, + 'familyName': PARTIAL_MIGRATION_FAMILY_NAME, + 'dateOfBirth': '1990-01-01', + 'dateOfIssuance': '2020-01-01', + 'dateOfExpiration': '2050-01-01', + 'licenseType': license_type, + 'licenseStatus': 'active', + 'compactEligibility': 'eligible', + 'homeAddressStreet1': '123 Test Street', + 'homeAddressCity': 'Omaha', + 'homeAddressState': 'ne', + 'homeAddressPostalCode': '68001', + } + if previous_ssn is not None: + license_record['previousSSN'] = previous_ssn + return license_record + + +def _get_license_types_in_partition(compact: str, provider_id: str) -> set[str]: + return { + record['licenseType'] + for record in _get_provider_dynamo_records(compact, provider_id) + if record['type'] == 'license' + } + + +def test_partial_ssn_migration(): + """ + Partial migration: a mock practitioner with OT and OTA licenses under one (incorrect) SSN has only the + OT license's SSN corrected. The OT license must move to a new provider id while the OTA license and the + old provider record remain in place. + + Step 1: Upload OT + OTA licenses under the same mock SSN and wait for the provider to be created. + Step 2: Re-upload the OT license with a corrected SSN and previousSSN set to the original mock SSN. + Step 3: Verify the OT license now lives under a new provider id with its own top-level provider record, + while the OTA license and provider record remain under the old provider id. + Step 4: Clean up all DynamoDB records for both provider ids. + """ + test_staff_user_sub = create_test_staff_user( + email=TEST_STAFF_USER_EMAIL, + compact=PARTIAL_MIGRATION_COMPACT, + jurisdiction=PARTIAL_MIGRATION_JURISDICTION, + permissions={'actions': {'admin'}, 'jurisdictions': {PARTIAL_MIGRATION_JURISDICTION: {'write', 'admin'}}}, + ) + staff_headers = get_staff_user_auth_headers(TEST_STAFF_USER_EMAIL) + client_headers, test_app_client_id = _create_test_app_client_headers( + TEST_APP_CLIENT_NAME, PARTIAL_MIGRATION_COMPACT, PARTIAL_MIGRATION_JURISDICTION + ) + old_provider_id = None + new_provider_id = None + try: + # Step 1: create the mock practitioner with two license types under the same (incorrect) SSN + _upload_license_records( + client_headers, + PARTIAL_MIGRATION_COMPACT, + PARTIAL_MIGRATION_JURISDICTION, + [ + _build_partial_test_license(OT_LICENSE_TYPE, PARTIAL_MIGRATION_ORIGINAL_SSN), + _build_partial_test_license(OTA_LICENSE_TYPE, PARTIAL_MIGRATION_ORIGINAL_SSN), + ], + ) + old_provider_id = wait_for_provider_creation( + staff_headers, PARTIAL_MIGRATION_COMPACT, PARTIAL_MIGRATION_GIVEN_NAME, PARTIAL_MIGRATION_FAMILY_NAME + ) + _wait_until( + f'both license records to exist under provider {old_provider_id}', + lambda: ( + _get_license_types_in_partition(PARTIAL_MIGRATION_COMPACT, old_provider_id) + == {OT_LICENSE_TYPE, OTA_LICENSE_TYPE} + ), + ) + + # Step 2: correct the SSN on the OT license only + _upload_license_records( + client_headers, + PARTIAL_MIGRATION_COMPACT, + PARTIAL_MIGRATION_JURISDICTION, + [ + _build_partial_test_license( + OT_LICENSE_TYPE, PARTIAL_MIGRATION_CORRECTED_SSN, previous_ssn=PARTIAL_MIGRATION_ORIGINAL_SSN + ) + ], + ) + + # Step 3: wait for the OT license to arrive under a new provider id + def _find_new_provider_id(): + provider_ids = _query_provider_ids_by_name( + staff_headers, PARTIAL_MIGRATION_COMPACT, PARTIAL_MIGRATION_GIVEN_NAME, PARTIAL_MIGRATION_FAMILY_NAME + ) + return next((provider_id for provider_id in provider_ids if provider_id != old_provider_id), None) + + new_provider_id = _wait_until('the OT license to migrate to a new provider id', _find_new_provider_id) + + old_provider_records = _get_provider_dynamo_records(PARTIAL_MIGRATION_COMPACT, old_provider_id) + old_record_types = {record['type'] for record in old_provider_records} + old_license_types = {record['licenseType'] for record in old_provider_records if record['type'] == 'license'} + if 'provider' not in old_record_types: + raise SmokeTestFailureException( + 'The old provider record was deleted during a partial migration; it should have been preserved' + ) + if old_license_types != {OTA_LICENSE_TYPE}: + raise SmokeTestFailureException( + f'Expected only the OTA license to remain under the old provider id; found: {old_license_types}' + ) + print(f'Verified the OTA license and provider record remain under old provider {old_provider_id}') + + new_provider_records = _get_provider_dynamo_records(PARTIAL_MIGRATION_COMPACT, new_provider_id) + new_record_types = {record['type'] for record in new_provider_records} + new_license_types = {record['licenseType'] for record in new_provider_records if record['type'] == 'license'} + if 'provider' not in new_record_types: + raise SmokeTestFailureException('No top-level provider record was created for the new provider id') + if new_license_types != {OT_LICENSE_TYPE}: + raise SmokeTestFailureException( + f'Expected only the OT license under the new provider id; found: {new_license_types}' + ) + print(f'Verified the OT license and a new provider record exist under new provider {new_provider_id}') + print('Partial migration smoke test passed.') + finally: + # Step 4: clean up all DynamoDB records for the mock practitioner (the SSN table records cannot be + # deleted by developers, and the fixed mock SSNs make reruns reuse the same mappings) + for provider_id in (old_provider_id, new_provider_id): + if provider_id: + cleanup_test_provider_records(provider_id, PARTIAL_MIGRATION_COMPACT) + delete_test_staff_user(TEST_STAFF_USER_EMAIL, user_sub=test_staff_user_sub, compact=PARTIAL_MIGRATION_COMPACT) + delete_test_app_client(test_app_client_id) + + +if __name__ == '__main__': + load_smoke_test_env() + test_full_ssn_migration_roundtrip() + test_partial_ssn_migration() From 3a146d4020a2ed24f2e324b4f3153b8dc69a358c Mon Sep 17 00:00:00 2001 From: Landon Shumway Date: Mon, 13 Jul 2026 15:23:29 -0700 Subject: [PATCH 17/41] formatting --- .../compact-connect/tests/smoke/ssn_migration_smoke_tests.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/backend/compact-connect/tests/smoke/ssn_migration_smoke_tests.py b/backend/compact-connect/tests/smoke/ssn_migration_smoke_tests.py index e35a9e6bca..a894b2b80e 100644 --- a/backend/compact-connect/tests/smoke/ssn_migration_smoke_tests.py +++ b/backend/compact-connect/tests/smoke/ssn_migration_smoke_tests.py @@ -55,7 +55,6 @@ wait_for_provider_creation, ) - # If you test provider is in a different compact, change this value TEST_COMPACT = 'coun' # The corrected SSN the test provider is temporarily migrated to during the full migration roundtrip @@ -480,9 +479,7 @@ def _recover_stranded_test_provider(): for record in _get_provider_dynamo_records(TEST_COMPACT, config.test_provider_original_provider_id) if record['type'] == 'provider' ) - _restore_test_provider_account( - TEST_COMPACT, config.test_provider_original_provider_id, recovered_provider_record - ) + _restore_test_provider_account(TEST_COMPACT, config.test_provider_original_provider_id, recovered_provider_record) print('Recovery complete: records and Cognito account are back under the original provider id.') From 96518972ff0bcab1a51af50e764f22e0e1be50b2 Mon Sep 17 00:00:00 2001 From: Landon Shumway Date: Mon, 13 Jul 2026 17:08:47 -0700 Subject: [PATCH 18/41] Migration refinements to add guards and contextual logging --- .../cc_common/data_model/data_client.py | 95 ++++++- .../test_data_client_ssn_correction.py | 249 ++++++++++++++++++ 2 files changed, 332 insertions(+), 12 deletions(-) diff --git a/backend/compact-connect/lambdas/python/common/cc_common/data_model/data_client.py b/backend/compact-connect/lambdas/python/common/cc_common/data_model/data_client.py index 7634cd3f9a..fbd973bb0c 100644 --- a/backend/compact-connect/lambdas/python/common/cc_common/data_model/data_client.py +++ b/backend/compact-connect/lambdas/python/common/cc_common/data_model/data_client.py @@ -2923,13 +2923,7 @@ def migrate_provider_for_ssn_correction( logger.info('Previous provider has no license matching the corrected upload; nothing to migrate') return SsnCorrectionMigrationResult(migration_performed=False) - try: - old_provider_data = old_provider_records.get_provider_record() - except CCInternalException: - # The top-level provider record was already deleted by a partially-completed full migration that - # is now being replayed; continue the migration without the concurrency fence - logger.warning('Old provider record not found; continuing replay of a partially-completed migration') - old_provider_data = None + old_top_level_provider_data = old_provider_records.get_provider_record() # The corrected license was the old provider's only license: this is a full migration of the old # provider (everything moves, and the old provider is deleted), as opposed to a partial migration @@ -2941,6 +2935,18 @@ def migrate_provider_for_ssn_correction( person_level_records = old_provider_records.get_person_level_records() if full_migration else [] records_to_move = [*records_to_move, *person_level_records] + if full_migration: + # A full migration deletes the old provider's top-level record, so every record in the old + # partition must be selected for migration; any record the selectors above don't recognize (e.g. a + # record type introduced after this migration logic was written) would otherwise be silently + # orphaned in a partition with no provider record. Fail before writing anything so the old + # provider stays intact and the message retries visibly instead. + self._verify_full_migration_accounts_for_all_old_provider_records( + old_provider_records=old_provider_records, + records_to_move=records_to_move, + old_provider_data=old_top_level_provider_data, + ) + target_license = next(record for record in records_to_move if record.type == ProviderRecordType.LICENSE) target_license_key = self._provider_record_key(target_license) @@ -3012,14 +3018,14 @@ def migrate_provider_for_ssn_correction( # ssnCorrection record and fence are skipped (the run that deleted the record already wrote the # ssnCorrection), leaving only the target license delete. final_transaction_items = [] - if old_provider_data is not None: + if old_top_level_provider_data is not None: ssn_correction_update = ProviderUpdateData.create_new( { 'type': ProviderRecordType.PROVIDER_UPDATE, 'updateType': UpdateCategory.SSN_CORRECTION, 'providerId': new_provider_id, 'compact': compact, - 'previous': old_provider_data.to_dict(), + 'previous': old_top_level_provider_data.to_dict(), 'createDate': config.current_standard_datetime, 'updatedValues': {'ssnLastFour': new_ssn_last_four}, } @@ -3027,7 +3033,7 @@ def migrate_provider_for_ssn_correction( final_transaction_items.append(self._build_put_transaction_item(ssn_correction_update)) final_transaction_items.append( self._build_conditioned_old_provider_transaction_item( - old_provider_data=old_provider_data, + old_provider_data=old_top_level_provider_data, old_provider_records=old_provider_records, full_migration=full_migration, jurisdiction=jurisdiction, @@ -3045,6 +3051,7 @@ def migrate_provider_for_ssn_correction( # Small migration: commit everything as one all-or-nothing transaction, with no cross-transaction # replay window to reason about. The fence's dateOfUpdate condition failing rolls the whole # transaction back and raises for SQS retry. + self._log_ssn_migration_transaction_items('single-atomic-transaction', all_transaction_items) self._execute_batched_transactions(all_transaction_items) else: # Large migration: the operations cannot fit in a single atomic transaction, so run them as @@ -3052,16 +3059,19 @@ def migrate_provider_for_ssn_correction( # split across a batch boundary, so the old provider record and target license are always torn # down together. The fence's dateOfUpdate condition failing raises for SQS retry; the retry # re-reads current state and takes the now-correct branch. + self._log_ssn_migration_transaction_items('create', create_transaction_items) self._execute_batched_transactions(create_transaction_items) + self._log_ssn_migration_transaction_items('delete', delete_transaction_items) self._execute_batched_transactions(delete_transaction_items) + self._log_ssn_migration_transaction_items('final', final_transaction_items) self._execute_batched_transactions(final_transaction_items) return SsnCorrectionMigrationResult( migration_performed=True, full_migration=full_migration, old_provider_registered_email=( - old_provider_data.to_dict().get('compactConnectRegisteredEmailAddress') - if full_migration and old_provider_data is not None + old_top_level_provider_data.to_dict().get('compactConnectRegisteredEmailAddress') + if full_migration and old_top_level_provider_data is not None else None ), ) @@ -3072,6 +3082,67 @@ def _provider_record_key(record: CCDataClass) -> dict[str, str]: serialized = record.serialize_to_database_record() return {'pk': serialized['pk'], 'sk': serialized['sk']} + def _verify_full_migration_accounts_for_all_old_provider_records( + self, + *, + old_provider_records: ProviderUserRecords, + records_to_move: list[CCDataClass], + old_provider_data: ProviderData | None, + ) -> None: + """ + Verify that a full migration will leave nothing behind in the old provider's partition. + + Compares every record read from the old partition against the records selected for migration plus the + top-level provider record (deleted by the final transaction). Raises before any write if a record is + unaccounted for, since deleting the top-level provider record while leaving other records in the + partition would orphan them with no provider to belong to. + + :raises CCInternalException: If the old partition contains a record the migration would not move + """ + accounted_keys = { + (key['pk'], key['sk']) for key in (self._provider_record_key(record) for record in records_to_move) + } + if old_provider_data is not None: + old_provider_key = self._provider_record_key(old_provider_data) + accounted_keys.add((old_provider_key['pk'], old_provider_key['sk'])) + + unaccounted_keys = sorted( + (record['pk'], record['sk']) + for record in old_provider_records.provider_records + if (record['pk'], record['sk']) not in accounted_keys + ) + if unaccounted_keys: + logger.error( + 'Old provider partition contains records this migration does not know how to move; ' + 'aborting before any writes', + unaccounted_record_keys=unaccounted_keys, + ) + raise CCInternalException( + 'SSN correction migration aborted: the old provider has records the migration would orphan' + ) + + @staticmethod + def _log_ssn_migration_transaction_items(phase: str, transaction_items: list[dict]) -> None: + """ + Log the pk and sorted sks of the records a migration phase is about to create and delete, so the exact + set of items migrated between the two provider partitions can be reconstructed from the logs. + """ + created_sks_by_pk = {} + deleted_sks_by_pk = {} + for item in transaction_items: + if 'Put' in item: + record = item['Put']['Item'] + created_sks_by_pk.setdefault(record['pk']['S'], []).append(record['sk']['S']) + elif 'Delete' in item: + record_key = item['Delete']['Key'] + deleted_sks_by_pk.setdefault(record_key['pk']['S'], []).append(record_key['sk']['S']) + logger.info( + 'Executing SSN correction migration transactions', + phase=phase, + creating_items={pk: sorted(sks) for pk, sks in created_sks_by_pk.items()}, + deleting_items={pk: sorted(sks) for pk, sks in deleted_sks_by_pk.items()}, + ) + def _build_put_transaction_item(self, record: CCDataClass) -> dict: return { 'Put': { diff --git a/backend/compact-connect/lambdas/python/common/tests/function/test_data_client_ssn_correction.py b/backend/compact-connect/lambdas/python/common/tests/function/test_data_client_ssn_correction.py index c62679a2a7..dd9b69a4ab 100644 --- a/backend/compact-connect/lambdas/python/common/tests/function/test_data_client_ssn_correction.py +++ b/backend/compact-connect/lambdas/python/common/tests/function/test_data_client_ssn_correction.py @@ -1,4 +1,5 @@ # ruff: noqa: F403, F405 star import of test constants file +from collections import Counter from datetime import date, datetime from unittest.mock import patch @@ -15,6 +16,7 @@ NEW_SSN_LAST_FOUR = '6789' # aslp compact license type that is not the default 'speech-language pathologist' OTHER_LICENSE_TYPE = 'audiologist' +OTHER_LICENSE_TYPE_ABBREVIATION = 'aud' @mock_aws @@ -121,6 +123,123 @@ def test_full_migration_moves_all_records_and_empties_old_partition(self): self.assertIn(NEW_PROVIDER_ID, document_key) self.assertNotIn(DEFAULT_PROVIDER_ID, document_key) + def _put_records_associated_with_remaining_license(self): + """Store a second license of another type for the old provider, along with its own full set of + dependent records: a privilege, license/privilege update history, and adverse actions and + investigations against both the license and the privilege. In a partial migration targeting the + default license, none of these records may move. + """ + self.test_data_generator.put_default_license_record_in_provider_table({'licenseType': OTHER_LICENSE_TYPE}) + self.test_data_generator.put_default_privilege_record_in_provider_table({'licenseType': OTHER_LICENSE_TYPE}) + self.test_data_generator.put_default_license_update_record_in_provider_table( + {'licenseType': OTHER_LICENSE_TYPE} + ) + self.test_data_generator.put_default_privilege_update_record_in_provider_table( + {'licenseType': OTHER_LICENSE_TYPE} + ) + self.test_data_generator.put_default_adverse_action_record_in_provider_table( + { + 'actionAgainst': 'license', + 'jurisdiction': DEFAULT_LICENSE_JURISDICTION, + 'licenseType': OTHER_LICENSE_TYPE, + 'licenseTypeAbbreviation': OTHER_LICENSE_TYPE_ABBREVIATION, + 'adverseActionId': '11111111-1111-1111-1111-111111111111', + } + ) + self.test_data_generator.put_default_adverse_action_record_in_provider_table( + { + 'actionAgainst': 'privilege', + 'jurisdiction': DEFAULT_PRIVILEGE_JURISDICTION, + 'licenseType': OTHER_LICENSE_TYPE, + 'licenseTypeAbbreviation': OTHER_LICENSE_TYPE_ABBREVIATION, + 'adverseActionId': '22222222-2222-2222-2222-222222222222', + } + ) + self.test_data_generator.put_default_investigation_record_in_provider_table( + { + 'investigationAgainst': 'license', + 'jurisdiction': DEFAULT_LICENSE_JURISDICTION, + 'licenseType': OTHER_LICENSE_TYPE, + 'licenseTypeAbbreviation': OTHER_LICENSE_TYPE_ABBREVIATION, + 'investigationId': '33333333-3333-3333-3333-333333333333', + } + ) + self.test_data_generator.put_default_investigation_record_in_provider_table( + { + 'investigationAgainst': 'privilege', + 'jurisdiction': DEFAULT_PRIVILEGE_JURISDICTION, + 'licenseType': OTHER_LICENSE_TYPE, + 'licenseTypeAbbreviation': OTHER_LICENSE_TYPE_ABBREVIATION, + 'investigationId': '44444444-4444-4444-4444-444444444444', + } + ) + + def test_partial_migration_moves_only_records_associated_with_target_license(self): + """A partial migration must move ONLY the corrected license and its dependent records: the privileges + purchased against it, and the adverse action / investigation / update history records of the license + and those privileges. The remaining license's dependent records and the person-level records must all + stay in the old partition and must not be copied to the new one. + """ + self._put_full_old_provider_records() + self._put_records_associated_with_remaining_license() + + result = self._migrate() + + self.assertTrue(result.migration_performed) + self.assertFalse(result.full_migration) + + # the old provider keeps exactly the remaining license's records plus the person-level records + old_records = self._get_all_records_for_provider(DEFAULT_PROVIDER_ID) + expected_old_counts = { + 'provider': 1, + 'license': 1, + 'privilege': 1, + 'licenseUpdate': 1, + 'privilegeUpdate': 1, + 'adverseAction': 2, + 'investigation': 2, + 'militaryAffiliation': 1, + 'providerUpdate': 1, + } + self.assertEqual(expected_old_counts, Counter(record['type'] for record in old_records)) + # every license-scoped record left behind belongs to the remaining audiologist license + for record in old_records: + if 'licenseType' in record: + self.assertEqual( + OTHER_LICENSE_TYPE, + record['licenseType'], + f'{record["type"]} record for the target license was left behind: {record["sk"]}', + ) + + # the new provider received exactly the target license's records, a newly-created top-level provider + # record, and the ssnCorrection update; no audiologist-license or person-level record was copied over + new_records = self._get_all_records_for_provider(NEW_PROVIDER_ID) + expected_new_counts = { + 'provider': 1, + 'license': 1, + 'privilege': 1, + 'licenseUpdate': 1, + 'privilegeUpdate': 1, + 'adverseAction': 2, + 'investigation': 2, + 'providerUpdate': 1, + } + self.assertEqual(expected_new_counts, Counter(record['type'] for record in new_records)) + for record in new_records: + if 'licenseType' in record: + self.assertEqual( + DEFAULT_LICENSE_TYPE, + record['licenseType'], + f'{record["type"]} record not associated with the target license was moved: {record["sk"]}', + ) + + # only the migrated target license picks up the corrected ssnLastFour; the license remaining with the + # old provider still carries the last four of the (incorrect) SSN it was uploaded under + migrated_license = next(record for record in new_records if record['type'] == 'license') + self.assertEqual(NEW_SSN_LAST_FOUR, migrated_license['ssnLastFour']) + remaining_license = next(record for record in old_records if record['type'] == 'license') + self.assertEqual(DEFAULT_SSN_LAST_FOUR, remaining_license['ssnLastFour']) + def test_partial_migration_when_another_license_type_remains_in_same_state(self): self._put_full_old_provider_records() # a second license of another type in the same jurisdiction, with no privileges @@ -185,6 +304,52 @@ def test_partial_migration_when_license_in_other_jurisdiction_remains(self): self.assertEqual(1, len(new_licenses)) self.assertEqual(DEFAULT_LICENSE_JURISDICTION, new_licenses[0]['jurisdiction']) + def test_partial_migration_repopulates_old_provider_record_from_remaining_license(self): + """On a partial migration the old top-level provider record must be rebuilt from the license that + remains: its demographic and status fields, its jurisdiction, and the privilege jurisdictions of the + privileges that remain with it, while the provider's registration fields are preserved. + """ + self.test_data_generator.put_default_provider_record_in_provider_table() + # the target license (oh/slp) with a privilege in ne, both of which migrate + self.test_data_generator.put_default_license_record_in_provider_table() + self.test_data_generator.put_default_privilege_record_in_provider_table() + # the remaining license (oh/audiologist) carries different demographic/status values than the target + # license, and has its own privilege in ky that stays behind + self.test_data_generator.put_default_license_record_in_provider_table( + { + 'licenseType': OTHER_LICENSE_TYPE, + 'givenName': 'Remaininggivenname', + 'familyName': 'Remainingfamilyname', + 'dateOfExpiration': date.fromisoformat('2035-01-01'), + 'jurisdictionUploadedLicenseStatus': 'inactive', + 'jurisdictionUploadedCompactEligibility': 'ineligible', + } + ) + self.test_data_generator.put_default_privilege_record_in_provider_table( + {'licenseType': OTHER_LICENSE_TYPE, 'jurisdiction': 'ky'} + ) + + result = self._migrate() + + self.assertTrue(result.migration_performed) + self.assertFalse(result.full_migration) + + # the old provider record was repopulated from the remaining audiologist license + old_provider_record = self._get_records_of_type(DEFAULT_PROVIDER_ID, 'provider')[0] + self.assertEqual(DEFAULT_LICENSE_JURISDICTION, old_provider_record['licenseJurisdiction']) + self.assertEqual('Remaininggivenname', old_provider_record['givenName']) + self.assertEqual('Remainingfamilyname', old_provider_record['familyName']) + self.assertEqual('2035-01-01', old_provider_record['dateOfExpiration']) + self.assertEqual('inactive', old_provider_record['jurisdictionUploadedLicenseStatus']) + self.assertEqual('ineligible', old_provider_record['jurisdictionUploadedCompactEligibility']) + + # privilege jurisdictions reflect only the privilege that stayed behind (ky), not the migrated one (ne) + self.assertEqual({'ky'}, set(old_provider_record['privilegeJurisdictions'])) + + # the provider's registration state is preserved through the repopulation + self.assertEqual(DEFAULT_REGISTERED_EMAIL_ADDRESS, old_provider_record['compactConnectRegisteredEmailAddress']) + self.assertEqual(DEFAULT_LICENSE_JURISDICTION, old_provider_record['currentHomeJurisdiction']) + def test_migration_leaves_new_provider_pre_existing_records_untouched(self): # the new provider already has records from another state pre_existing_provider = self.test_data_generator.put_default_provider_record_in_provider_table( @@ -309,6 +474,90 @@ def test_ssn_correction_provider_update_content(self): # previous holds the snapshot of the old provider record, including the old ssnLastFour self.assertEqual(DEFAULT_SSN_LAST_FOUR, ssn_correction['previous']['ssnLastFour']) self.assertEqual(NEW_SSN_LAST_FOUR, ssn_correction['updatedValues']['ssnLastFour']) + # the 'previous' object is a verbatim snapshot of the old provider record, so it must retain the OLD + # provider id (unlike migrated update records, whose embedded snapshots are re-keyed) + self.assertEqual(DEFAULT_PROVIDER_ID, ssn_correction['previous']['providerId']) + + @classmethod + def _find_paths_containing_value(cls, value, target: str, path: str = '') -> list[str]: + """Recursively find the paths of every string field within a record that contains the target value.""" + paths = [] + if isinstance(value, dict): + for key, nested_value in value.items(): + paths.extend(cls._find_paths_containing_value(nested_value, target, f'{path}.{key}' if path else key)) + elif isinstance(value, (list, set, tuple)): + for index, nested_value in enumerate(value): + paths.extend(cls._find_paths_containing_value(nested_value, target, f'{path}[{index}]')) + elif isinstance(value, str) and target in value: + paths.append(path) + return paths + + def test_migration_rekeys_every_provider_id_reference_except_ssn_correction_previous_snapshot(self): + """Every provider id reference in the migrated records — including the 'previous' snapshots embedded + in update records and military affiliation document keys — must be re-keyed to the new provider id. + The single intentional exception is the ssnCorrection provider update record, whose 'previous' object + snapshots the old provider record verbatim, old provider id included. + """ + self._put_full_old_provider_records() + + self._migrate() + + new_records = self._get_all_records_for_provider(NEW_PROVIDER_ID) + + # every record carries the new provider id + for record in new_records: + self.assertEqual(NEW_PROVIDER_ID, record['providerId'], f'providerId not re-keyed on {record["sk"]}') + + # of the update records, only provider updates embed a provider id in their 'previous' snapshot + # (license/privilege update snapshots do not include one); the migrated provider update history + # record's snapshot must be re-keyed + migrated_provider_updates = [ + record + for record in new_records + if record['type'] == 'providerUpdate' and record['updateType'] != 'ssnCorrection' + ] + self.assertEqual(1, len(migrated_provider_updates)) + self.assertEqual( + NEW_PROVIDER_ID, + migrated_provider_updates[0]['previous']['providerId'], + f'previous.providerId not re-keyed on {migrated_provider_updates[0]["sk"]}', + ) + + # catch-all regression net: scan every field of every migrated record for the old provider id. It may + # appear in exactly one place across the entire new partition: the ssnCorrection update's 'previous' + # snapshot. Anything else is a field the migration failed to re-key. + ssn_correction_sk = next(record['sk'] for record in new_records if record.get('updateType') == 'ssnCorrection') + old_provider_id_locations = { + (record['sk'], path) + for record in new_records + for path in self._find_paths_containing_value(record, DEFAULT_PROVIDER_ID) + } + self.assertEqual({(ssn_correction_sk, 'previous.providerId')}, old_provider_id_locations) + + def test_full_migration_raises_when_old_partition_contains_record_migration_cannot_move(self): + """A full migration deletes the old top-level provider record, so any record the migration does not + know how to move (e.g. a record type introduced after the migration logic was written) would be left + orphaned in a partition with no provider record. The migration must detect this and fail before + writing anything, leaving the old provider fully intact for a retry after a code fix. + """ + self._put_full_old_provider_records() + self.config.provider_table.put_item( + Item={ + 'pk': f'{DEFAULT_COMPACT}#PROVIDER#{DEFAULT_PROVIDER_ID}', + 'sk': f'{DEFAULT_COMPACT}#PROVIDER#some-future-record-type#1', + 'type': 'someFutureRecordType', + 'providerId': DEFAULT_PROVIDER_ID, + 'compact': DEFAULT_COMPACT, + } + ) + old_records_before = self._get_all_records_for_provider(DEFAULT_PROVIDER_ID) + + with self.assertRaises(CCInternalException): + self._migrate() + + # nothing was written or deleted + self.assertEqual(old_records_before, self._get_all_records_for_provider(DEFAULT_PROVIDER_ID)) + self.assertEqual([], self._get_all_records_for_provider(NEW_PROVIDER_ID)) def test_migration_raises_when_old_provider_record_modified_concurrently(self): self._put_full_old_provider_records() From 8c41f7f00917c383bd9b5ad3f71f521994a793ef Mon Sep 17 00:00:00 2001 From: Landon Shumway Date: Tue, 14 Jul 2026 07:14:29 -0700 Subject: [PATCH 19/41] Feedback - add race condition check for top level provider creation --- .../cc_common/data_model/data_client.py | 14 +++++-- .../test_data_client_ssn_correction.py | 37 ++++++++++++++++++- 2 files changed, 47 insertions(+), 4 deletions(-) diff --git a/backend/compact-connect/lambdas/python/common/cc_common/data_model/data_client.py b/backend/compact-connect/lambdas/python/common/cc_common/data_model/data_client.py index fbd973bb0c..80da80fede 100644 --- a/backend/compact-connect/lambdas/python/common/cc_common/data_model/data_client.py +++ b/backend/compact-connect/lambdas/python/common/cc_common/data_model/data_client.py @@ -2995,7 +2995,10 @@ def migrate_provider_for_ssn_correction( create_transaction_items.append(self._build_put_transaction_item(rekeyed_record)) # Create a top-level provider record for the new provider only if it does not already have one; a - # pre-existing record is never modified + # pre-existing record is never modified. The existence check and this Put are not atomic with each + # other, so the Put is conditioned on the record still being absent: if a concurrent write (e.g. a + # different migration into the same new provider id) creates one in between, this Put fails instead of + # silently clobbering it, and the transaction raises for SQS retry to re-read the now-current state. new_provider_record = self._build_new_provider_record_if_absent( compact=compact, new_provider_id=new_provider_id, @@ -3003,7 +3006,11 @@ def migrate_provider_for_ssn_correction( rekeyed_privileges=rekeyed_privileges, ) if new_provider_record is not None: - create_transaction_items.append(self._build_put_transaction_item(new_provider_record)) + create_transaction_items.append( + self._build_put_transaction_item( + new_provider_record, condition={'ConditionExpression': 'attribute_not_exists(pk)'} + ) + ) # deletes: the moved records on the old provider, except the target license and the top-level provider # record (both handled in the final group). @@ -3143,11 +3150,12 @@ def _log_ssn_migration_transaction_items(phase: str, transaction_items: list[dic deleting_items={pk: sorted(sks) for pk, sks in deleted_sks_by_pk.items()}, ) - def _build_put_transaction_item(self, record: CCDataClass) -> dict: + def _build_put_transaction_item(self, record: CCDataClass, condition: dict | None = None) -> dict: return { 'Put': { 'TableName': self.config.provider_table_name, 'Item': TypeSerializer().serialize(record.serialize_to_database_record())['M'], + **(condition or {}), } } diff --git a/backend/compact-connect/lambdas/python/common/tests/function/test_data_client_ssn_correction.py b/backend/compact-connect/lambdas/python/common/tests/function/test_data_client_ssn_correction.py index dd9b69a4ab..482c3494fc 100644 --- a/backend/compact-connect/lambdas/python/common/tests/function/test_data_client_ssn_correction.py +++ b/backend/compact-connect/lambdas/python/common/tests/function/test_data_client_ssn_correction.py @@ -4,7 +4,7 @@ from unittest.mock import patch from boto3.dynamodb.conditions import Key -from cc_common.exceptions import CCInternalException +from cc_common.exceptions import CCInternalException, CCNotFoundException from common_test.test_constants import * from moto import mock_aws @@ -382,6 +382,41 @@ def test_migration_leaves_new_provider_pre_existing_records_untouched(self): new_provider_record = self._get_records_of_type(NEW_PROVIDER_ID, 'provider')[0] self.assertEqual(pre_existing_provider.serialize_to_database_record(), new_provider_record) + def test_migration_raises_when_new_provider_record_created_concurrently(self): + """The absent-check for the new provider's top-level record and the Put that creates one are not + atomic with each other. If a concurrent write creates that record in between, the Put must be + conditioned on the record still being absent so it fails instead of silently overwriting the + concurrently-created record. + """ + self._put_full_old_provider_records() + + # a competing write creates the new provider's top-level record after this migration's absent-check + # would have run, but before its transaction commits + pre_existing_provider = self.test_data_generator.put_default_provider_record_in_provider_table( + {'providerId': NEW_PROVIDER_ID, 'licenseJurisdiction': 'ky', 'privilegeJurisdictions': set()} + ) + + real_get_provider_top_level_record = self.config.data_client.get_provider_top_level_record + + def _stale_absent_check_for_new_provider(*, compact, provider_id): + if str(provider_id) == NEW_PROVIDER_ID: + raise CCNotFoundException('Provider not found') + return real_get_provider_top_level_record(compact=compact, provider_id=provider_id) + + with patch.object( + self.config.data_client, 'get_provider_top_level_record', side_effect=_stale_absent_check_for_new_provider + ): + with self.assertRaises(CCInternalException): + self._migrate() + + # the conditioned Put failed, so nothing was written or deleted: the old provider is intact and the + # concurrently-created new provider record is untouched + self.assertEqual(1, len(self._get_records_of_type(DEFAULT_PROVIDER_ID, 'provider'))) + self.assertEqual(1, len(self._get_records_of_type(DEFAULT_PROVIDER_ID, 'license'))) + new_provider_records = self._get_records_of_type(NEW_PROVIDER_ID, 'provider') + self.assertEqual(1, len(new_provider_records)) + self.assertEqual(pre_existing_provider.serialize_to_database_record(), new_provider_records[0]) + def test_no_op_when_old_provider_has_no_matching_license(self): self.test_data_generator.put_default_provider_record_in_provider_table() self.test_data_generator.put_default_license_record_in_provider_table({'licenseType': OTHER_LICENSE_TYPE}) From a8b5a85737c03cf9633a229299a734a9e5d3f907 Mon Sep 17 00:00:00 2001 From: Landon Shumway Date: Tue, 14 Jul 2026 07:49:59 -0700 Subject: [PATCH 20/41] feedback - smoke test improvements --- .../tests/smoke/ssn_migration_smoke_tests.py | 136 +++++++++++++----- 1 file changed, 101 insertions(+), 35 deletions(-) diff --git a/backend/compact-connect/tests/smoke/ssn_migration_smoke_tests.py b/backend/compact-connect/tests/smoke/ssn_migration_smoke_tests.py index a894b2b80e..910fe61607 100644 --- a/backend/compact-connect/tests/smoke/ssn_migration_smoke_tests.py +++ b/backend/compact-connect/tests/smoke/ssn_migration_smoke_tests.py @@ -223,6 +223,30 @@ def _verify_all_records_migrated( print(f'Verified all {len(source_normalized)} migratable records now exist under provider {target_provider_id}') +def _verify_license_ssn_last_four(*, records: list[dict], expected_ssn_last_four: str, license_type: str | None = None): + """Verify every license record (optionally filtered to a single license type) carries the expected + ssnLastFour. This is checked separately from _verify_all_records_migrated because ssnLastFour is + deliberately excluded from that comparison (it legitimately differs before and after a migration). + """ + license_records = [ + record + for record in records + if record['type'] == 'license' and (license_type is None or record['licenseType'] == license_type) + ] + if not license_records: + raise SmokeTestFailureException('No license record found to verify ssnLastFour against') + mismatched = [ + f'{record["licenseType"]}: {record["ssnLastFour"]}' + for record in license_records + if record['ssnLastFour'] != expected_ssn_last_four + ] + if mismatched: + raise SmokeTestFailureException( + f'Expected ssnLastFour {expected_ssn_last_four} on all license records; found: {mismatched}' + ) + print(f'Verified ssnLastFour {expected_ssn_last_four} on {len(license_records)} license record(s)') + + def _verify_all_s3_objects_migrated(*, source_objects: dict[str, bytes], compact: str, target_provider_id: str): """Verify every S3 object captured from the source keyspace exists, byte-for-byte, under the target keyspace.""" target_objects = _get_provider_s3_objects(compact, target_provider_id) @@ -339,18 +363,35 @@ def _find_new_provider_id(): def _restore_test_provider_account(compact: str, provider_id: str, baseline_provider_record: dict): """Restore the shared test provider account after a full migration deleted its Cognito user. - Recreates the provider Cognito user pointed at the given provider id and restores the registration - fields on the top-level provider record, which are intentionally dropped by the migration (in real - usage the practitioner re-registers). + Recreates the provider Cognito user pointed at the given provider id (if it does not already exist) and + restores the registration fields on the top-level provider record (if they are not already present), + which are intentionally dropped by the migration (in real usage the practitioner re-registers). The two + are independent: either can already be in place (e.g. a previous restore attempt partially completed) + while the other still needs restoring, so each is checked and repaired on its own. - If the Cognito user already exists, this is a no-op: 'custom:compact' and 'custom:providerId' are - immutable Cognito custom attributes, so an existing user cannot be re-pointed at a different provider - id. A pre-existing user only happens when the full migration never actually deleted it (e.g. the test - failed before reaching that step, or this is being called defensively after a failure), in which case - the account is already valid and does not need restoring. + If the Cognito user already exists, its 'custom:providerId' attribute (immutable once set) is validated + against the expected provider id. A match means the account is already valid and is left as-is. A + mismatch means a stray user from an unrelated run is bound to the wrong provider id; since the attribute + cannot be repointed, this is raised for manual cleanup rather than silently left in a broken state. """ username = config.test_provider_user_username - try: + + if _cognito_user_exists(username): + existing_user = config.cognito_client.admin_get_user( + UserPoolId=config.cognito_provider_user_pool_id, Username=username + ) + bound_provider_id = next( + (attr['Value'] for attr in existing_user['UserAttributes'] if attr['Name'] == 'custom:providerId'), + None, + ) + if bound_provider_id != provider_id: + raise SmokeTestFailureException( + f'Test provider Cognito user already exists but is bound to provider id {bound_provider_id}, ' + f'not the expected {provider_id}. custom:providerId is immutable, so this cannot be ' + f'auto-repaired: manually delete the Cognito user {username} and re-run.' + ) + print(f'Test provider Cognito user already exists and is correctly bound to provider id {provider_id}') + else: config.cognito_client.admin_create_user( UserPoolId=config.cognito_provider_user_pool_id, Username=username, @@ -363,38 +404,35 @@ def _restore_test_provider_account(compact: str, provider_id: str, baseline_prov MessageAction='SUPPRESS', ) print(f'Recreated test provider Cognito user, pointed at provider id {provider_id}') - except ClientError as e: - if e.response['Error']['Code'] != 'UsernameExistsException': - raise - print( - f'Test provider Cognito user already exists; leaving it as-is (custom:providerId is immutable, ' - f'so it cannot be re-pointed at provider id {provider_id})' - ) - return - config.cognito_client.admin_set_user_password( - UserPoolId=config.cognito_provider_user_pool_id, - Username=username, - Password=config.test_provider_user_password, - Permanent=True, - ) - # clear the cached provider token so the next /me call performs a fresh login against the restored user - os.environ.pop('TEST_PROVIDER_USER_ID_TOKEN', None) + config.cognito_client.admin_set_user_password( + UserPoolId=config.cognito_provider_user_pool_id, + Username=username, + Password=config.test_provider_user_password, + Permanent=True, + ) + # clear the cached provider token so the next /me call performs a fresh login against the restored user + os.environ.pop('TEST_PROVIDER_USER_ID_TOKEN', None) - # restore the registration fields on the provider record, which populate_provider_record does not carry over + # restore the registration fields on the provider record, which populate_provider_record does not carry + # over, if they are not already present - independent of whether the Cognito user needed recreating + provider_key = {'pk': f'{compact}#PROVIDER#{provider_id}', 'sk': f'{compact}#PROVIDER'} + current_provider_record = get_provider_user_dynamodb_table().get_item(Key=provider_key).get('Item', {}) registration_fields = { field: baseline_provider_record[field] for field in ('compactConnectRegisteredEmailAddress', 'currentHomeJurisdiction') - if field in baseline_provider_record + if field in baseline_provider_record and current_provider_record.get(field) != baseline_provider_record[field] } if registration_fields: get_provider_user_dynamodb_table().update_item( - Key={'pk': f'{compact}#PROVIDER#{provider_id}', 'sk': f'{compact}#PROVIDER'}, + Key=provider_key, UpdateExpression='SET ' + ', '.join(f'#{i} = :{i}' for i in range(len(registration_fields))), ExpressionAttributeNames={f'#{i}': field for i, field in enumerate(registration_fields)}, ExpressionAttributeValues={f':{i}': value for i, value in enumerate(registration_fields.values())}, ) print(f'Restored registration fields on provider record {provider_id}: {sorted(registration_fields)}') + else: + print(f'Registration fields on provider record {provider_id} are already up to date; nothing to restore') def _cognito_user_exists(username: str) -> bool: @@ -541,6 +579,14 @@ def test_full_ssn_migration_roundtrip(): ) staff_headers = get_staff_user_auth_headers(TEST_STAFF_USER_EMAIL) client_headers, test_app_client_id = _create_test_app_client_headers(TEST_APP_CLIENT_NAME, compact, jurisdiction) + # Tracks the provider id that currently holds the test provider's records, so the 'finally' block below + # can restore the Cognito account against it no matter where in the test a failure occurs. + # _migrate_test_provider_to_ssn only returns a provider id after polling DynamoDB to confirm records + # actually live there, so this is always accurate: it starts at original_provider_id, and advances only + # when a migration step below actually completes (not merely gets attempted). Named distinctly from + # _migrate_test_provider_to_ssn's 'current_provider_id' parameter, which is an unrelated per-call + # argument (the id being migrated FROM), not this function-scoped tracker. + last_known_provider_id = original_provider_id try: # Step 3 + 4: migrate to the corrected SSN and verify everything moved migrated_provider_id = _migrate_test_provider_to_ssn( @@ -552,6 +598,7 @@ def test_full_ssn_migration_roundtrip(): corrected_ssn=FULL_MIGRATION_CORRECTED_SSN, previous_ssn=config.test_provider_mock_ssn, ) + last_known_provider_id = migrated_provider_id migrated_records = _get_provider_dynamo_records(compact, migrated_provider_id) _verify_all_records_migrated( source_records=pre_migration_records, @@ -564,6 +611,11 @@ def test_full_ssn_migration_roundtrip(): for record in migrated_records ): raise SmokeTestFailureException('No ssnCorrection provider update record found after migration') + # ssnLastFour is deliberately excluded from _verify_all_records_migrated's record comparison (it + # legitimately differs pre/post migration), so it must be checked explicitly here + _verify_license_ssn_last_four( + records=migrated_records, expected_ssn_last_four=FULL_MIGRATION_CORRECTED_SSN[-4:] + ) _verify_all_s3_objects_migrated( source_objects=pre_migration_s3_objects, compact=compact, target_provider_id=migrated_provider_id ) @@ -583,24 +635,26 @@ def test_full_ssn_migration_roundtrip(): f'Roundtrip migration did not return to the original provider id. ' f'Expected {original_provider_id}, got {returned_provider_id}' ) + last_known_provider_id = returned_provider_id + returned_records = _get_provider_dynamo_records(compact, original_provider_id) _verify_all_records_migrated( source_records=pre_migration_records, source_provider_id=original_provider_id, - target_records=_get_provider_dynamo_records(compact, original_provider_id), + target_records=returned_records, target_provider_id=original_provider_id, ) + _verify_license_ssn_last_four( + records=returned_records, expected_ssn_last_four=config.test_provider_mock_ssn[-4:] + ) _verify_all_s3_objects_migrated( source_objects=pre_migration_s3_objects, compact=compact, target_provider_id=original_provider_id ) print('Roundtrip migration completed; all records and documents are back under the original provider id') finally: # Restore the shared test provider account no matter what state the test failed in: point the - # Cognito user at whichever provider id currently holds the provider's records - current_ids = _query_provider_ids_by_name( - staff_headers, compact, provider_data['givenName'], provider_data['familyName'] - ) - restore_provider_id = current_ids[0] if current_ids else original_provider_id - _restore_test_provider_account(compact, restore_provider_id, baseline_provider_record) + # Cognito user at whichever provider id currently holds the provider's records (last_known_provider_id, + # tracked above - see its comment for why this is reliable without an extra lookup here). + _restore_test_provider_account(compact, last_known_provider_id, baseline_provider_record) delete_test_staff_user(TEST_STAFF_USER_EMAIL, user_sub=test_staff_user_sub, compact=compact) delete_test_app_client(test_app_client_id) @@ -726,6 +780,12 @@ def _find_new_provider_id(): raise SmokeTestFailureException( f'Expected only the OTA license to remain under the old provider id; found: {old_license_types}' ) + # the OTA license was never touched by the correction, so it must still carry the original SSN + _verify_license_ssn_last_four( + records=old_provider_records, + expected_ssn_last_four=PARTIAL_MIGRATION_ORIGINAL_SSN[-4:], + license_type=OTA_LICENSE_TYPE, + ) print(f'Verified the OTA license and provider record remain under old provider {old_provider_id}') new_provider_records = _get_provider_dynamo_records(PARTIAL_MIGRATION_COMPACT, new_provider_id) @@ -737,6 +797,12 @@ def _find_new_provider_id(): raise SmokeTestFailureException( f'Expected only the OT license under the new provider id; found: {new_license_types}' ) + # the OT license was migrated with the corrected SSN, so it must carry the corrected last four + _verify_license_ssn_last_four( + records=new_provider_records, + expected_ssn_last_four=PARTIAL_MIGRATION_CORRECTED_SSN[-4:], + license_type=OT_LICENSE_TYPE, + ) print(f'Verified the OT license and a new provider record exist under new provider {new_provider_id}') print('Partial migration smoke test passed.') finally: From fdb2c6277a0dc01afe9ca25b19c0e94286a2c538 Mon Sep 17 00:00:00 2001 From: Landon Shumway Date: Tue, 14 Jul 2026 07:55:17 -0700 Subject: [PATCH 21/41] feedback - cleanup app client if auth failure occurs --- .../tests/smoke/ssn_migration_smoke_tests.py | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/backend/compact-connect/tests/smoke/ssn_migration_smoke_tests.py b/backend/compact-connect/tests/smoke/ssn_migration_smoke_tests.py index 910fe61607..7b62ce58a4 100644 --- a/backend/compact-connect/tests/smoke/ssn_migration_smoke_tests.py +++ b/backend/compact-connect/tests/smoke/ssn_migration_smoke_tests.py @@ -126,12 +126,21 @@ def _upload_license_records(client_headers: dict, compact: str, jurisdiction: st def _create_test_app_client_headers(client_name: str, compact: str, jurisdiction: str) -> tuple[dict, str]: - """Create a state IT-system test app client and return (auth headers, client_id) for later cleanup.""" + """Create a state IT-system test app client and return (auth headers, client_id) for later cleanup. + + If token acquisition fails after the app client was created, the app client is deleted before the + error propagates - otherwise a caller would never receive a client_id to clean it up with. + """ client_credentials = create_test_app_client(client_name, compact, jurisdiction) - client_headers = get_client_auth_headers( - client_credentials['client_id'], client_credentials['client_secret'], compact, jurisdiction - ) - return client_headers, client_credentials['client_id'] + client_id = client_credentials['client_id'] + try: + client_headers = get_client_auth_headers( + client_id, client_credentials['client_secret'], compact, jurisdiction + ) + except Exception: + delete_test_app_client(client_id) + raise + return client_headers, client_id def _get_provider_dynamo_records(compact: str, provider_id: str) -> list[dict]: From 645abca7916c098ec56a2dca7c2aec776c9073ba Mon Sep 17 00:00:00 2001 From: Landon Shumway Date: Tue, 14 Jul 2026 12:59:10 -0700 Subject: [PATCH 22/41] feedback - update email body to include note about privileges --- .../lambdas/nodejs/lib/email/email-notification-service.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/compact-connect/lambdas/nodejs/lib/email/email-notification-service.ts b/backend/compact-connect/lambdas/nodejs/lib/email/email-notification-service.ts index 8a423346a4..5005174877 100644 --- a/backend/compact-connect/lambdas/nodejs/lib/email/email-notification-service.ts +++ b/backend/compact-connect/lambdas/nodejs/lib/email/email-notification-service.ts @@ -455,7 +455,7 @@ export class EmailNotificationService extends BaseEmailService { const report = this.getNewEmailTemplate(); const subject = `Action Required: Registration Update - CompactConnect`; const registrationUrl = `${environmentVariableService.getUiBasePathUrl()}/register`; - const bodyText = `Your state licensing board recently corrected the information on one of your license records in the CompactConnect system. As part of this correction, you will need to register again with your license record.\n\nTo continue using CompactConnect, please register again using the link below:\n\n${registrationUrl}\n\nIf you have any questions, please contact your state licensing board.`; + const bodyText = `Your state licensing board recently corrected the information on one of your license records in the CompactConnect system. As part of this correction, you will need to register again with your license record.\n\nAny active privileges you currently hold remain active and unaffected by this change, so you may continue practicing under them.\n\nTo continue using CompactConnect, please register again using the link below:\n\n${registrationUrl}\n\nIf you have any questions, please contact your state licensing board.`; this.insertHeader(report, 'Registration Update Required'); this.insertBody(report, bodyText, 'center', true); From 7e9b368a6fba435fa9267a15f5d2cf36fd0f078a Mon Sep 17 00:00:00 2001 From: Landon Shumway Date: Tue, 14 Jul 2026 13:00:06 -0700 Subject: [PATCH 23/41] Add note to README FAQ about how to correct SSN for provider --- backend/compact-connect/docs/README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/backend/compact-connect/docs/README.md b/backend/compact-connect/docs/README.md index 23d094f5bd..fab8a17348 100644 --- a/backend/compact-connect/docs/README.md +++ b/backend/compact-connect/docs/README.md @@ -108,7 +108,9 @@ If data is not available for a required field, that particular license record ca ### Can we upload the same licenses multiple times? What if their information changes? -Yes. CompactConnect is designed to automatically detect and track changes to license records over time. When you upload a license record, CompactConnect will determine if the record currently exists in the CompactConnect database using the provided SSN to match with any existing licensee in the system, and create the record if not found. If the license record already exists, CompactConnect will check the differences between the existing record in the system and changes uploaded by the state, and apply the changes accordingly. +Yes. CompactConnect is designed to automatically detect and track changes to license records over time. The Social Security Number (SSN) is the unique identifier CompactConnect uses to create and match individual practitioner accounts. When you upload a license record, CompactConnect uses the provided SSN to determine whether that practitioner already has an account in the system, creating one if not found. If the practitioner's account already exists, CompactConnect will check the differences between the existing license record and the changes uploaded by the state, and apply the changes accordingly. + +Because accounts are matched on SSN, simply changing the SSN in your state's system and then uploading the corrected license will **not** update the practitioner's existing CompactConnect account. It will create a brand new, separate account under the new SSN and leave the original account (and any privileges tied to it) unchanged. If a license was previously uploaded with an incorrect SSN, use the `previousSSN` field (see the field table above) when uploading the corrected SSN so CompactConnect can migrate the practitioner's existing account instead of creating a duplicate. ### Which of these license values will be publicly visible? From 5862b754579247728c31fe6b4c71caf6e10df605 Mon Sep 17 00:00:00 2001 From: Landon Shumway Date: Tue, 14 Jul 2026 14:32:43 -0700 Subject: [PATCH 24/41] Suppress email notification to smoke test staff users --- backend/compact-connect/tests/smoke/smoke_common.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/backend/compact-connect/tests/smoke/smoke_common.py b/backend/compact-connect/tests/smoke/smoke_common.py index 6743b459de..b512be0c07 100644 --- a/backend/compact-connect/tests/smoke/smoke_common.py +++ b/backend/compact-connect/tests/smoke/smoke_common.py @@ -62,6 +62,9 @@ def get_sub_from_attributes(user_attributes: list): Username=email, UserAttributes=[{'Name': 'email', 'Value': email}], TemporaryPassword=_TEMP_STAFF_PASSWORD, + # these are fake test addresses (e.g. @smokeTestFakeEmail.com); suppress Cognito's welcome + # message so we don't send mail to (and bounce against) addresses that can't receive it + MessageAction='SUPPRESS', ) logger.info(f"Created staff user, '{email}'. Setting password.") # set this to simplify login flow for user From 2c0951fb51b045ea8748ce0c021b9cf75a1e8fd2 Mon Sep 17 00:00:00 2001 From: Landon Shumway Date: Tue, 14 Jul 2026 14:34:21 -0700 Subject: [PATCH 25/41] Update API specs to latest --- .../docs/api-specification/latest-oas30.json | 1039 +- .../api-specification/latest-oas30.json | 18397 +++++++--------- .../internal/postman/postman-collection.json | 318 +- .../docs/postman/postman-collection.json | 38 +- .../api-specification/latest-oas30.json | 164 +- 5 files changed, 9171 insertions(+), 10785 deletions(-) diff --git a/backend/compact-connect/docs/api-specification/latest-oas30.json b/backend/compact-connect/docs/api-specification/latest-oas30.json index b1996174fa..b579d0fb57 100644 --- a/backend/compact-connect/docs/api-specification/latest-oas30.json +++ b/backend/compact-connect/docs/api-specification/latest-oas30.json @@ -2,11 +2,14 @@ "openapi": "3.0.1", "info": { "title": "StateApi", - "version": "2025-09-11T20:54:37Z" + "version": "2026-07-14T15:20:26Z" }, "servers": [ { - "url": "https://state-api.beta.compactconnect.org" + "url": "https://state-api.beta.compactconnect.org", + "x-amazon-apigateway-endpoint-configuration": { + "disableExecuteApiEndpoint": true + } } ], "paths": { @@ -42,7 +45,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/SandboStatewYA2yiuq5Kyj" + "$ref": "#/components/schemas/TestSStatejhIAEk3qM7mR" } } }, @@ -54,7 +57,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/SandboStatecjpoBVtvh5nr" + "$ref": "#/components/schemas/TestSStatej0rhnQc3na8L" } } } @@ -64,7 +67,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/SandboStateT5uVnUWmQEFs" + "$ref": "#/components/schemas/TestSStatecAgwR1yswwcK" } } } @@ -72,13 +75,19 @@ }, "security": [ { - "SandboxStateAPIStackStateApiStateAuthAuthorizer7F83A6D3": [ + "TestBackendPipelineStackTestStateAPIStackStateApiStateAuthAuthorizerCFF4E444": [ "aslp/write", "al/aslp.write", "ak/aslp.write", "ar/aslp.write", "co/aslp.write", "de/aslp.write", + "fl/aslp.write", + "ga/aslp.write", + "id/aslp.write", + "in/aslp.write", + "ia/aslp.write", + "ks/aslp.write", "ky/aslp.write", "la/aslp.write", "me/aslp.write", @@ -86,25 +95,95 @@ "mn/aslp.write", "ms/aslp.write", "mo/aslp.write", + "mt/aslp.write", "ne/aslp.write", + "nh/aslp.write", + "nc/aslp.write", "oh/aslp.write", + "ok/aslp.write", + "ri/aslp.write", + "sc/aslp.write", + "tn/aslp.write", + "ut/aslp.write", + "vt/aslp.write", + "va/aslp.write", + "vi/aslp.write", + "wa/aslp.write", + "wv/aslp.write", + "wi/aslp.write", + "wy/aslp.write", "octp/write", "al/octp.write", "ar/octp.write", + "az/octp.write", + "co/octp.write", + "de/octp.write", + "ga/octp.write", + "ia/octp.write", + "in/octp.write", "ky/octp.write", "la/octp.write", + "me/octp.write", + "md/octp.write", + "mn/octp.write", "ms/octp.write", + "mo/octp.write", + "mt/octp.write", "ne/octp.write", + "nh/octp.write", + "nc/octp.write", + "nd/octp.write", "oh/octp.write", + "ri/octp.write", + "sc/octp.write", + "sd/octp.write", + "tn/octp.write", + "ut/octp.write", + "vt/octp.write", + "va/octp.write", + "wa/octp.write", + "wv/octp.write", + "wi/octp.write", + "wy/octp.write", "coun/write", "al/coun.write", "ar/coun.write", + "az/coun.write", + "co/coun.write", + "ct/coun.write", + "dc/coun.write", + "de/coun.write", "fl/coun.write", "ga/coun.write", + "ia/coun.write", + "in/coun.write", + "ks/coun.write", "ky/coun.write", + "la/coun.write", + "me/coun.write", + "md/coun.write", + "mn/coun.write", + "ms/coun.write", + "mo/coun.write", + "mt/coun.write", "ne/coun.write", + "nh/coun.write", + "nj/coun.write", + "nc/coun.write", + "nd/coun.write", "oh/coun.write", - "ut/coun.write" + "ok/coun.write", + "ri/coun.write", + "sc/coun.write", + "sd/coun.write", + "tn/coun.write", + "ut/coun.write", + "vt/coun.write", + "va/coun.write", + "wa/coun.write", + "wv/coun.write", + "wi/coun.write", + "wy/coun.write" ] } ] @@ -144,7 +223,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/SandboState4XO9paC5FgIb" + "$ref": "#/components/schemas/TestSStateqdOWiiW9X7Lu" } } } @@ -152,13 +231,19 @@ }, "security": [ { - "SandboxStateAPIStackStateApiStateAuthAuthorizer7F83A6D3": [ + "TestBackendPipelineStackTestStateAPIStackStateApiStateAuthAuthorizerCFF4E444": [ "aslp/write", "al/aslp.write", "ak/aslp.write", "ar/aslp.write", "co/aslp.write", "de/aslp.write", + "fl/aslp.write", + "ga/aslp.write", + "id/aslp.write", + "in/aslp.write", + "ia/aslp.write", + "ks/aslp.write", "ky/aslp.write", "la/aslp.write", "me/aslp.write", @@ -166,25 +251,95 @@ "mn/aslp.write", "ms/aslp.write", "mo/aslp.write", + "mt/aslp.write", "ne/aslp.write", + "nh/aslp.write", + "nc/aslp.write", "oh/aslp.write", + "ok/aslp.write", + "ri/aslp.write", + "sc/aslp.write", + "tn/aslp.write", + "ut/aslp.write", + "vt/aslp.write", + "va/aslp.write", + "vi/aslp.write", + "wa/aslp.write", + "wv/aslp.write", + "wi/aslp.write", + "wy/aslp.write", "octp/write", "al/octp.write", "ar/octp.write", + "az/octp.write", + "co/octp.write", + "de/octp.write", + "ga/octp.write", + "ia/octp.write", + "in/octp.write", "ky/octp.write", "la/octp.write", + "me/octp.write", + "md/octp.write", + "mn/octp.write", "ms/octp.write", + "mo/octp.write", + "mt/octp.write", "ne/octp.write", + "nh/octp.write", + "nc/octp.write", + "nd/octp.write", "oh/octp.write", + "ri/octp.write", + "sc/octp.write", + "sd/octp.write", + "tn/octp.write", + "ut/octp.write", + "vt/octp.write", + "va/octp.write", + "wa/octp.write", + "wv/octp.write", + "wi/octp.write", + "wy/octp.write", "coun/write", "al/coun.write", "ar/coun.write", + "az/coun.write", + "co/coun.write", + "ct/coun.write", + "dc/coun.write", + "de/coun.write", "fl/coun.write", "ga/coun.write", + "ia/coun.write", + "in/coun.write", + "ks/coun.write", "ky/coun.write", + "la/coun.write", + "me/coun.write", + "md/coun.write", + "mn/coun.write", + "ms/coun.write", + "mo/coun.write", + "mt/coun.write", "ne/coun.write", + "nh/coun.write", + "nj/coun.write", + "nc/coun.write", + "nd/coun.write", "oh/coun.write", - "ut/coun.write" + "ok/coun.write", + "ri/coun.write", + "sc/coun.write", + "sd/coun.write", + "tn/coun.write", + "ut/coun.write", + "vt/coun.write", + "va/coun.write", + "wa/coun.write", + "wv/coun.write", + "wi/coun.write", + "wy/coun.write" ] } ] @@ -222,7 +377,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/SandboState6u3oL0Pj2NvF" + "$ref": "#/components/schemas/TestSStatetozLR0rjtgUW" } } }, @@ -234,7 +389,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/SandboState6zZCzv3yXEop" + "$ref": "#/components/schemas/TestSStateZndnEuXeYCaf" } } } @@ -242,7 +397,7 @@ }, "security": [ { - "SandboxStateAPIStackStateApiStateAuthAuthorizer7F83A6D3": [ + "TestBackendPipelineStackTestStateAPIStackStateApiStateAuthAuthorizerCFF4E444": [ "aslp/readGeneral", "octp/readGeneral", "coun/readGeneral" @@ -293,7 +448,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/SandboStateZKrvdt5xPggv" + "$ref": "#/components/schemas/TestSStateCnjlHSdTcZOH" } } } @@ -301,7 +456,7 @@ }, "security": [ { - "SandboxStateAPIStackStateApiStateAuthAuthorizer7F83A6D3": [ + "TestBackendPipelineStackTestStateAPIStackStateApiStateAuthAuthorizerCFF4E444": [ "aslp/readGeneral", "octp/readGeneral", "coun/readGeneral" @@ -313,32 +468,65 @@ }, "components": { "schemas": { - "SandboStateZKrvdt5xPggv": { + "TestSStateZndnEuXeYCaf": { "required": [ - "privileges", - "providerUIUrl" + "pagination", + "providers" ], "type": "object", "properties": { - "privileges": { + "pagination": { + "type": "object", + "properties": { + "prevLastKey": { + "maxLength": 1024, + "minLength": 1, + "type": "object" + }, + "lastKey": { + "maxLength": 1024, + "minLength": 1, + "type": "object" + }, + "pageSize": { + "maximum": 100, + "minimum": 5, + "type": "integer" + } + } + }, + "sorting": { + "type": "object", + "properties": { + "direction": { + "type": "string", + "description": "Direction to sort results by", + "enum": [ + "ascending", + "descending" + ] + } + }, + "description": "How to sort results" + }, + "providers": { + "maxItems": 100, "type": "array", "items": { "required": [ + "birthMonthDay", "compact", "compactEligibility", "dateOfExpiration", - "dateOfIssuance", - "dateOfRenewal", "dateOfUpdate", "familyName", "givenName", - "jurisdiction", + "jurisdictionUploadedCompactEligibility", + "jurisdictionUploadedLicenseStatus", "licenseJurisdiction", "licenseStatus", - "licenseType", - "privilegeId", + "privilegeJurisdictions", "providerId", - "status", "type" ], "type": "object", @@ -409,341 +597,228 @@ "coun" ] }, - "homeAddressStreet2": { + "npi": { + "pattern": "^[0-9]{10}$", + "type": "string" + }, + "givenName": { "maxLength": 100, "minLength": 1, "type": "string" }, - "jurisdiction": { + "compactEligibility": { "type": "string", "enum": [ - "al", - "ak", - "az", - "ar", - "ca", - "co", - "ct", - "de", - "dc", - "fl", - "ga", - "hi", - "id", - "il", - "in", - "ia", - "ks", - "ky", - "la", - "me", - "md", - "ma", - "mi", - "mn", - "ms", - "mo", - "mt", - "ne", - "nv", - "nh", - "nj", - "nm", - "ny", - "nc", - "nd", - "oh", - "ok", - "or", - "pa", - "pr", - "ri", - "sc", - "sd", - "tn", - "tx", - "ut", - "vt", - "va", - "vi", - "wa", - "wv", - "wi", - "wy" + "eligible", + "ineligible" ] }, - "homeAddressStreet1": { - "maxLength": 100, - "minLength": 2, - "type": "string" - }, - "type": { + "jurisdictionUploadedCompactEligibility": { "type": "string", "enum": [ - "statePrivilege" + "eligible", + "ineligible" ] }, - "suffix": { - "maxLength": 100, - "minLength": 1, - "type": "string" - }, - "dateOfIssuance": { + "dateOfBirth": { "pattern": "^[12]{1}[0-9]{3}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$", "type": "string", "format": "date" }, - "licenseType": { + "jurisdictionUploadedLicenseStatus": { "type": "string", "enum": [ - "audiologist", - "speech-language pathologist", - "occupational therapist", - "occupational therapy assistant", - "licensed professional counselor" + "active", + "inactive" ] }, - "emailAddress": { - "maxLength": 100, - "minLength": 5, + "privilegeJurisdictions": { + "type": "array", + "items": { + "type": "string", + "enum": [ + "al", + "ak", + "az", + "ar", + "ca", + "co", + "ct", + "de", + "dc", + "fl", + "ga", + "hi", + "id", + "il", + "in", + "ia", + "ks", + "ky", + "la", + "me", + "md", + "ma", + "mi", + "mn", + "ms", + "mo", + "mt", + "ne", + "nv", + "nh", + "nj", + "nm", + "ny", + "nc", + "nd", + "oh", + "ok", + "or", + "pa", + "pr", + "ri", + "sc", + "sd", + "tn", + "tx", + "ut", + "vt", + "va", + "vi", + "wa", + "wv", + "wi", + "wy" + ] + } + }, + "type": { "type": "string", - "format": "email" + "enum": [ + "provider" + ] + }, + "suffix": { + "maxLength": 100, + "minLength": 1, + "type": "string" + }, + "ssnLastFour": { + "pattern": "^[0-9]{4}$", + "type": "string" }, "dateOfExpiration": { "pattern": "^[12]{1}[0-9]{3}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$", "type": "string", "format": "date" }, - "homeAddressState": { - "maxLength": 100, - "minLength": 2, - "type": "string" - }, "providerId": { "pattern": "[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab]{1}[0-9a-f]{3}-[0-9a-f]{12}", "type": "string" }, - "dateOfRenewal": { - "pattern": "^[12]{1}[0-9]{3}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$", + "licenseStatus": { "type": "string", - "format": "date" + "enum": [ + "active", + "inactive" + ] }, "familyName": { "maxLength": 100, "minLength": 1, "type": "string" }, - "homeAddressCity": { - "maxLength": 100, - "minLength": 2, - "type": "string" - }, - "licenseNumber": { + "middleName": { "maxLength": 100, "minLength": 1, "type": "string" }, + "birthMonthDay": { + "pattern": "^[01]{1}[0-9]{1}-[0-3]{1}[0-9]{1}$", + "type": "string", + "format": "date" + }, "compactConnectRegisteredEmailAddress": { "maxLength": 100, "minLength": 5, "type": "string", "format": "email" }, - "npi": { - "pattern": "^[0-9]{10}$", - "type": "string" - }, - "homeAddressPostalCode": { - "maxLength": 7, - "minLength": 5, - "type": "string" - }, - "compactEligibility": { - "type": "string", - "enum": [ - "eligible", - "ineligible" - ] - }, - "givenName": { - "maxLength": 100, - "minLength": 1, - "type": "string" - }, - "dateOfBirth": { - "pattern": "^[12]{1}[0-9]{3}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$", + "dateOfUpdate": { + "pattern": "^[12]{1}[0-9]{3}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$", "type": "string", "format": "date" - }, - "ssnLastFour": { - "pattern": "^[0-9]{4}$", - "type": "string" - }, - "phoneNumber": { - "pattern": "^\\+[0-9]{8,15}$", - "type": "string" - }, - "privilegeId": { - "type": "string" - }, - "licenseStatus": { - "type": "string", - "enum": [ - "active", - "inactive" - ] - }, - "middleName": { - "maxLength": 100, - "minLength": 1, - "type": "string" - }, - "licenseStatusName": { - "maxLength": 100, - "minLength": 1, - "type": "string" - }, - "dateOfUpdate": { - "pattern": "^[12]{1}[0-9]{3}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$", - "type": "string", - "format": "date" - }, - "status": { - "type": "string", - "enum": [ - "active", - "inactive" - ] } } } - }, - "providerUIUrl": { - "type": "string", - "description": "URL to the provider UI page", - "format": "uri" } } }, - "SandboState6u3oL0Pj2NvF": { + "TestSStateqdOWiiW9X7Lu": { "required": [ - "query" + "upload" ], "type": "object", "properties": { - "pagination": { - "type": "object", - "properties": { - "lastKey": { - "maxLength": 1024, - "minLength": 1, - "type": "string" - }, - "pageSize": { - "maximum": 100, - "minimum": 5, - "type": "integer" - } - }, - "additionalProperties": false - }, - "query": { + "upload": { "required": [ - "endDateTime", - "startDateTime" + "fields", + "url" ], "type": "object", "properties": { - "startDateTime": { - "pattern": "^[12]{1}[0-9]{3}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])T([01][0-9]|2[0-3]):[0-5][0-9]:[0-5][0-9](\\.[0-9]{1,3})?Z$", - "type": "string", - "format": "date-time" + "fields": { + "type": "object", + "additionalProperties": { + "type": "string" + } }, - "endDateTime": { - "pattern": "^[12]{1}[0-9]{3}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])T([01][0-9]|2[0-3]):[0-5][0-9]:[0-5][0-9](\\.[0-9]{1,3})?Z$", - "type": "string", - "format": "date-time" - } - }, - "additionalProperties": false, - "description": "The query parameters" - }, - "sorting": { - "type": "object", - "properties": { - "direction": { - "type": "string", - "description": "Direction to sort results by", - "enum": [ - "ascending", - "descending" - ] + "url": { + "type": "string" } - }, - "description": "How to sort results" + } } - }, - "additionalProperties": false + } }, - "SandboState6zZCzv3yXEop": { + "TestSStatecAgwR1yswwcK": { "required": [ - "pagination", - "providers" + "message" ], "type": "object", "properties": { - "pagination": { - "type": "object", - "properties": { - "prevLastKey": { - "maxLength": 1024, - "minLength": 1, - "type": "object" - }, - "lastKey": { - "maxLength": 1024, - "minLength": 1, - "type": "object" - }, - "pageSize": { - "maximum": 100, - "minimum": 5, - "type": "integer" - } - } - }, - "sorting": { - "type": "object", - "properties": { - "direction": { - "type": "string", - "description": "Direction to sort results by", - "enum": [ - "ascending", - "descending" - ] - } - }, - "description": "How to sort results" - }, - "providers": { - "maxItems": 100, + "message": { + "type": "string", + "description": "A message about the request" + } + } + }, + "TestSStateCnjlHSdTcZOH": { + "required": [ + "privileges", + "providerUIUrl" + ], + "type": "object", + "properties": { + "privileges": { "type": "array", "items": { "required": [ - "birthMonthDay", "compact", "compactEligibility", "dateOfExpiration", + "dateOfIssuance", + "dateOfRenewal", "dateOfUpdate", "familyName", "givenName", - "jurisdictionUploadedCompactEligibility", - "jurisdictionUploadedLicenseStatus", + "jurisdiction", "licenseJurisdiction", "licenseStatus", - "privilegeJurisdictions", + "licenseType", + "privilegeId", "providerId", + "status", "type" ], "type": "object", @@ -808,19 +883,159 @@ }, "compact": { "type": "string", - "enum": [ - "aslp", - "octp", - "coun" - ] + "enum": [ + "aslp", + "octp", + "coun" + ] + }, + "homeAddressStreet2": { + "maxLength": 100, + "minLength": 1, + "type": "string" + }, + "jurisdiction": { + "type": "string", + "enum": [ + "al", + "ak", + "az", + "ar", + "ca", + "co", + "ct", + "de", + "dc", + "fl", + "ga", + "hi", + "id", + "il", + "in", + "ia", + "ks", + "ky", + "la", + "me", + "md", + "ma", + "mi", + "mn", + "ms", + "mo", + "mt", + "ne", + "nv", + "nh", + "nj", + "nm", + "ny", + "nc", + "nd", + "oh", + "ok", + "or", + "pa", + "pr", + "ri", + "sc", + "sd", + "tn", + "tx", + "ut", + "vt", + "va", + "vi", + "wa", + "wv", + "wi", + "wy" + ] + }, + "homeAddressStreet1": { + "maxLength": 100, + "minLength": 2, + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "statePrivilege" + ] + }, + "suffix": { + "maxLength": 100, + "minLength": 1, + "type": "string" + }, + "dateOfIssuance": { + "pattern": "^[12]{1}[0-9]{3}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$", + "type": "string", + "format": "date" + }, + "licenseType": { + "type": "string", + "enum": [ + "audiologist", + "speech-language pathologist", + "occupational therapist", + "occupational therapy assistant", + "licensed professional counselor" + ] + }, + "emailAddress": { + "maxLength": 100, + "minLength": 5, + "type": "string", + "format": "email" + }, + "dateOfExpiration": { + "pattern": "^[12]{1}[0-9]{3}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$", + "type": "string", + "format": "date" + }, + "homeAddressState": { + "maxLength": 100, + "minLength": 2, + "type": "string" + }, + "providerId": { + "pattern": "[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab]{1}[0-9a-f]{3}-[0-9a-f]{12}", + "type": "string" + }, + "dateOfRenewal": { + "pattern": "^[12]{1}[0-9]{3}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$", + "type": "string", + "format": "date" + }, + "familyName": { + "maxLength": 100, + "minLength": 1, + "type": "string" + }, + "homeAddressCity": { + "maxLength": 100, + "minLength": 2, + "type": "string" + }, + "licenseNumber": { + "maxLength": 100, + "minLength": 1, + "type": "string" + }, + "compactConnectRegisteredEmailAddress": { + "maxLength": 100, + "minLength": 5, + "type": "string", + "format": "email" }, "npi": { "pattern": "^[0-9]{10}$", "type": "string" }, - "givenName": { - "maxLength": 100, - "minLength": 1, + "homeAddressPostalCode": { + "maxLength": 7, + "minLength": 5, "type": "string" }, "compactEligibility": { @@ -830,108 +1045,25 @@ "ineligible" ] }, - "jurisdictionUploadedCompactEligibility": { - "type": "string", - "enum": [ - "eligible", - "ineligible" - ] + "givenName": { + "maxLength": 100, + "minLength": 1, + "type": "string" }, "dateOfBirth": { "pattern": "^[12]{1}[0-9]{3}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$", "type": "string", "format": "date" }, - "jurisdictionUploadedLicenseStatus": { - "type": "string", - "enum": [ - "active", - "inactive" - ] - }, - "privilegeJurisdictions": { - "type": "array", - "items": { - "type": "string", - "enum": [ - "al", - "ak", - "az", - "ar", - "ca", - "co", - "ct", - "de", - "dc", - "fl", - "ga", - "hi", - "id", - "il", - "in", - "ia", - "ks", - "ky", - "la", - "me", - "md", - "ma", - "mi", - "mn", - "ms", - "mo", - "mt", - "ne", - "nv", - "nh", - "nj", - "nm", - "ny", - "nc", - "nd", - "oh", - "ok", - "or", - "pa", - "pr", - "ri", - "sc", - "sd", - "tn", - "tx", - "ut", - "vt", - "va", - "vi", - "wa", - "wv", - "wi", - "wy" - ] - } - }, - "type": { - "type": "string", - "enum": [ - "provider" - ] - }, - "suffix": { - "maxLength": 100, - "minLength": 1, - "type": "string" - }, "ssnLastFour": { "pattern": "^[0-9]{4}$", "type": "string" }, - "dateOfExpiration": { - "pattern": "^[12]{1}[0-9]{3}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$", - "type": "string", - "format": "date" + "phoneNumber": { + "pattern": "^\\+[0-9]{8,15}$", + "type": "string" }, - "providerId": { - "pattern": "[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab]{1}[0-9a-f]{3}-[0-9a-f]{12}", + "privilegeId": { "type": "string" }, "licenseStatus": { @@ -941,74 +1073,39 @@ "inactive" ] }, - "familyName": { + "middleName": { "maxLength": 100, "minLength": 1, "type": "string" }, - "middleName": { + "licenseStatusName": { "maxLength": 100, "minLength": 1, "type": "string" }, - "birthMonthDay": { - "pattern": "^[01]{1}[0-9]{1}-[0-3]{1}[0-9]{1}$", - "type": "string", - "format": "date" - }, - "compactConnectRegisteredEmailAddress": { - "maxLength": 100, - "minLength": 5, - "type": "string", - "format": "email" - }, "dateOfUpdate": { "pattern": "^[12]{1}[0-9]{3}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$", "type": "string", "format": "date" + }, + "status": { + "type": "string", + "enum": [ + "active", + "inactive" + ] } } } - } - } - }, - "SandboStatecjpoBVtvh5nr": { - "type": "object", - "properties": { - "message": { - "type": "string", - "description": "Message indicating success or failure" }, - "errors": { - "type": "object", - "additionalProperties": { - "type": "object", - "additionalProperties": { - "type": "array", - "description": "List of error messages for a field", - "items": { - "type": "string" - } - }, - "description": "Errors for a specific record" - }, - "description": "Validation errors by record index" - } - } - }, - "SandboStateT5uVnUWmQEFs": { - "required": [ - "message" - ], - "type": "object", - "properties": { - "message": { + "providerUIUrl": { "type": "string", - "description": "A message about the request" + "description": "URL to the provider UI page", + "format": "uri" } } }, - "SandboStatewYA2yiuq5Kyj": { + "TestSStatejhIAEk3qM7mR": { "maxItems": 100, "type": "array", "items": { @@ -1146,45 +1243,109 @@ "maxLength": 100, "minLength": 1, "type": "string" + }, + "previousSSN": { + "pattern": "^[0-9]{3}-[0-9]{2}-[0-9]{4}$", + "type": "string", + "description": "The incorrect social security number previously uploaded for this license. When provided, the system migrates the records uploaded under it over to the provider associated with the corrected ssn." } }, "additionalProperties": false } }, - "SandboState4XO9paC5FgIb": { + "TestSStatej0rhnQc3na8L": { + "type": "object", + "properties": { + "message": { + "type": "string", + "description": "Message indicating success or failure" + }, + "errors": { + "type": "object", + "additionalProperties": { + "type": "object", + "additionalProperties": { + "type": "array", + "description": "List of error messages for a field", + "items": { + "type": "string" + } + }, + "description": "Errors for a specific record" + }, + "description": "Validation errors by record index" + } + } + }, + "TestSStatetozLR0rjtgUW": { "required": [ - "upload" + "query" ], "type": "object", "properties": { - "upload": { + "pagination": { + "type": "object", + "properties": { + "lastKey": { + "maxLength": 1024, + "minLength": 1, + "type": "string" + }, + "pageSize": { + "maximum": 100, + "minimum": 5, + "type": "integer" + } + }, + "additionalProperties": false + }, + "query": { "required": [ - "fields", - "url" + "endDateTime", + "startDateTime" ], "type": "object", "properties": { - "fields": { - "type": "object", - "additionalProperties": { - "type": "string" - } + "startDateTime": { + "pattern": "^[12]{1}[0-9]{3}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])T([01][0-9]|2[0-3]):[0-5][0-9]:[0-5][0-9](\\.[0-9]{1,3})?Z$", + "type": "string", + "format": "date-time" }, - "url": { - "type": "string" + "endDateTime": { + "pattern": "^[12]{1}[0-9]{3}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])T([01][0-9]|2[0-3]):[0-5][0-9]:[0-5][0-9](\\.[0-9]{1,3})?Z$", + "type": "string", + "format": "date-time" } - } + }, + "additionalProperties": false, + "description": "The query parameters" + }, + "sorting": { + "type": "object", + "properties": { + "direction": { + "type": "string", + "description": "Direction to sort results by", + "enum": [ + "ascending", + "descending" + ] + } + }, + "description": "How to sort results" } - } + }, + "additionalProperties": false } }, "securitySchemes": { - "SandboxStateAPIStackStateApiStateAuthAuthorizer7F83A6D3": { + "TestBackendPipelineStackTestStateAPIStackStateApiStateAuthAuthorizerCFF4E444": { "type": "apiKey", "name": "Authorization", "in": "header", "x-amazon-apigateway-authtype": "cognito_user_pools" } } - } + }, + "x-amazon-apigateway-security-policy": "TLS_1_0" } diff --git a/backend/compact-connect/docs/internal/api-specification/latest-oas30.json b/backend/compact-connect/docs/internal/api-specification/latest-oas30.json index c51f87bd74..2a8639b38b 100644 --- a/backend/compact-connect/docs/internal/api-specification/latest-oas30.json +++ b/backend/compact-connect/docs/internal/api-specification/latest-oas30.json @@ -2,16 +2,19 @@ "openapi": "3.0.1", "info": { "title": "LicenseApi", - "version": "2026-05-26T15:59:12Z" + "version": "2026-04-06T22:59:42Z" }, "servers": [ { - "url": "https://api.beta.compactconnect.org" + "url": "https://api.beta.compactconnect.org", + "x-amazon-apigateway-endpoint-configuration": { + "disableExecuteApiEndpoint": true + } } ], "paths": { - "/v1/compacts/{compact}/providers/{providerId}/licenses/jurisdiction/{jurisdiction}/licenseType/{licenseType}/investigation": { - "post": { + "/v1/compacts/{compact}": { + "get": { "parameters": [ { "name": "Authorization", @@ -28,25 +31,42 @@ "schema": { "type": "string" } - }, - { - "name": "providerId", - "in": "path", - "required": true, - "schema": { - "type": "string" + } + ], + "responses": { + "200": { + "description": "200 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TestALicenP6vvlXwvuH66" + } + } } - }, + } + }, + "security": [ { - "name": "jurisdiction", - "in": "path", + "TestBackendPipelineStackTestAPIStackLicenseApiStaffUsersPoolAuthorizer084A18F1": [ + "aslp/readGeneral", + "octp/readGeneral", + "coun/readGeneral" + ] + } + ] + }, + "put": { + "parameters": [ + { + "name": "Authorization", + "in": "header", "required": true, "schema": { "type": "string" } }, { - "name": "licenseType", + "name": "compact", "in": "path", "required": true, "schema": { @@ -58,7 +78,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/SandboLicen9VEPwkhZhKem" + "$ref": "#/components/schemas/TestALicenW6ok160MLwS9" } } }, @@ -70,7 +90,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/SandboLicenTMQQKAeKTKQR" + "$ref": "#/components/schemas/TestALicenEMkErOzoY9dd" } } } @@ -78,13 +98,19 @@ }, "security": [ { - "SandboxAPIStackLicenseApiStaffUsersPoolAuthorizer14A84A9B": [ + "TestBackendPipelineStackTestAPIStackLicenseApiStaffUsersPoolAuthorizer084A18F1": [ "aslp/admin", "al/aslp.admin", "ak/aslp.admin", "ar/aslp.admin", "co/aslp.admin", "de/aslp.admin", + "fl/aslp.admin", + "ga/aslp.admin", + "id/aslp.admin", + "in/aslp.admin", + "ia/aslp.admin", + "ks/aslp.admin", "ky/aslp.admin", "la/aslp.admin", "me/aslp.admin", @@ -92,49 +118,113 @@ "mn/aslp.admin", "ms/aslp.admin", "mo/aslp.admin", + "mt/aslp.admin", "ne/aslp.admin", + "nh/aslp.admin", + "nc/aslp.admin", "oh/aslp.admin", + "ok/aslp.admin", + "ri/aslp.admin", + "sc/aslp.admin", + "tn/aslp.admin", + "ut/aslp.admin", + "vt/aslp.admin", + "va/aslp.admin", + "vi/aslp.admin", + "wa/aslp.admin", + "wv/aslp.admin", + "wi/aslp.admin", + "wy/aslp.admin", "octp/admin", "al/octp.admin", "ar/octp.admin", + "az/octp.admin", + "co/octp.admin", + "de/octp.admin", + "ga/octp.admin", + "ia/octp.admin", + "in/octp.admin", "ky/octp.admin", "la/octp.admin", + "me/octp.admin", + "md/octp.admin", + "mn/octp.admin", "ms/octp.admin", + "mo/octp.admin", + "mt/octp.admin", "ne/octp.admin", + "nh/octp.admin", + "nc/octp.admin", + "nd/octp.admin", "oh/octp.admin", + "ri/octp.admin", + "sc/octp.admin", + "sd/octp.admin", + "tn/octp.admin", + "ut/octp.admin", + "vt/octp.admin", + "va/octp.admin", + "wa/octp.admin", + "wv/octp.admin", + "wi/octp.admin", + "wy/octp.admin", "coun/admin", "al/coun.admin", "ar/coun.admin", + "az/coun.admin", + "co/coun.admin", + "ct/coun.admin", + "dc/coun.admin", + "de/coun.admin", "fl/coun.admin", "ga/coun.admin", + "ia/coun.admin", + "in/coun.admin", + "ks/coun.admin", "ky/coun.admin", + "la/coun.admin", + "me/coun.admin", + "md/coun.admin", + "mn/coun.admin", + "ms/coun.admin", + "mo/coun.admin", + "mt/coun.admin", "ne/coun.admin", + "nh/coun.admin", + "nj/coun.admin", + "nc/coun.admin", + "nd/coun.admin", "oh/coun.admin", - "ut/coun.admin" + "ok/coun.admin", + "ri/coun.admin", + "sc/coun.admin", + "sd/coun.admin", + "tn/coun.admin", + "ut/coun.admin", + "vt/coun.admin", + "va/coun.admin", + "wa/coun.admin", + "wv/coun.admin", + "wi/coun.admin", + "wy/coun.admin" ] } ] - }, - "options": { + } + }, + "/v1/compacts/{compact}/attestations/{attestationId}": { + "get": { "parameters": [ { - "name": "compact", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - }, - { - "name": "providerId", - "in": "path", + "name": "Authorization", + "in": "header", "required": true, "schema": { "type": "string" } }, { - "name": "jurisdiction", + "name": "compact", "in": "path", "required": true, "schema": { @@ -142,7 +232,7 @@ } }, { - "name": "licenseType", + "name": "attestationId", "in": "path", "required": true, "schema": { @@ -151,42 +241,49 @@ } ], "responses": { - "204": { - "description": "204 response", - "headers": { - "Access-Control-Allow-Origin": { - "schema": { - "type": "string" - } - }, - "Access-Control-Allow-Methods": { - "schema": { - "type": "string" - } - }, - "Vary": { - "schema": { - "type": "string" - } - }, - "Access-Control-Allow-Headers": { + "200": { + "description": "200 response", + "content": { + "application/json": { "schema": { - "type": "string" + "$ref": "#/components/schemas/TestALicenbzfo0nLKzHWq" } } - }, - "content": {} + } } - } + }, + "security": [ + { + "TestBackendPipelineStackTestAPIStackLicenseApiProviderUsersPoolAuthorizer246E3F74": [] + } + ] } }, - "/v1/provider-users/initiateRecovery": { + "/v1/compacts/{compact}/credentials/payment-processor": { "post": { + "parameters": [ + { + "name": "Authorization", + "in": "header", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "compact", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/SandboLicenJt67zBFIGGPS" + "$ref": "#/components/schemas/TestALicenQaU7r1ltReBC" } } }, @@ -198,57 +295,141 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/SandboLicenTMQQKAeKTKQR" + "$ref": "#/components/schemas/TestALicenYbbH6xLqpmhB" } } } } - } - }, - "options": { - "responses": { - "204": { - "description": "204 response", - "headers": { - "Access-Control-Allow-Origin": { - "schema": { - "type": "string" - } - }, - "Access-Control-Allow-Methods": { - "schema": { - "type": "string" - } - }, - "Vary": { - "schema": { - "type": "string" - } - }, - "Access-Control-Allow-Headers": { - "schema": { - "type": "string" - } - } - }, - "content": {} + }, + "security": [ + { + "TestBackendPipelineStackTestAPIStackLicenseApiStaffUsersPoolAuthorizer084A18F1": [ + "aslp/admin", + "al/aslp.admin", + "ak/aslp.admin", + "ar/aslp.admin", + "co/aslp.admin", + "de/aslp.admin", + "fl/aslp.admin", + "ga/aslp.admin", + "id/aslp.admin", + "in/aslp.admin", + "ia/aslp.admin", + "ks/aslp.admin", + "ky/aslp.admin", + "la/aslp.admin", + "me/aslp.admin", + "md/aslp.admin", + "mn/aslp.admin", + "ms/aslp.admin", + "mo/aslp.admin", + "mt/aslp.admin", + "ne/aslp.admin", + "nh/aslp.admin", + "nc/aslp.admin", + "oh/aslp.admin", + "ok/aslp.admin", + "ri/aslp.admin", + "sc/aslp.admin", + "tn/aslp.admin", + "ut/aslp.admin", + "vt/aslp.admin", + "va/aslp.admin", + "vi/aslp.admin", + "wa/aslp.admin", + "wv/aslp.admin", + "wi/aslp.admin", + "wy/aslp.admin", + "octp/admin", + "al/octp.admin", + "ar/octp.admin", + "az/octp.admin", + "co/octp.admin", + "de/octp.admin", + "ga/octp.admin", + "ia/octp.admin", + "in/octp.admin", + "ky/octp.admin", + "la/octp.admin", + "me/octp.admin", + "md/octp.admin", + "mn/octp.admin", + "ms/octp.admin", + "mo/octp.admin", + "mt/octp.admin", + "ne/octp.admin", + "nh/octp.admin", + "nc/octp.admin", + "nd/octp.admin", + "oh/octp.admin", + "ri/octp.admin", + "sc/octp.admin", + "sd/octp.admin", + "tn/octp.admin", + "ut/octp.admin", + "vt/octp.admin", + "va/octp.admin", + "wa/octp.admin", + "wv/octp.admin", + "wi/octp.admin", + "wy/octp.admin", + "coun/admin", + "al/coun.admin", + "ar/coun.admin", + "az/coun.admin", + "co/coun.admin", + "ct/coun.admin", + "dc/coun.admin", + "de/coun.admin", + "fl/coun.admin", + "ga/coun.admin", + "ia/coun.admin", + "in/coun.admin", + "ks/coun.admin", + "ky/coun.admin", + "la/coun.admin", + "me/coun.admin", + "md/coun.admin", + "mn/coun.admin", + "ms/coun.admin", + "mo/coun.admin", + "mt/coun.admin", + "ne/coun.admin", + "nh/coun.admin", + "nj/coun.admin", + "nc/coun.admin", + "nd/coun.admin", + "oh/coun.admin", + "ok/coun.admin", + "ri/coun.admin", + "sc/coun.admin", + "sd/coun.admin", + "tn/coun.admin", + "ut/coun.admin", + "vt/coun.admin", + "va/coun.admin", + "wa/coun.admin", + "wv/coun.admin", + "wi/coun.admin", + "wy/coun.admin" + ] } - } + ] } }, - "/v1/compacts/{compact}/providers/{providerId}/licenses": { - "options": { + "/v1/compacts/{compact}/jurisdictions": { + "get": { "parameters": [ { - "name": "compact", - "in": "path", + "name": "Authorization", + "in": "header", "required": true, "schema": { "type": "string" } }, { - "name": "providerId", + "name": "compact", "in": "path", "required": true, "schema": { @@ -257,36 +438,29 @@ } ], "responses": { - "204": { - "description": "204 response", - "headers": { - "Access-Control-Allow-Origin": { - "schema": { - "type": "string" - } - }, - "Access-Control-Allow-Methods": { - "schema": { - "type": "string" - } - }, - "Vary": { - "schema": { - "type": "string" - } - }, - "Access-Control-Allow-Headers": { + "200": { + "description": "200 response", + "content": { + "application/json": { "schema": { - "type": "string" + "$ref": "#/components/schemas/TestALicenuoi86SrA9xKo" } } - }, - "content": {} + } } - } + }, + "security": [ + { + "TestBackendPipelineStackTestAPIStackLicenseApiStaffUsersPoolAuthorizer084A18F1": [ + "aslp/readGeneral", + "octp/readGeneral", + "coun/readGeneral" + ] + } + ] } }, - "/v1/compacts/{compact}/jurisdictions/{jurisdiction}/licenses/bulk-upload": { + "/v1/compacts/{compact}/jurisdictions/{jurisdiction}": { "get": { "parameters": [ { @@ -320,7 +494,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/SandboLicenjfa9vGqBChQd" + "$ref": "#/components/schemas/TestALicenXybIHf2p94sK" } } } @@ -328,45 +502,24 @@ }, "security": [ { - "SandboxAPIStackLicenseApiStaffUsersPoolAuthorizer14A84A9B": [ - "aslp/write", - "al/aslp.write", - "ak/aslp.write", - "ar/aslp.write", - "co/aslp.write", - "de/aslp.write", - "ky/aslp.write", - "la/aslp.write", - "me/aslp.write", - "md/aslp.write", - "mn/aslp.write", - "ms/aslp.write", - "mo/aslp.write", - "ne/aslp.write", - "oh/aslp.write", - "octp/write", - "al/octp.write", - "ar/octp.write", - "ky/octp.write", - "la/octp.write", - "ms/octp.write", - "ne/octp.write", - "oh/octp.write", - "coun/write", - "al/coun.write", - "ar/coun.write", - "fl/coun.write", - "ga/coun.write", - "ky/coun.write", - "ne/coun.write", - "oh/coun.write", - "ut/coun.write" + "TestBackendPipelineStackTestAPIStackLicenseApiStaffUsersPoolAuthorizer084A18F1": [ + "aslp/readGeneral", + "octp/readGeneral", + "coun/readGeneral" ] } ] }, - "options": { + "put": { "parameters": [ + { + "name": "Authorization", + "in": "header", + "required": true, + "schema": { + "type": "string" + } + }, { "name": "compact", "in": "path", @@ -384,71 +537,155 @@ } } ], - "responses": { - "204": { - "description": "204 response", - "headers": { - "Access-Control-Allow-Origin": { - "schema": { - "type": "string" - } - }, - "Access-Control-Allow-Methods": { - "schema": { - "type": "string" - } - }, - "Vary": { - "schema": { - "type": "string" - } - }, - "Access-Control-Allow-Headers": { - "schema": { - "type": "string" - } + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TestALicenrrveP2TCwpbO" } - }, - "content": {} - } - } - } - }, - "/v1/public/jurisdictions": { - "options": { + } + }, + "required": true + }, "responses": { - "204": { - "description": "204 response", - "headers": { - "Access-Control-Allow-Origin": { - "schema": { - "type": "string" - } - }, - "Access-Control-Allow-Methods": { - "schema": { - "type": "string" - } - }, - "Vary": { - "schema": { - "type": "string" - } - }, - "Access-Control-Allow-Headers": { + "200": { + "description": "200 response", + "content": { + "application/json": { "schema": { - "type": "string" + "$ref": "#/components/schemas/TestALicenEMkErOzoY9dd" } } - }, - "content": {} + } } - } + }, + "security": [ + { + "TestBackendPipelineStackTestAPIStackLicenseApiStaffUsersPoolAuthorizer084A18F1": [ + "aslp/admin", + "al/aslp.admin", + "ak/aslp.admin", + "ar/aslp.admin", + "co/aslp.admin", + "de/aslp.admin", + "fl/aslp.admin", + "ga/aslp.admin", + "id/aslp.admin", + "in/aslp.admin", + "ia/aslp.admin", + "ks/aslp.admin", + "ky/aslp.admin", + "la/aslp.admin", + "me/aslp.admin", + "md/aslp.admin", + "mn/aslp.admin", + "ms/aslp.admin", + "mo/aslp.admin", + "mt/aslp.admin", + "ne/aslp.admin", + "nh/aslp.admin", + "nc/aslp.admin", + "oh/aslp.admin", + "ok/aslp.admin", + "ri/aslp.admin", + "sc/aslp.admin", + "tn/aslp.admin", + "ut/aslp.admin", + "vt/aslp.admin", + "va/aslp.admin", + "vi/aslp.admin", + "wa/aslp.admin", + "wv/aslp.admin", + "wi/aslp.admin", + "wy/aslp.admin", + "octp/admin", + "al/octp.admin", + "ar/octp.admin", + "az/octp.admin", + "co/octp.admin", + "de/octp.admin", + "ga/octp.admin", + "ia/octp.admin", + "in/octp.admin", + "ky/octp.admin", + "la/octp.admin", + "me/octp.admin", + "md/octp.admin", + "mn/octp.admin", + "ms/octp.admin", + "mo/octp.admin", + "mt/octp.admin", + "ne/octp.admin", + "nh/octp.admin", + "nc/octp.admin", + "nd/octp.admin", + "oh/octp.admin", + "ri/octp.admin", + "sc/octp.admin", + "sd/octp.admin", + "tn/octp.admin", + "ut/octp.admin", + "vt/octp.admin", + "va/octp.admin", + "wa/octp.admin", + "wv/octp.admin", + "wi/octp.admin", + "wy/octp.admin", + "coun/admin", + "al/coun.admin", + "ar/coun.admin", + "az/coun.admin", + "co/coun.admin", + "ct/coun.admin", + "dc/coun.admin", + "de/coun.admin", + "fl/coun.admin", + "ga/coun.admin", + "ia/coun.admin", + "in/coun.admin", + "ks/coun.admin", + "ky/coun.admin", + "la/coun.admin", + "me/coun.admin", + "md/coun.admin", + "mn/coun.admin", + "ms/coun.admin", + "mo/coun.admin", + "mt/coun.admin", + "ne/coun.admin", + "nh/coun.admin", + "nj/coun.admin", + "nc/coun.admin", + "nd/coun.admin", + "oh/coun.admin", + "ok/coun.admin", + "ri/coun.admin", + "sc/coun.admin", + "sd/coun.admin", + "tn/coun.admin", + "ut/coun.admin", + "vt/coun.admin", + "va/coun.admin", + "wa/coun.admin", + "wv/coun.admin", + "wi/coun.admin", + "wy/coun.admin" + ] + } + ] } }, - "/v1/public/compacts/{compact}/jurisdictions": { + "/v1/compacts/{compact}/jurisdictions/{jurisdiction}/licenses/bulk-upload": { "get": { "parameters": [ + { + "name": "Authorization", + "in": "header", + "required": true, + "schema": { + "type": "string" + } + }, { "name": "compact", "in": "path", @@ -456,6 +693,14 @@ "schema": { "type": "string" } + }, + { + "name": "jurisdiction", + "in": "path", + "required": true, + "schema": { + "type": "string" + } } ], "responses": { @@ -464,55 +709,129 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/SandboLicenLnkOp52kvwLg" + "$ref": "#/components/schemas/TestALicenfZAZJXjiwCtn" } } } } - } - }, - "options": { - "parameters": [ + }, + "security": [ { - "name": "compact", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "204": { - "description": "204 response", - "headers": { - "Access-Control-Allow-Origin": { - "schema": { - "type": "string" - } - }, - "Access-Control-Allow-Methods": { - "schema": { - "type": "string" - } - }, - "Vary": { - "schema": { - "type": "string" - } - }, - "Access-Control-Allow-Headers": { - "schema": { - "type": "string" - } - } - }, - "content": {} + "TestBackendPipelineStackTestAPIStackLicenseApiStaffUsersPoolAuthorizer084A18F1": [ + "aslp/write", + "al/aslp.write", + "ak/aslp.write", + "ar/aslp.write", + "co/aslp.write", + "de/aslp.write", + "fl/aslp.write", + "ga/aslp.write", + "id/aslp.write", + "in/aslp.write", + "ia/aslp.write", + "ks/aslp.write", + "ky/aslp.write", + "la/aslp.write", + "me/aslp.write", + "md/aslp.write", + "mn/aslp.write", + "ms/aslp.write", + "mo/aslp.write", + "mt/aslp.write", + "ne/aslp.write", + "nh/aslp.write", + "nc/aslp.write", + "oh/aslp.write", + "ok/aslp.write", + "ri/aslp.write", + "sc/aslp.write", + "tn/aslp.write", + "ut/aslp.write", + "vt/aslp.write", + "va/aslp.write", + "vi/aslp.write", + "wa/aslp.write", + "wv/aslp.write", + "wi/aslp.write", + "wy/aslp.write", + "octp/write", + "al/octp.write", + "ar/octp.write", + "az/octp.write", + "co/octp.write", + "de/octp.write", + "ga/octp.write", + "ia/octp.write", + "in/octp.write", + "ky/octp.write", + "la/octp.write", + "me/octp.write", + "md/octp.write", + "mn/octp.write", + "ms/octp.write", + "mo/octp.write", + "mt/octp.write", + "ne/octp.write", + "nh/octp.write", + "nc/octp.write", + "nd/octp.write", + "oh/octp.write", + "ri/octp.write", + "sc/octp.write", + "sd/octp.write", + "tn/octp.write", + "ut/octp.write", + "vt/octp.write", + "va/octp.write", + "wa/octp.write", + "wv/octp.write", + "wi/octp.write", + "wy/octp.write", + "coun/write", + "al/coun.write", + "ar/coun.write", + "az/coun.write", + "co/coun.write", + "ct/coun.write", + "dc/coun.write", + "de/coun.write", + "fl/coun.write", + "ga/coun.write", + "ia/coun.write", + "in/coun.write", + "ks/coun.write", + "ky/coun.write", + "la/coun.write", + "me/coun.write", + "md/coun.write", + "mn/coun.write", + "ms/coun.write", + "mo/coun.write", + "mt/coun.write", + "ne/coun.write", + "nh/coun.write", + "nj/coun.write", + "nc/coun.write", + "nd/coun.write", + "oh/coun.write", + "ok/coun.write", + "ri/coun.write", + "sc/coun.write", + "sd/coun.write", + "tn/coun.write", + "ut/coun.write", + "vt/coun.write", + "va/coun.write", + "wa/coun.write", + "wv/coun.write", + "wi/coun.write", + "wy/coun.write" + ] } - } + ] } }, - "/v1/purchases/privileges": { + "/v1/compacts/{compact}/providers/query": { "post": { "parameters": [ { @@ -522,13 +841,21 @@ "schema": { "type": "string" } + }, + { + "name": "compact", + "in": "path", + "required": true, + "schema": { + "type": "string" + } } ], "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/SandboLicendZ26vvlCKCbW" + "$ref": "#/components/schemas/TestALicendW7jPEWN2hvi" } } }, @@ -540,7 +867,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/SandboLicenVHq6DcHpnqp0" + "$ref": "#/components/schemas/TestALicenRPKcrvQc1Mcd" } } } @@ -548,42 +875,16 @@ }, "security": [ { - "SandboxAPIStackLicenseApiProviderUsersPoolAuthorizerEB7523BA": [] + "TestBackendPipelineStackTestAPIStackLicenseApiStaffUsersPoolAuthorizer084A18F1": [ + "aslp/readGeneral", + "octp/readGeneral", + "coun/readGeneral" + ] } ] - }, - "options": { - "responses": { - "204": { - "description": "204 response", - "headers": { - "Access-Control-Allow-Origin": { - "schema": { - "type": "string" - } - }, - "Access-Control-Allow-Methods": { - "schema": { - "type": "string" - } - }, - "Vary": { - "schema": { - "type": "string" - } - }, - "Access-Control-Allow-Headers": { - "schema": { - "type": "string" - } - } - }, - "content": {} - } - } } }, - "/v1/provider-users/me": { + "/v1/compacts/{compact}/providers/{providerId}": { "get": { "parameters": [ { @@ -593,6 +894,22 @@ "schema": { "type": "string" } + }, + { + "name": "compact", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "providerId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } } ], "responses": { @@ -601,7 +918,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/SandboLicenJlHz6gimzgVV" + "$ref": "#/components/schemas/TestALicenbtMyL0MqSVUK" } } } @@ -609,43 +926,17 @@ }, "security": [ { - "SandboxAPIStackLicenseApiProviderUsersPoolAuthorizerEB7523BA": [] + "TestBackendPipelineStackTestAPIStackLicenseApiStaffUsersPoolAuthorizer084A18F1": [ + "aslp/readGeneral", + "octp/readGeneral", + "coun/readGeneral" + ] } ] - }, - "options": { - "responses": { - "204": { - "description": "204 response", - "headers": { - "Access-Control-Allow-Origin": { - "schema": { - "type": "string" - } - }, - "Access-Control-Allow-Methods": { - "schema": { - "type": "string" - } - }, - "Vary": { - "schema": { - "type": "string" - } - }, - "Access-Control-Allow-Headers": { - "schema": { - "type": "string" - } - } - }, - "content": {} - } - } } }, - "/v1/compacts/{compact}/providers/{providerId}/ssn": { - "get": { + "/v1/compacts/{compact}/providers/{providerId}/licenses/jurisdiction/{jurisdiction}/licenseType/{licenseType}/encumbrance": { + "post": { "parameters": [ { "name": "Authorization", @@ -670,15 +961,41 @@ "schema": { "type": "string" } + }, + { + "name": "jurisdiction", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "licenseType", + "in": "path", + "required": true, + "schema": { + "type": "string" + } } ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TestALicendmGvxmbGgc3i" + } + } + }, + "required": true + }, "responses": { "200": { "description": "200 response", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/SandboLicenx7ouhX772atw" + "$ref": "#/components/schemas/TestALicenEMkErOzoY9dd" } } } @@ -686,45 +1003,131 @@ }, "security": [ { - "SandboxAPIStackLicenseApiStaffUsersPoolAuthorizer14A84A9B": [ - "aslp/readSSN", - "al/aslp.readSSN", - "ak/aslp.readSSN", - "ar/aslp.readSSN", - "co/aslp.readSSN", - "de/aslp.readSSN", - "ky/aslp.readSSN", - "la/aslp.readSSN", - "me/aslp.readSSN", - "md/aslp.readSSN", - "mn/aslp.readSSN", - "ms/aslp.readSSN", - "mo/aslp.readSSN", - "ne/aslp.readSSN", - "oh/aslp.readSSN", - "octp/readSSN", - "al/octp.readSSN", - "ar/octp.readSSN", - "ky/octp.readSSN", - "la/octp.readSSN", - "ms/octp.readSSN", - "ne/octp.readSSN", - "oh/octp.readSSN", - "coun/readSSN", - "al/coun.readSSN", - "ar/coun.readSSN", - "fl/coun.readSSN", - "ga/coun.readSSN", - "ky/coun.readSSN", - "ne/coun.readSSN", - "oh/coun.readSSN", - "ut/coun.readSSN" + "TestBackendPipelineStackTestAPIStackLicenseApiStaffUsersPoolAuthorizer084A18F1": [ + "aslp/admin", + "al/aslp.admin", + "ak/aslp.admin", + "ar/aslp.admin", + "co/aslp.admin", + "de/aslp.admin", + "fl/aslp.admin", + "ga/aslp.admin", + "id/aslp.admin", + "in/aslp.admin", + "ia/aslp.admin", + "ks/aslp.admin", + "ky/aslp.admin", + "la/aslp.admin", + "me/aslp.admin", + "md/aslp.admin", + "mn/aslp.admin", + "ms/aslp.admin", + "mo/aslp.admin", + "mt/aslp.admin", + "ne/aslp.admin", + "nh/aslp.admin", + "nc/aslp.admin", + "oh/aslp.admin", + "ok/aslp.admin", + "ri/aslp.admin", + "sc/aslp.admin", + "tn/aslp.admin", + "ut/aslp.admin", + "vt/aslp.admin", + "va/aslp.admin", + "vi/aslp.admin", + "wa/aslp.admin", + "wv/aslp.admin", + "wi/aslp.admin", + "wy/aslp.admin", + "octp/admin", + "al/octp.admin", + "ar/octp.admin", + "az/octp.admin", + "co/octp.admin", + "de/octp.admin", + "ga/octp.admin", + "ia/octp.admin", + "in/octp.admin", + "ky/octp.admin", + "la/octp.admin", + "me/octp.admin", + "md/octp.admin", + "mn/octp.admin", + "ms/octp.admin", + "mo/octp.admin", + "mt/octp.admin", + "ne/octp.admin", + "nh/octp.admin", + "nc/octp.admin", + "nd/octp.admin", + "oh/octp.admin", + "ri/octp.admin", + "sc/octp.admin", + "sd/octp.admin", + "tn/octp.admin", + "ut/octp.admin", + "vt/octp.admin", + "va/octp.admin", + "wa/octp.admin", + "wv/octp.admin", + "wi/octp.admin", + "wy/octp.admin", + "coun/admin", + "al/coun.admin", + "ar/coun.admin", + "az/coun.admin", + "co/coun.admin", + "ct/coun.admin", + "dc/coun.admin", + "de/coun.admin", + "fl/coun.admin", + "ga/coun.admin", + "ia/coun.admin", + "in/coun.admin", + "ks/coun.admin", + "ky/coun.admin", + "la/coun.admin", + "me/coun.admin", + "md/coun.admin", + "mn/coun.admin", + "ms/coun.admin", + "mo/coun.admin", + "mt/coun.admin", + "ne/coun.admin", + "nh/coun.admin", + "nj/coun.admin", + "nc/coun.admin", + "nd/coun.admin", + "oh/coun.admin", + "ok/coun.admin", + "ri/coun.admin", + "sc/coun.admin", + "sd/coun.admin", + "tn/coun.admin", + "ut/coun.admin", + "vt/coun.admin", + "va/coun.admin", + "wa/coun.admin", + "wv/coun.admin", + "wi/coun.admin", + "wy/coun.admin" ] } ] - }, - "options": { + } + }, + "/v1/compacts/{compact}/providers/{providerId}/licenses/jurisdiction/{jurisdiction}/licenseType/{licenseType}/encumbrance/{encumbranceId}": { + "patch": { "parameters": [ + { + "name": "Authorization", + "in": "header", + "required": true, + "schema": { + "type": "string" + } + }, { "name": "compact", "in": "path", @@ -740,75 +1143,25 @@ "schema": { "type": "string" } - } - ], - "responses": { - "204": { - "description": "204 response", - "headers": { - "Access-Control-Allow-Origin": { - "schema": { - "type": "string" - } - }, - "Access-Control-Allow-Methods": { - "schema": { - "type": "string" - } - }, - "Vary": { - "schema": { - "type": "string" - } - }, - "Access-Control-Allow-Headers": { - "schema": { - "type": "string" - } - } - }, - "content": {} - } - } - } - }, - "/v1/public": { - "options": { - "responses": { - "204": { - "description": "204 response", - "headers": { - "Access-Control-Allow-Origin": { - "schema": { - "type": "string" - } - }, - "Access-Control-Allow-Methods": { - "schema": { - "type": "string" - } - }, - "Vary": { - "schema": { - "type": "string" - } - }, - "Access-Control-Allow-Headers": { - "schema": { - "type": "string" - } - } - }, - "content": {} - } - } - } - }, - "/v1/public/compacts/{compact}/providers/query": { - "post": { - "parameters": [ + }, { - "name": "compact", + "name": "jurisdiction", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "licenseType", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "encumbranceId", "in": "path", "required": true, "schema": { @@ -820,7 +1173,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/SandboLicenyuZlweRzUTEW" + "$ref": "#/components/schemas/TestALicenPVQzJn8rvFIW" } } }, @@ -832,57 +1185,139 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/SandboLicenf1YSNMeYKlGD" + "$ref": "#/components/schemas/TestALicenEMkErOzoY9dd" } } } } - } - }, - "options": { + }, + "security": [ + { + "TestBackendPipelineStackTestAPIStackLicenseApiStaffUsersPoolAuthorizer084A18F1": [ + "aslp/admin", + "al/aslp.admin", + "ak/aslp.admin", + "ar/aslp.admin", + "co/aslp.admin", + "de/aslp.admin", + "fl/aslp.admin", + "ga/aslp.admin", + "id/aslp.admin", + "in/aslp.admin", + "ia/aslp.admin", + "ks/aslp.admin", + "ky/aslp.admin", + "la/aslp.admin", + "me/aslp.admin", + "md/aslp.admin", + "mn/aslp.admin", + "ms/aslp.admin", + "mo/aslp.admin", + "mt/aslp.admin", + "ne/aslp.admin", + "nh/aslp.admin", + "nc/aslp.admin", + "oh/aslp.admin", + "ok/aslp.admin", + "ri/aslp.admin", + "sc/aslp.admin", + "tn/aslp.admin", + "ut/aslp.admin", + "vt/aslp.admin", + "va/aslp.admin", + "vi/aslp.admin", + "wa/aslp.admin", + "wv/aslp.admin", + "wi/aslp.admin", + "wy/aslp.admin", + "octp/admin", + "al/octp.admin", + "ar/octp.admin", + "az/octp.admin", + "co/octp.admin", + "de/octp.admin", + "ga/octp.admin", + "ia/octp.admin", + "in/octp.admin", + "ky/octp.admin", + "la/octp.admin", + "me/octp.admin", + "md/octp.admin", + "mn/octp.admin", + "ms/octp.admin", + "mo/octp.admin", + "mt/octp.admin", + "ne/octp.admin", + "nh/octp.admin", + "nc/octp.admin", + "nd/octp.admin", + "oh/octp.admin", + "ri/octp.admin", + "sc/octp.admin", + "sd/octp.admin", + "tn/octp.admin", + "ut/octp.admin", + "vt/octp.admin", + "va/octp.admin", + "wa/octp.admin", + "wv/octp.admin", + "wi/octp.admin", + "wy/octp.admin", + "coun/admin", + "al/coun.admin", + "ar/coun.admin", + "az/coun.admin", + "co/coun.admin", + "ct/coun.admin", + "dc/coun.admin", + "de/coun.admin", + "fl/coun.admin", + "ga/coun.admin", + "ia/coun.admin", + "in/coun.admin", + "ks/coun.admin", + "ky/coun.admin", + "la/coun.admin", + "me/coun.admin", + "md/coun.admin", + "mn/coun.admin", + "ms/coun.admin", + "mo/coun.admin", + "mt/coun.admin", + "ne/coun.admin", + "nh/coun.admin", + "nj/coun.admin", + "nc/coun.admin", + "nd/coun.admin", + "oh/coun.admin", + "ok/coun.admin", + "ri/coun.admin", + "sc/coun.admin", + "sd/coun.admin", + "tn/coun.admin", + "ut/coun.admin", + "vt/coun.admin", + "va/coun.admin", + "wa/coun.admin", + "wv/coun.admin", + "wi/coun.admin", + "wy/coun.admin" + ] + } + ] + } + }, + "/v1/compacts/{compact}/providers/{providerId}/licenses/jurisdiction/{jurisdiction}/licenseType/{licenseType}/investigation": { + "post": { "parameters": [ { - "name": "compact", - "in": "path", + "name": "Authorization", + "in": "header", "required": true, "schema": { "type": "string" } - } - ], - "responses": { - "204": { - "description": "204 response", - "headers": { - "Access-Control-Allow-Origin": { - "schema": { - "type": "string" - } - }, - "Access-Control-Allow-Methods": { - "schema": { - "type": "string" - } - }, - "Vary": { - "schema": { - "type": "string" - } - }, - "Access-Control-Allow-Headers": { - "schema": { - "type": "string" - } - } - }, - "content": {} - } - } - } - }, - "/v1/compacts/{compact}/providers/{providerId}/privileges/jurisdiction/{jurisdiction}/licenseType/{licenseType}": { - "options": { - "parameters": [ + }, { "name": "compact", "in": "path", @@ -916,39 +1351,155 @@ } } ], - "responses": { - "204": { - "description": "204 response", - "headers": { - "Access-Control-Allow-Origin": { - "schema": { - "type": "string" - } - }, - "Access-Control-Allow-Methods": { - "schema": { - "type": "string" - } - }, - "Vary": { - "schema": { - "type": "string" - } - }, - "Access-Control-Allow-Headers": { + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TestALicen1SjCp0NSNbsm" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "200 response", + "content": { + "application/json": { "schema": { - "type": "string" + "$ref": "#/components/schemas/TestALicenEMkErOzoY9dd" } } - }, - "content": {} + } } - } + }, + "security": [ + { + "TestBackendPipelineStackTestAPIStackLicenseApiStaffUsersPoolAuthorizer084A18F1": [ + "aslp/admin", + "al/aslp.admin", + "ak/aslp.admin", + "ar/aslp.admin", + "co/aslp.admin", + "de/aslp.admin", + "fl/aslp.admin", + "ga/aslp.admin", + "id/aslp.admin", + "in/aslp.admin", + "ia/aslp.admin", + "ks/aslp.admin", + "ky/aslp.admin", + "la/aslp.admin", + "me/aslp.admin", + "md/aslp.admin", + "mn/aslp.admin", + "ms/aslp.admin", + "mo/aslp.admin", + "mt/aslp.admin", + "ne/aslp.admin", + "nh/aslp.admin", + "nc/aslp.admin", + "oh/aslp.admin", + "ok/aslp.admin", + "ri/aslp.admin", + "sc/aslp.admin", + "tn/aslp.admin", + "ut/aslp.admin", + "vt/aslp.admin", + "va/aslp.admin", + "vi/aslp.admin", + "wa/aslp.admin", + "wv/aslp.admin", + "wi/aslp.admin", + "wy/aslp.admin", + "octp/admin", + "al/octp.admin", + "ar/octp.admin", + "az/octp.admin", + "co/octp.admin", + "de/octp.admin", + "ga/octp.admin", + "ia/octp.admin", + "in/octp.admin", + "ky/octp.admin", + "la/octp.admin", + "me/octp.admin", + "md/octp.admin", + "mn/octp.admin", + "ms/octp.admin", + "mo/octp.admin", + "mt/octp.admin", + "ne/octp.admin", + "nh/octp.admin", + "nc/octp.admin", + "nd/octp.admin", + "oh/octp.admin", + "ri/octp.admin", + "sc/octp.admin", + "sd/octp.admin", + "tn/octp.admin", + "ut/octp.admin", + "vt/octp.admin", + "va/octp.admin", + "wa/octp.admin", + "wv/octp.admin", + "wi/octp.admin", + "wy/octp.admin", + "coun/admin", + "al/coun.admin", + "ar/coun.admin", + "az/coun.admin", + "co/coun.admin", + "ct/coun.admin", + "dc/coun.admin", + "de/coun.admin", + "fl/coun.admin", + "ga/coun.admin", + "ia/coun.admin", + "in/coun.admin", + "ks/coun.admin", + "ky/coun.admin", + "la/coun.admin", + "me/coun.admin", + "md/coun.admin", + "mn/coun.admin", + "ms/coun.admin", + "mo/coun.admin", + "mt/coun.admin", + "ne/coun.admin", + "nh/coun.admin", + "nj/coun.admin", + "nc/coun.admin", + "nd/coun.admin", + "oh/coun.admin", + "ok/coun.admin", + "ri/coun.admin", + "sc/coun.admin", + "sd/coun.admin", + "tn/coun.admin", + "ut/coun.admin", + "vt/coun.admin", + "va/coun.admin", + "wa/coun.admin", + "wv/coun.admin", + "wi/coun.admin", + "wy/coun.admin" + ] + } + ] } }, - "/v1/compacts/{compact}/providers/{providerId}/licenses/jurisdiction/{jurisdiction}/licenseType": { - "options": { + "/v1/compacts/{compact}/providers/{providerId}/licenses/jurisdiction/{jurisdiction}/licenseType/{licenseType}/investigation/{investigationId}": { + "patch": { "parameters": [ + { + "name": "Authorization", + "in": "header", + "required": true, + "schema": { + "type": "string" + } + }, { "name": "compact", "in": "path", @@ -972,40 +1523,164 @@ "schema": { "type": "string" } + }, + { + "name": "licenseType", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "investigationId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } } ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TestALicenBPW27y0J3cSV" + } + } + }, + "required": true + }, "responses": { - "204": { - "description": "204 response", - "headers": { - "Access-Control-Allow-Origin": { - "schema": { - "type": "string" - } - }, - "Access-Control-Allow-Methods": { - "schema": { - "type": "string" - } - }, - "Vary": { - "schema": { - "type": "string" - } - }, - "Access-Control-Allow-Headers": { + "200": { + "description": "200 response", + "content": { + "application/json": { "schema": { - "type": "string" + "$ref": "#/components/schemas/TestALicenEMkErOzoY9dd" } } - }, - "content": {} + } } - } + }, + "security": [ + { + "TestBackendPipelineStackTestAPIStackLicenseApiStaffUsersPoolAuthorizer084A18F1": [ + "aslp/admin", + "al/aslp.admin", + "ak/aslp.admin", + "ar/aslp.admin", + "co/aslp.admin", + "de/aslp.admin", + "fl/aslp.admin", + "ga/aslp.admin", + "id/aslp.admin", + "in/aslp.admin", + "ia/aslp.admin", + "ks/aslp.admin", + "ky/aslp.admin", + "la/aslp.admin", + "me/aslp.admin", + "md/aslp.admin", + "mn/aslp.admin", + "ms/aslp.admin", + "mo/aslp.admin", + "mt/aslp.admin", + "ne/aslp.admin", + "nh/aslp.admin", + "nc/aslp.admin", + "oh/aslp.admin", + "ok/aslp.admin", + "ri/aslp.admin", + "sc/aslp.admin", + "tn/aslp.admin", + "ut/aslp.admin", + "vt/aslp.admin", + "va/aslp.admin", + "vi/aslp.admin", + "wa/aslp.admin", + "wv/aslp.admin", + "wi/aslp.admin", + "wy/aslp.admin", + "octp/admin", + "al/octp.admin", + "ar/octp.admin", + "az/octp.admin", + "co/octp.admin", + "de/octp.admin", + "ga/octp.admin", + "ia/octp.admin", + "in/octp.admin", + "ky/octp.admin", + "la/octp.admin", + "me/octp.admin", + "md/octp.admin", + "mn/octp.admin", + "ms/octp.admin", + "mo/octp.admin", + "mt/octp.admin", + "ne/octp.admin", + "nh/octp.admin", + "nc/octp.admin", + "nd/octp.admin", + "oh/octp.admin", + "ri/octp.admin", + "sc/octp.admin", + "sd/octp.admin", + "tn/octp.admin", + "ut/octp.admin", + "vt/octp.admin", + "va/octp.admin", + "wa/octp.admin", + "wv/octp.admin", + "wi/octp.admin", + "wy/octp.admin", + "coun/admin", + "al/coun.admin", + "ar/coun.admin", + "az/coun.admin", + "co/coun.admin", + "ct/coun.admin", + "dc/coun.admin", + "de/coun.admin", + "fl/coun.admin", + "ga/coun.admin", + "ia/coun.admin", + "in/coun.admin", + "ks/coun.admin", + "ky/coun.admin", + "la/coun.admin", + "me/coun.admin", + "md/coun.admin", + "mn/coun.admin", + "ms/coun.admin", + "mo/coun.admin", + "mt/coun.admin", + "ne/coun.admin", + "nh/coun.admin", + "nj/coun.admin", + "nc/coun.admin", + "nd/coun.admin", + "oh/coun.admin", + "ok/coun.admin", + "ri/coun.admin", + "sc/coun.admin", + "sd/coun.admin", + "tn/coun.admin", + "ut/coun.admin", + "vt/coun.admin", + "va/coun.admin", + "wa/coun.admin", + "wv/coun.admin", + "wi/coun.admin", + "wy/coun.admin" + ] + } + ] } }, - "/v1/compacts/{compact}/attestations/{attestationId}": { - "get": { + "/v1/compacts/{compact}/providers/{providerId}/militaryAudit": { + "patch": { "parameters": [ { "name": "Authorization", @@ -1024,7 +1699,7 @@ } }, { - "name": "attestationId", + "name": "providerId", "in": "path", "required": true, "schema": { @@ -1032,13 +1707,23 @@ } } ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TestALicenAPZgnted7k9a" + } + } + }, + "required": true + }, "responses": { "200": { "description": "200 response", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/SandboLicen0TA7m9cBJLpz" + "$ref": "#/components/schemas/TestALicenEMkErOzoY9dd" } } } @@ -1046,12 +1731,131 @@ }, "security": [ { - "SandboxAPIStackLicenseApiProviderUsersPoolAuthorizerEB7523BA": [] + "TestBackendPipelineStackTestAPIStackLicenseApiStaffUsersPoolAuthorizer084A18F1": [ + "aslp/admin", + "al/aslp.admin", + "ak/aslp.admin", + "ar/aslp.admin", + "co/aslp.admin", + "de/aslp.admin", + "fl/aslp.admin", + "ga/aslp.admin", + "id/aslp.admin", + "in/aslp.admin", + "ia/aslp.admin", + "ks/aslp.admin", + "ky/aslp.admin", + "la/aslp.admin", + "me/aslp.admin", + "md/aslp.admin", + "mn/aslp.admin", + "ms/aslp.admin", + "mo/aslp.admin", + "mt/aslp.admin", + "ne/aslp.admin", + "nh/aslp.admin", + "nc/aslp.admin", + "oh/aslp.admin", + "ok/aslp.admin", + "ri/aslp.admin", + "sc/aslp.admin", + "tn/aslp.admin", + "ut/aslp.admin", + "vt/aslp.admin", + "va/aslp.admin", + "vi/aslp.admin", + "wa/aslp.admin", + "wv/aslp.admin", + "wi/aslp.admin", + "wy/aslp.admin", + "octp/admin", + "al/octp.admin", + "ar/octp.admin", + "az/octp.admin", + "co/octp.admin", + "de/octp.admin", + "ga/octp.admin", + "ia/octp.admin", + "in/octp.admin", + "ky/octp.admin", + "la/octp.admin", + "me/octp.admin", + "md/octp.admin", + "mn/octp.admin", + "ms/octp.admin", + "mo/octp.admin", + "mt/octp.admin", + "ne/octp.admin", + "nh/octp.admin", + "nc/octp.admin", + "nd/octp.admin", + "oh/octp.admin", + "ri/octp.admin", + "sc/octp.admin", + "sd/octp.admin", + "tn/octp.admin", + "ut/octp.admin", + "vt/octp.admin", + "va/octp.admin", + "wa/octp.admin", + "wv/octp.admin", + "wi/octp.admin", + "wy/octp.admin", + "coun/admin", + "al/coun.admin", + "ar/coun.admin", + "az/coun.admin", + "co/coun.admin", + "ct/coun.admin", + "dc/coun.admin", + "de/coun.admin", + "fl/coun.admin", + "ga/coun.admin", + "ia/coun.admin", + "in/coun.admin", + "ks/coun.admin", + "ky/coun.admin", + "la/coun.admin", + "me/coun.admin", + "md/coun.admin", + "mn/coun.admin", + "ms/coun.admin", + "mo/coun.admin", + "mt/coun.admin", + "ne/coun.admin", + "nh/coun.admin", + "nj/coun.admin", + "nc/coun.admin", + "nd/coun.admin", + "oh/coun.admin", + "ok/coun.admin", + "ri/coun.admin", + "sc/coun.admin", + "sd/coun.admin", + "tn/coun.admin", + "ut/coun.admin", + "vt/coun.admin", + "va/coun.admin", + "wa/coun.admin", + "wv/coun.admin", + "wi/coun.admin", + "wy/coun.admin" + ] } ] - }, - "options": { + } + }, + "/v1/compacts/{compact}/providers/{providerId}/privileges/jurisdiction/{jurisdiction}/licenseType/{licenseType}/deactivate": { + "post": { "parameters": [ + { + "name": "Authorization", + "in": "header", + "required": true, + "schema": { + "type": "string" + } + }, { "name": "compact", "in": "path", @@ -1061,63 +1865,47 @@ } }, { - "name": "attestationId", + "name": "providerId", "in": "path", "required": true, "schema": { "type": "string" } - } - ], - "responses": { - "204": { - "description": "204 response", - "headers": { - "Access-Control-Allow-Origin": { - "schema": { - "type": "string" - } - }, - "Access-Control-Allow-Methods": { - "schema": { - "type": "string" - } - }, - "Vary": { - "schema": { - "type": "string" - } - }, - "Access-Control-Allow-Headers": { - "schema": { - "type": "string" - } - } - }, - "content": {} - } - } - } - }, - "/v1/purchases/privileges/options": { - "get": { - "parameters": [ + }, { - "name": "Authorization", - "in": "header", + "name": "jurisdiction", + "in": "path", "required": true, "schema": { "type": "string" } - } - ], - "responses": { + }, + { + "name": "licenseType", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TestALicenVGKXMT4yfZHA" + } + } + }, + "required": true + }, + "responses": { "200": { "description": "200 response", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/SandboLicenaiHAuDR162f2" + "$ref": "#/components/schemas/TestALicenEMkErOzoY9dd" } } } @@ -1125,44 +1913,131 @@ }, "security": [ { - "SandboxAPIStackLicenseApiProviderUsersPoolAuthorizerEB7523BA": [] + "TestBackendPipelineStackTestAPIStackLicenseApiStaffUsersPoolAuthorizer084A18F1": [ + "aslp/admin", + "al/aslp.admin", + "ak/aslp.admin", + "ar/aslp.admin", + "co/aslp.admin", + "de/aslp.admin", + "fl/aslp.admin", + "ga/aslp.admin", + "id/aslp.admin", + "in/aslp.admin", + "ia/aslp.admin", + "ks/aslp.admin", + "ky/aslp.admin", + "la/aslp.admin", + "me/aslp.admin", + "md/aslp.admin", + "mn/aslp.admin", + "ms/aslp.admin", + "mo/aslp.admin", + "mt/aslp.admin", + "ne/aslp.admin", + "nh/aslp.admin", + "nc/aslp.admin", + "oh/aslp.admin", + "ok/aslp.admin", + "ri/aslp.admin", + "sc/aslp.admin", + "tn/aslp.admin", + "ut/aslp.admin", + "vt/aslp.admin", + "va/aslp.admin", + "vi/aslp.admin", + "wa/aslp.admin", + "wv/aslp.admin", + "wi/aslp.admin", + "wy/aslp.admin", + "octp/admin", + "al/octp.admin", + "ar/octp.admin", + "az/octp.admin", + "co/octp.admin", + "de/octp.admin", + "ga/octp.admin", + "ia/octp.admin", + "in/octp.admin", + "ky/octp.admin", + "la/octp.admin", + "me/octp.admin", + "md/octp.admin", + "mn/octp.admin", + "ms/octp.admin", + "mo/octp.admin", + "mt/octp.admin", + "ne/octp.admin", + "nh/octp.admin", + "nc/octp.admin", + "nd/octp.admin", + "oh/octp.admin", + "ri/octp.admin", + "sc/octp.admin", + "sd/octp.admin", + "tn/octp.admin", + "ut/octp.admin", + "vt/octp.admin", + "va/octp.admin", + "wa/octp.admin", + "wv/octp.admin", + "wi/octp.admin", + "wy/octp.admin", + "coun/admin", + "al/coun.admin", + "ar/coun.admin", + "az/coun.admin", + "co/coun.admin", + "ct/coun.admin", + "dc/coun.admin", + "de/coun.admin", + "fl/coun.admin", + "ga/coun.admin", + "ia/coun.admin", + "in/coun.admin", + "ks/coun.admin", + "ky/coun.admin", + "la/coun.admin", + "me/coun.admin", + "md/coun.admin", + "mn/coun.admin", + "ms/coun.admin", + "mo/coun.admin", + "mt/coun.admin", + "ne/coun.admin", + "nh/coun.admin", + "nj/coun.admin", + "nc/coun.admin", + "nd/coun.admin", + "oh/coun.admin", + "ok/coun.admin", + "ri/coun.admin", + "sc/coun.admin", + "sd/coun.admin", + "tn/coun.admin", + "ut/coun.admin", + "vt/coun.admin", + "va/coun.admin", + "wa/coun.admin", + "wv/coun.admin", + "wi/coun.admin", + "wy/coun.admin" + ] } ] - }, - "options": { - "responses": { - "204": { - "description": "204 response", - "headers": { - "Access-Control-Allow-Origin": { - "schema": { - "type": "string" - } - }, - "Access-Control-Allow-Methods": { - "schema": { - "type": "string" - } - }, - "Vary": { - "schema": { - "type": "string" - } - }, - "Access-Control-Allow-Headers": { - "schema": { - "type": "string" - } - } - }, - "content": {} - } - } } }, - "/v1/public/compacts/{compact}": { - "options": { + "/v1/compacts/{compact}/providers/{providerId}/privileges/jurisdiction/{jurisdiction}/licenseType/{licenseType}/encumbrance": { + "post": { "parameters": [ + { + "name": "Authorization", + "in": "header", + "required": true, + "schema": { + "type": "string" + } + }, { "name": "compact", "in": "path", @@ -1170,44 +2045,10 @@ "schema": { "type": "string" } - } - ], - "responses": { - "204": { - "description": "204 response", - "headers": { - "Access-Control-Allow-Origin": { - "schema": { - "type": "string" - } - }, - "Access-Control-Allow-Methods": { - "schema": { - "type": "string" - } - }, - "Vary": { - "schema": { - "type": "string" - } - }, - "Access-Control-Allow-Headers": { - "schema": { - "type": "string" - } - } - }, - "content": {} - } - } - } - }, - "/v1/provider-users/me/jurisdiction/{jurisdiction}/licenseType/{licenseType}/history": { - "get": { - "parameters": [ + }, { - "name": "Authorization", - "in": "header", + "name": "providerId", + "in": "path", "required": true, "schema": { "type": "string" @@ -1230,13 +2071,23 @@ } } ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TestALicentD9lPRRh4cxz" + } + } + }, + "required": true + }, "responses": { "200": { "description": "200 response", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/SandboLicen0XcLtKpj7p28" + "$ref": "#/components/schemas/TestALicenEMkErOzoY9dd" } } } @@ -1244,14 +2095,133 @@ }, "security": [ { - "SandboxAPIStackLicenseApiProviderUsersPoolAuthorizerEB7523BA": [] + "TestBackendPipelineStackTestAPIStackLicenseApiStaffUsersPoolAuthorizer084A18F1": [ + "aslp/admin", + "al/aslp.admin", + "ak/aslp.admin", + "ar/aslp.admin", + "co/aslp.admin", + "de/aslp.admin", + "fl/aslp.admin", + "ga/aslp.admin", + "id/aslp.admin", + "in/aslp.admin", + "ia/aslp.admin", + "ks/aslp.admin", + "ky/aslp.admin", + "la/aslp.admin", + "me/aslp.admin", + "md/aslp.admin", + "mn/aslp.admin", + "ms/aslp.admin", + "mo/aslp.admin", + "mt/aslp.admin", + "ne/aslp.admin", + "nh/aslp.admin", + "nc/aslp.admin", + "oh/aslp.admin", + "ok/aslp.admin", + "ri/aslp.admin", + "sc/aslp.admin", + "tn/aslp.admin", + "ut/aslp.admin", + "vt/aslp.admin", + "va/aslp.admin", + "vi/aslp.admin", + "wa/aslp.admin", + "wv/aslp.admin", + "wi/aslp.admin", + "wy/aslp.admin", + "octp/admin", + "al/octp.admin", + "ar/octp.admin", + "az/octp.admin", + "co/octp.admin", + "de/octp.admin", + "ga/octp.admin", + "ia/octp.admin", + "in/octp.admin", + "ky/octp.admin", + "la/octp.admin", + "me/octp.admin", + "md/octp.admin", + "mn/octp.admin", + "ms/octp.admin", + "mo/octp.admin", + "mt/octp.admin", + "ne/octp.admin", + "nh/octp.admin", + "nc/octp.admin", + "nd/octp.admin", + "oh/octp.admin", + "ri/octp.admin", + "sc/octp.admin", + "sd/octp.admin", + "tn/octp.admin", + "ut/octp.admin", + "vt/octp.admin", + "va/octp.admin", + "wa/octp.admin", + "wv/octp.admin", + "wi/octp.admin", + "wy/octp.admin", + "coun/admin", + "al/coun.admin", + "ar/coun.admin", + "az/coun.admin", + "co/coun.admin", + "ct/coun.admin", + "dc/coun.admin", + "de/coun.admin", + "fl/coun.admin", + "ga/coun.admin", + "ia/coun.admin", + "in/coun.admin", + "ks/coun.admin", + "ky/coun.admin", + "la/coun.admin", + "me/coun.admin", + "md/coun.admin", + "mn/coun.admin", + "ms/coun.admin", + "mo/coun.admin", + "mt/coun.admin", + "ne/coun.admin", + "nh/coun.admin", + "nj/coun.admin", + "nc/coun.admin", + "nd/coun.admin", + "oh/coun.admin", + "ok/coun.admin", + "ri/coun.admin", + "sc/coun.admin", + "sd/coun.admin", + "tn/coun.admin", + "ut/coun.admin", + "vt/coun.admin", + "va/coun.admin", + "wa/coun.admin", + "wv/coun.admin", + "wi/coun.admin", + "wy/coun.admin" + ] } ] - }, - "options": { + } + }, + "/v1/compacts/{compact}/providers/{providerId}/privileges/jurisdiction/{jurisdiction}/licenseType/{licenseType}/encumbrance/{encumbranceId}": { + "patch": { "parameters": [ { - "name": "jurisdiction", + "name": "Authorization", + "in": "header", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "compact", "in": "path", "required": true, "schema": { @@ -1259,57 +2229,23 @@ } }, { - "name": "licenseType", + "name": "providerId", "in": "path", "required": true, "schema": { "type": "string" } - } - ], - "responses": { - "204": { - "description": "204 response", - "headers": { - "Access-Control-Allow-Origin": { - "schema": { - "type": "string" - } - }, - "Access-Control-Allow-Methods": { - "schema": { - "type": "string" - } - }, - "Vary": { - "schema": { - "type": "string" - } - }, - "Access-Control-Allow-Headers": { - "schema": { - "type": "string" - } - } - }, - "content": {} - } - } - } - }, - "/v1/compacts/{compact}/providers/{providerId}": { - "get": { - "parameters": [ + }, { - "name": "Authorization", - "in": "header", + "name": "jurisdiction", + "in": "path", "required": true, "schema": { "type": "string" } }, { - "name": "compact", + "name": "licenseType", "in": "path", "required": true, "schema": { @@ -1317,7 +2253,7 @@ } }, { - "name": "providerId", + "name": "encumbranceId", "in": "path", "required": true, "schema": { @@ -1325,13 +2261,23 @@ } } ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TestALicenoxYwJpjtTvcP" + } + } + }, + "required": true + }, "responses": { "200": { "description": "200 response", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/SandboLicenJlHz6gimzgVV" + "$ref": "#/components/schemas/TestALicenEMkErOzoY9dd" } } } @@ -1339,147 +2285,171 @@ }, "security": [ { - "SandboxAPIStackLicenseApiStaffUsersPoolAuthorizer14A84A9B": [ - "aslp/readGeneral", - "octp/readGeneral", - "coun/readGeneral" - ] - } - ] - }, - "options": { - "parameters": [ - { - "name": "compact", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - }, - { - "name": "providerId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "204": { - "description": "204 response", - "headers": { - "Access-Control-Allow-Origin": { - "schema": { - "type": "string" - } - }, - "Access-Control-Allow-Methods": { - "schema": { - "type": "string" - } - }, - "Vary": { - "schema": { - "type": "string" - } - }, - "Access-Control-Allow-Headers": { - "schema": { - "type": "string" - } - } - }, - "content": {} + "TestBackendPipelineStackTestAPIStackLicenseApiStaffUsersPoolAuthorizer084A18F1": [ + "aslp/admin", + "al/aslp.admin", + "ak/aslp.admin", + "ar/aslp.admin", + "co/aslp.admin", + "de/aslp.admin", + "fl/aslp.admin", + "ga/aslp.admin", + "id/aslp.admin", + "in/aslp.admin", + "ia/aslp.admin", + "ks/aslp.admin", + "ky/aslp.admin", + "la/aslp.admin", + "me/aslp.admin", + "md/aslp.admin", + "mn/aslp.admin", + "ms/aslp.admin", + "mo/aslp.admin", + "mt/aslp.admin", + "ne/aslp.admin", + "nh/aslp.admin", + "nc/aslp.admin", + "oh/aslp.admin", + "ok/aslp.admin", + "ri/aslp.admin", + "sc/aslp.admin", + "tn/aslp.admin", + "ut/aslp.admin", + "vt/aslp.admin", + "va/aslp.admin", + "vi/aslp.admin", + "wa/aslp.admin", + "wv/aslp.admin", + "wi/aslp.admin", + "wy/aslp.admin", + "octp/admin", + "al/octp.admin", + "ar/octp.admin", + "az/octp.admin", + "co/octp.admin", + "de/octp.admin", + "ga/octp.admin", + "ia/octp.admin", + "in/octp.admin", + "ky/octp.admin", + "la/octp.admin", + "me/octp.admin", + "md/octp.admin", + "mn/octp.admin", + "ms/octp.admin", + "mo/octp.admin", + "mt/octp.admin", + "ne/octp.admin", + "nh/octp.admin", + "nc/octp.admin", + "nd/octp.admin", + "oh/octp.admin", + "ri/octp.admin", + "sc/octp.admin", + "sd/octp.admin", + "tn/octp.admin", + "ut/octp.admin", + "vt/octp.admin", + "va/octp.admin", + "wa/octp.admin", + "wv/octp.admin", + "wi/octp.admin", + "wy/octp.admin", + "coun/admin", + "al/coun.admin", + "ar/coun.admin", + "az/coun.admin", + "co/coun.admin", + "ct/coun.admin", + "dc/coun.admin", + "de/coun.admin", + "fl/coun.admin", + "ga/coun.admin", + "ia/coun.admin", + "in/coun.admin", + "ks/coun.admin", + "ky/coun.admin", + "la/coun.admin", + "me/coun.admin", + "md/coun.admin", + "mn/coun.admin", + "ms/coun.admin", + "mo/coun.admin", + "mt/coun.admin", + "ne/coun.admin", + "nh/coun.admin", + "nj/coun.admin", + "nc/coun.admin", + "nd/coun.admin", + "oh/coun.admin", + "ok/coun.admin", + "ri/coun.admin", + "sc/coun.admin", + "sd/coun.admin", + "tn/coun.admin", + "ut/coun.admin", + "vt/coun.admin", + "va/coun.admin", + "wa/coun.admin", + "wv/coun.admin", + "wi/coun.admin", + "wy/coun.admin" + ] } - } + ] } }, - "/v1/public/jurisdictions/live": { + "/v1/compacts/{compact}/providers/{providerId}/privileges/jurisdiction/{jurisdiction}/licenseType/{licenseType}/history": { "get": { "parameters": [ + { + "name": "Authorization", + "in": "header", + "required": true, + "schema": { + "type": "string" + } + }, { "name": "compact", - "in": "query", + "in": "path", + "required": true, "schema": { "type": "string" } - } - ], - "responses": { - "200": { - "description": "200 response", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/SandboLicenEPhGnRpsTFzc" - } - } + }, + { + "name": "providerId", + "in": "path", + "required": true, + "schema": { + "type": "string" } - } - } - }, - "options": { - "responses": { - "204": { - "description": "204 response", - "headers": { - "Access-Control-Allow-Origin": { - "schema": { - "type": "string" - } - }, - "Access-Control-Allow-Methods": { - "schema": { - "type": "string" - } - }, - "Vary": { - "schema": { - "type": "string" - } - }, - "Access-Control-Allow-Headers": { - "schema": { - "type": "string" - } - } - }, - "content": {} - } - } - } - }, - "/v1/provider-users/me/home-jurisdiction": { - "put": { - "parameters": [ + }, { - "name": "Authorization", - "in": "header", + "name": "jurisdiction", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "licenseType", + "in": "path", "required": true, "schema": { "type": "string" } } ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/SandboLicenJVUAEniGDz2F" - } - } - }, - "required": true - }, "responses": { "200": { "description": "200 response", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/SandboLicenTMQQKAeKTKQR" + "$ref": "#/components/schemas/TestALicenxX5qS7HBoRtp" } } } @@ -1487,44 +2457,26 @@ }, "security": [ { - "SandboxAPIStackLicenseApiProviderUsersPoolAuthorizerEB7523BA": [] + "TestBackendPipelineStackTestAPIStackLicenseApiStaffUsersPoolAuthorizer084A18F1": [ + "aslp/readGeneral", + "octp/readGeneral", + "coun/readGeneral" + ] } ] - }, - "options": { - "responses": { - "204": { - "description": "204 response", - "headers": { - "Access-Control-Allow-Origin": { - "schema": { - "type": "string" - } - }, - "Access-Control-Allow-Methods": { - "schema": { - "type": "string" - } - }, - "Vary": { - "schema": { - "type": "string" - } - }, - "Access-Control-Allow-Headers": { - "schema": { - "type": "string" - } - } - }, - "content": {} - } - } } }, - "/v1/compacts/{compact}/providers/{providerId}/privileges": { - "options": { + "/v1/compacts/{compact}/providers/{providerId}/privileges/jurisdiction/{jurisdiction}/licenseType/{licenseType}/investigation": { + "post": { "parameters": [ + { + "name": "Authorization", + "in": "header", + "required": true, + "schema": { + "type": "string" + } + }, { "name": "compact", "in": "path", @@ -1540,107 +2492,17 @@ "schema": { "type": "string" } - } - ], - "responses": { - "204": { - "description": "204 response", - "headers": { - "Access-Control-Allow-Origin": { - "schema": { - "type": "string" - } - }, - "Access-Control-Allow-Methods": { - "schema": { - "type": "string" - } - }, - "Vary": { - "schema": { - "type": "string" - } - }, - "Access-Control-Allow-Headers": { - "schema": { - "type": "string" - } - } - }, - "content": {} - } - } - } - }, - "/v1/provider-users/verifyRecovery": { - "post": { - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/SandboLicen5bneP2wVdz6l" - } - } }, - "required": true - }, - "responses": { - "200": { - "description": "200 response", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/SandboLicenTMQQKAeKTKQR" - } - } - } - } - } - }, - "options": { - "responses": { - "204": { - "description": "204 response", - "headers": { - "Access-Control-Allow-Origin": { - "schema": { - "type": "string" - } - }, - "Access-Control-Allow-Methods": { - "schema": { - "type": "string" - } - }, - "Vary": { - "schema": { - "type": "string" - } - }, - "Access-Control-Allow-Headers": { - "schema": { - "type": "string" - } - } - }, - "content": {} - } - } - } - }, - "/v1/compacts/{compact}/providers/query": { - "post": { - "parameters": [ { - "name": "Authorization", - "in": "header", + "name": "jurisdiction", + "in": "path", "required": true, "schema": { "type": "string" } }, { - "name": "compact", + "name": "licenseType", "in": "path", "required": true, "schema": { @@ -1652,7 +2514,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/SandboLicenyuZlweRzUTEW" + "$ref": "#/components/schemas/TestALicenFQqmK0F4LBsK" } } }, @@ -1664,7 +2526,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/SandboLicenDTjDt3roB2dM" + "$ref": "#/components/schemas/TestALicenEMkErOzoY9dd" } } } @@ -1672,57 +2534,122 @@ }, "security": [ { - "SandboxAPIStackLicenseApiStaffUsersPoolAuthorizer14A84A9B": [ - "aslp/readGeneral", - "octp/readGeneral", - "coun/readGeneral" - ] - } - ] - }, - "options": { - "parameters": [ - { - "name": "compact", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "204": { - "description": "204 response", - "headers": { - "Access-Control-Allow-Origin": { - "schema": { - "type": "string" - } - }, - "Access-Control-Allow-Methods": { - "schema": { - "type": "string" - } - }, - "Vary": { - "schema": { - "type": "string" - } - }, - "Access-Control-Allow-Headers": { - "schema": { - "type": "string" - } - } - }, - "content": {} + "TestBackendPipelineStackTestAPIStackLicenseApiStaffUsersPoolAuthorizer084A18F1": [ + "aslp/admin", + "al/aslp.admin", + "ak/aslp.admin", + "ar/aslp.admin", + "co/aslp.admin", + "de/aslp.admin", + "fl/aslp.admin", + "ga/aslp.admin", + "id/aslp.admin", + "in/aslp.admin", + "ia/aslp.admin", + "ks/aslp.admin", + "ky/aslp.admin", + "la/aslp.admin", + "me/aslp.admin", + "md/aslp.admin", + "mn/aslp.admin", + "ms/aslp.admin", + "mo/aslp.admin", + "mt/aslp.admin", + "ne/aslp.admin", + "nh/aslp.admin", + "nc/aslp.admin", + "oh/aslp.admin", + "ok/aslp.admin", + "ri/aslp.admin", + "sc/aslp.admin", + "tn/aslp.admin", + "ut/aslp.admin", + "vt/aslp.admin", + "va/aslp.admin", + "vi/aslp.admin", + "wa/aslp.admin", + "wv/aslp.admin", + "wi/aslp.admin", + "wy/aslp.admin", + "octp/admin", + "al/octp.admin", + "ar/octp.admin", + "az/octp.admin", + "co/octp.admin", + "de/octp.admin", + "ga/octp.admin", + "ia/octp.admin", + "in/octp.admin", + "ky/octp.admin", + "la/octp.admin", + "me/octp.admin", + "md/octp.admin", + "mn/octp.admin", + "ms/octp.admin", + "mo/octp.admin", + "mt/octp.admin", + "ne/octp.admin", + "nh/octp.admin", + "nc/octp.admin", + "nd/octp.admin", + "oh/octp.admin", + "ri/octp.admin", + "sc/octp.admin", + "sd/octp.admin", + "tn/octp.admin", + "ut/octp.admin", + "vt/octp.admin", + "va/octp.admin", + "wa/octp.admin", + "wv/octp.admin", + "wi/octp.admin", + "wy/octp.admin", + "coun/admin", + "al/coun.admin", + "ar/coun.admin", + "az/coun.admin", + "co/coun.admin", + "ct/coun.admin", + "dc/coun.admin", + "de/coun.admin", + "fl/coun.admin", + "ga/coun.admin", + "ia/coun.admin", + "in/coun.admin", + "ks/coun.admin", + "ky/coun.admin", + "la/coun.admin", + "me/coun.admin", + "md/coun.admin", + "mn/coun.admin", + "ms/coun.admin", + "mo/coun.admin", + "mt/coun.admin", + "ne/coun.admin", + "nh/coun.admin", + "nj/coun.admin", + "nc/coun.admin", + "nd/coun.admin", + "oh/coun.admin", + "ok/coun.admin", + "ri/coun.admin", + "sc/coun.admin", + "sd/coun.admin", + "tn/coun.admin", + "ut/coun.admin", + "vt/coun.admin", + "va/coun.admin", + "wa/coun.admin", + "wv/coun.admin", + "wi/coun.admin", + "wy/coun.admin" + ] } - } + ] } }, - "/v1/compacts/{compact}/providers/{providerId}/privileges/jurisdiction/{jurisdiction}/licenseType/{licenseType}/investigation": { - "post": { + "/v1/compacts/{compact}/providers/{providerId}/privileges/jurisdiction/{jurisdiction}/licenseType/{licenseType}/investigation/{investigationId}": { + "patch": { "parameters": [ { "name": "Authorization", @@ -1763,13 +2690,21 @@ "schema": { "type": "string" } + }, + { + "name": "investigationId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } } ], "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/SandboLicenKbxrVseriPZY" + "$ref": "#/components/schemas/TestALicenkkd2ugQD7XDr" } } }, @@ -1781,7 +2716,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/SandboLicenTMQQKAeKTKQR" + "$ref": "#/components/schemas/TestALicenEMkErOzoY9dd" } } } @@ -1789,13 +2724,19 @@ }, "security": [ { - "SandboxAPIStackLicenseApiStaffUsersPoolAuthorizer14A84A9B": [ + "TestBackendPipelineStackTestAPIStackLicenseApiStaffUsersPoolAuthorizer084A18F1": [ "aslp/admin", "al/aslp.admin", "ak/aslp.admin", "ar/aslp.admin", "co/aslp.admin", "de/aslp.admin", + "fl/aslp.admin", + "ga/aslp.admin", + "id/aslp.admin", + "in/aslp.admin", + "ia/aslp.admin", + "ks/aslp.admin", "ky/aslp.admin", "la/aslp.admin", "me/aslp.admin", @@ -1803,49 +2744,113 @@ "mn/aslp.admin", "ms/aslp.admin", "mo/aslp.admin", + "mt/aslp.admin", "ne/aslp.admin", + "nh/aslp.admin", + "nc/aslp.admin", "oh/aslp.admin", + "ok/aslp.admin", + "ri/aslp.admin", + "sc/aslp.admin", + "tn/aslp.admin", + "ut/aslp.admin", + "vt/aslp.admin", + "va/aslp.admin", + "vi/aslp.admin", + "wa/aslp.admin", + "wv/aslp.admin", + "wi/aslp.admin", + "wy/aslp.admin", "octp/admin", "al/octp.admin", "ar/octp.admin", + "az/octp.admin", + "co/octp.admin", + "de/octp.admin", + "ga/octp.admin", + "ia/octp.admin", + "in/octp.admin", "ky/octp.admin", "la/octp.admin", + "me/octp.admin", + "md/octp.admin", + "mn/octp.admin", "ms/octp.admin", + "mo/octp.admin", + "mt/octp.admin", "ne/octp.admin", + "nh/octp.admin", + "nc/octp.admin", + "nd/octp.admin", "oh/octp.admin", + "ri/octp.admin", + "sc/octp.admin", + "sd/octp.admin", + "tn/octp.admin", + "ut/octp.admin", + "vt/octp.admin", + "va/octp.admin", + "wa/octp.admin", + "wv/octp.admin", + "wi/octp.admin", + "wy/octp.admin", "coun/admin", "al/coun.admin", "ar/coun.admin", + "az/coun.admin", + "co/coun.admin", + "ct/coun.admin", + "dc/coun.admin", + "de/coun.admin", "fl/coun.admin", "ga/coun.admin", + "ia/coun.admin", + "in/coun.admin", + "ks/coun.admin", "ky/coun.admin", + "la/coun.admin", + "me/coun.admin", + "md/coun.admin", + "mn/coun.admin", + "ms/coun.admin", + "mo/coun.admin", + "mt/coun.admin", "ne/coun.admin", + "nh/coun.admin", + "nj/coun.admin", + "nc/coun.admin", + "nd/coun.admin", "oh/coun.admin", - "ut/coun.admin" + "ok/coun.admin", + "ri/coun.admin", + "sc/coun.admin", + "sd/coun.admin", + "tn/coun.admin", + "ut/coun.admin", + "vt/coun.admin", + "va/coun.admin", + "wa/coun.admin", + "wv/coun.admin", + "wi/coun.admin", + "wy/coun.admin" ] } ] - }, - "options": { + } + }, + "/v1/compacts/{compact}/providers/{providerId}/ssn": { + "get": { "parameters": [ { - "name": "compact", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - }, - { - "name": "providerId", - "in": "path", + "name": "Authorization", + "in": "header", "required": true, "schema": { "type": "string" } }, { - "name": "jurisdiction", + "name": "compact", "in": "path", "required": true, "schema": { @@ -1853,7 +2858,7 @@ } }, { - "name": "licenseType", + "name": "providerId", "in": "path", "required": true, "schema": { @@ -1862,64 +2867,159 @@ } ], "responses": { - "204": { - "description": "204 response", - "headers": { - "Access-Control-Allow-Origin": { - "schema": { - "type": "string" - } - }, - "Access-Control-Allow-Methods": { - "schema": { - "type": "string" - } - }, - "Vary": { - "schema": { - "type": "string" - } - }, - "Access-Control-Allow-Headers": { + "200": { + "description": "200 response", + "content": { + "application/json": { "schema": { - "type": "string" + "$ref": "#/components/schemas/TestALicenvD3siA7fe7r2" } } - }, - "content": {} + } } - } + }, + "security": [ + { + "TestBackendPipelineStackTestAPIStackLicenseApiStaffUsersPoolAuthorizer084A18F1": [ + "aslp/readSSN", + "al/aslp.readSSN", + "ak/aslp.readSSN", + "ar/aslp.readSSN", + "co/aslp.readSSN", + "de/aslp.readSSN", + "fl/aslp.readSSN", + "ga/aslp.readSSN", + "id/aslp.readSSN", + "in/aslp.readSSN", + "ia/aslp.readSSN", + "ks/aslp.readSSN", + "ky/aslp.readSSN", + "la/aslp.readSSN", + "me/aslp.readSSN", + "md/aslp.readSSN", + "mn/aslp.readSSN", + "ms/aslp.readSSN", + "mo/aslp.readSSN", + "mt/aslp.readSSN", + "ne/aslp.readSSN", + "nh/aslp.readSSN", + "nc/aslp.readSSN", + "oh/aslp.readSSN", + "ok/aslp.readSSN", + "ri/aslp.readSSN", + "sc/aslp.readSSN", + "tn/aslp.readSSN", + "ut/aslp.readSSN", + "vt/aslp.readSSN", + "va/aslp.readSSN", + "vi/aslp.readSSN", + "wa/aslp.readSSN", + "wv/aslp.readSSN", + "wi/aslp.readSSN", + "wy/aslp.readSSN", + "octp/readSSN", + "al/octp.readSSN", + "ar/octp.readSSN", + "az/octp.readSSN", + "co/octp.readSSN", + "de/octp.readSSN", + "ga/octp.readSSN", + "ia/octp.readSSN", + "in/octp.readSSN", + "ky/octp.readSSN", + "la/octp.readSSN", + "me/octp.readSSN", + "md/octp.readSSN", + "mn/octp.readSSN", + "ms/octp.readSSN", + "mo/octp.readSSN", + "mt/octp.readSSN", + "ne/octp.readSSN", + "nh/octp.readSSN", + "nc/octp.readSSN", + "nd/octp.readSSN", + "oh/octp.readSSN", + "ri/octp.readSSN", + "sc/octp.readSSN", + "sd/octp.readSSN", + "tn/octp.readSSN", + "ut/octp.readSSN", + "vt/octp.readSSN", + "va/octp.readSSN", + "wa/octp.readSSN", + "wv/octp.readSSN", + "wi/octp.readSSN", + "wy/octp.readSSN", + "coun/readSSN", + "al/coun.readSSN", + "ar/coun.readSSN", + "az/coun.readSSN", + "co/coun.readSSN", + "ct/coun.readSSN", + "dc/coun.readSSN", + "de/coun.readSSN", + "fl/coun.readSSN", + "ga/coun.readSSN", + "ia/coun.readSSN", + "in/coun.readSSN", + "ks/coun.readSSN", + "ky/coun.readSSN", + "la/coun.readSSN", + "me/coun.readSSN", + "md/coun.readSSN", + "mn/coun.readSSN", + "ms/coun.readSSN", + "mo/coun.readSSN", + "mt/coun.readSSN", + "ne/coun.readSSN", + "nh/coun.readSSN", + "nj/coun.readSSN", + "nc/coun.readSSN", + "nd/coun.readSSN", + "oh/coun.readSSN", + "ok/coun.readSSN", + "ri/coun.readSSN", + "sc/coun.readSSN", + "sd/coun.readSSN", + "tn/coun.readSSN", + "ut/coun.readSSN", + "vt/coun.readSSN", + "va/coun.readSSN", + "wa/coun.readSSN", + "wv/coun.readSSN", + "wi/coun.readSSN", + "wy/coun.readSSN" + ] + } + ] } }, - "/v1/provider-users/me/military-affiliation": { - "post": { + "/v1/compacts/{compact}/staff-users": { + "get": { "parameters": [ { - "name": "Authorization", - "in": "header", + "name": "compact", + "in": "path", "required": true, "schema": { "type": "string" } } ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/SandboLicenoBgekYzIk0Uy" - } - } - }, - "required": true - }, "responses": { "200": { "description": "200 response", + "headers": { + "Access-Control-Allow-Origin": { + "schema": { + "type": "string" + } + } + }, "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/SandboLicenSFWw1LC3Pl63" + "$ref": "#/components/schemas/TestALicenlDVuL3LTR9jI" } } } @@ -1927,45 +3027,124 @@ }, "security": [ { - "SandboxAPIStackLicenseApiProviderUsersPoolAuthorizerEB7523BA": [] + "TestBackendPipelineStackTestAPIStackLicenseApiStaffUsersPoolAuthorizer084A18F1": [ + "aslp/admin", + "al/aslp.admin", + "ak/aslp.admin", + "ar/aslp.admin", + "co/aslp.admin", + "de/aslp.admin", + "fl/aslp.admin", + "ga/aslp.admin", + "id/aslp.admin", + "in/aslp.admin", + "ia/aslp.admin", + "ks/aslp.admin", + "ky/aslp.admin", + "la/aslp.admin", + "me/aslp.admin", + "md/aslp.admin", + "mn/aslp.admin", + "ms/aslp.admin", + "mo/aslp.admin", + "mt/aslp.admin", + "ne/aslp.admin", + "nh/aslp.admin", + "nc/aslp.admin", + "oh/aslp.admin", + "ok/aslp.admin", + "ri/aslp.admin", + "sc/aslp.admin", + "tn/aslp.admin", + "ut/aslp.admin", + "vt/aslp.admin", + "va/aslp.admin", + "vi/aslp.admin", + "wa/aslp.admin", + "wv/aslp.admin", + "wi/aslp.admin", + "wy/aslp.admin", + "octp/admin", + "al/octp.admin", + "ar/octp.admin", + "az/octp.admin", + "co/octp.admin", + "de/octp.admin", + "ga/octp.admin", + "ia/octp.admin", + "in/octp.admin", + "ky/octp.admin", + "la/octp.admin", + "me/octp.admin", + "md/octp.admin", + "mn/octp.admin", + "ms/octp.admin", + "mo/octp.admin", + "mt/octp.admin", + "ne/octp.admin", + "nh/octp.admin", + "nc/octp.admin", + "nd/octp.admin", + "oh/octp.admin", + "ri/octp.admin", + "sc/octp.admin", + "sd/octp.admin", + "tn/octp.admin", + "ut/octp.admin", + "vt/octp.admin", + "va/octp.admin", + "wa/octp.admin", + "wv/octp.admin", + "wi/octp.admin", + "wy/octp.admin", + "coun/admin", + "al/coun.admin", + "ar/coun.admin", + "az/coun.admin", + "co/coun.admin", + "ct/coun.admin", + "dc/coun.admin", + "de/coun.admin", + "fl/coun.admin", + "ga/coun.admin", + "ia/coun.admin", + "in/coun.admin", + "ks/coun.admin", + "ky/coun.admin", + "la/coun.admin", + "me/coun.admin", + "md/coun.admin", + "mn/coun.admin", + "ms/coun.admin", + "mo/coun.admin", + "mt/coun.admin", + "ne/coun.admin", + "nh/coun.admin", + "nj/coun.admin", + "nc/coun.admin", + "nd/coun.admin", + "oh/coun.admin", + "ok/coun.admin", + "ri/coun.admin", + "sc/coun.admin", + "sd/coun.admin", + "tn/coun.admin", + "ut/coun.admin", + "vt/coun.admin", + "va/coun.admin", + "wa/coun.admin", + "wv/coun.admin", + "wi/coun.admin", + "wy/coun.admin" + ] } ] }, - "options": { - "responses": { - "204": { - "description": "204 response", - "headers": { - "Access-Control-Allow-Origin": { - "schema": { - "type": "string" - } - }, - "Access-Control-Allow-Methods": { - "schema": { - "type": "string" - } - }, - "Vary": { - "schema": { - "type": "string" - } - }, - "Access-Control-Allow-Headers": { - "schema": { - "type": "string" - } - } - }, - "content": {} - } - } - }, - "patch": { + "post": { "parameters": [ { - "name": "Authorization", - "in": "header", + "name": "compact", + "in": "path", "required": true, "schema": { "type": "string" @@ -1976,7 +3155,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/SandboLicenztG3aZP1J9M3" + "$ref": "#/components/schemas/TestALicenuCn4gplRNmBC" } } }, @@ -1985,10 +3164,17 @@ "responses": { "200": { "description": "200 response", + "headers": { + "Access-Control-Allow-Origin": { + "schema": { + "type": "string" + } + } + }, "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/SandboLicenTMQQKAeKTKQR" + "$ref": "#/components/schemas/TestALicenY0wRQnCr1lHl" } } } @@ -1996,40 +3182,125 @@ }, "security": [ { - "SandboxAPIStackLicenseApiProviderUsersPoolAuthorizerEB7523BA": [] - } - ] - } - }, - "/v1/compacts/{compact}/providers/{providerId}/licenses/jurisdiction/{jurisdiction}/licenseType/{licenseType}/encumbrance/{encumbranceId}": { - "options": { - "parameters": [ - { - "name": "compact", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - }, - { - "name": "providerId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - }, - { - "name": "jurisdiction", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - }, + "TestBackendPipelineStackTestAPIStackLicenseApiStaffUsersPoolAuthorizer084A18F1": [ + "aslp/admin", + "al/aslp.admin", + "ak/aslp.admin", + "ar/aslp.admin", + "co/aslp.admin", + "de/aslp.admin", + "fl/aslp.admin", + "ga/aslp.admin", + "id/aslp.admin", + "in/aslp.admin", + "ia/aslp.admin", + "ks/aslp.admin", + "ky/aslp.admin", + "la/aslp.admin", + "me/aslp.admin", + "md/aslp.admin", + "mn/aslp.admin", + "ms/aslp.admin", + "mo/aslp.admin", + "mt/aslp.admin", + "ne/aslp.admin", + "nh/aslp.admin", + "nc/aslp.admin", + "oh/aslp.admin", + "ok/aslp.admin", + "ri/aslp.admin", + "sc/aslp.admin", + "tn/aslp.admin", + "ut/aslp.admin", + "vt/aslp.admin", + "va/aslp.admin", + "vi/aslp.admin", + "wa/aslp.admin", + "wv/aslp.admin", + "wi/aslp.admin", + "wy/aslp.admin", + "octp/admin", + "al/octp.admin", + "ar/octp.admin", + "az/octp.admin", + "co/octp.admin", + "de/octp.admin", + "ga/octp.admin", + "ia/octp.admin", + "in/octp.admin", + "ky/octp.admin", + "la/octp.admin", + "me/octp.admin", + "md/octp.admin", + "mn/octp.admin", + "ms/octp.admin", + "mo/octp.admin", + "mt/octp.admin", + "ne/octp.admin", + "nh/octp.admin", + "nc/octp.admin", + "nd/octp.admin", + "oh/octp.admin", + "ri/octp.admin", + "sc/octp.admin", + "sd/octp.admin", + "tn/octp.admin", + "ut/octp.admin", + "vt/octp.admin", + "va/octp.admin", + "wa/octp.admin", + "wv/octp.admin", + "wi/octp.admin", + "wy/octp.admin", + "coun/admin", + "al/coun.admin", + "ar/coun.admin", + "az/coun.admin", + "co/coun.admin", + "ct/coun.admin", + "dc/coun.admin", + "de/coun.admin", + "fl/coun.admin", + "ga/coun.admin", + "ia/coun.admin", + "in/coun.admin", + "ks/coun.admin", + "ky/coun.admin", + "la/coun.admin", + "me/coun.admin", + "md/coun.admin", + "mn/coun.admin", + "ms/coun.admin", + "mo/coun.admin", + "mt/coun.admin", + "ne/coun.admin", + "nh/coun.admin", + "nj/coun.admin", + "nc/coun.admin", + "nd/coun.admin", + "oh/coun.admin", + "ok/coun.admin", + "ri/coun.admin", + "sc/coun.admin", + "sd/coun.admin", + "tn/coun.admin", + "ut/coun.admin", + "vt/coun.admin", + "va/coun.admin", + "wa/coun.admin", + "wv/coun.admin", + "wi/coun.admin", + "wy/coun.admin" + ] + } + ] + } + }, + "/v1/compacts/{compact}/staff-users/{userId}": { + "get": { + "parameters": [ { - "name": "licenseType", + "name": "compact", "in": "path", "required": true, "schema": { @@ -2037,7 +3308,7 @@ } }, { - "name": "encumbranceId", + "name": "userId", "in": "path", "required": true, "schema": { @@ -2046,44 +3317,151 @@ } ], "responses": { - "204": { - "description": "204 response", - "headers": { - "Access-Control-Allow-Origin": { - "schema": { - "type": "string" - } - }, - "Access-Control-Allow-Methods": { + "404": { + "description": "404 response", + "content": { + "application/json": { "schema": { - "type": "string" + "$ref": "#/components/schemas/TestALicenEMkErOzoY9dd" } - }, - "Vary": { + } + } + }, + "200": { + "description": "200 response", + "headers": { + "Access-Control-Allow-Origin": { "schema": { "type": "string" } - }, - "Access-Control-Allow-Headers": { + } + }, + "content": { + "application/json": { "schema": { - "type": "string" + "$ref": "#/components/schemas/TestALicenY0wRQnCr1lHl" } } - }, - "content": {} + } } - } + }, + "security": [ + { + "TestBackendPipelineStackTestAPIStackLicenseApiStaffUsersPoolAuthorizer084A18F1": [ + "aslp/admin", + "al/aslp.admin", + "ak/aslp.admin", + "ar/aslp.admin", + "co/aslp.admin", + "de/aslp.admin", + "fl/aslp.admin", + "ga/aslp.admin", + "id/aslp.admin", + "in/aslp.admin", + "ia/aslp.admin", + "ks/aslp.admin", + "ky/aslp.admin", + "la/aslp.admin", + "me/aslp.admin", + "md/aslp.admin", + "mn/aslp.admin", + "ms/aslp.admin", + "mo/aslp.admin", + "mt/aslp.admin", + "ne/aslp.admin", + "nh/aslp.admin", + "nc/aslp.admin", + "oh/aslp.admin", + "ok/aslp.admin", + "ri/aslp.admin", + "sc/aslp.admin", + "tn/aslp.admin", + "ut/aslp.admin", + "vt/aslp.admin", + "va/aslp.admin", + "vi/aslp.admin", + "wa/aslp.admin", + "wv/aslp.admin", + "wi/aslp.admin", + "wy/aslp.admin", + "octp/admin", + "al/octp.admin", + "ar/octp.admin", + "az/octp.admin", + "co/octp.admin", + "de/octp.admin", + "ga/octp.admin", + "ia/octp.admin", + "in/octp.admin", + "ky/octp.admin", + "la/octp.admin", + "me/octp.admin", + "md/octp.admin", + "mn/octp.admin", + "ms/octp.admin", + "mo/octp.admin", + "mt/octp.admin", + "ne/octp.admin", + "nh/octp.admin", + "nc/octp.admin", + "nd/octp.admin", + "oh/octp.admin", + "ri/octp.admin", + "sc/octp.admin", + "sd/octp.admin", + "tn/octp.admin", + "ut/octp.admin", + "vt/octp.admin", + "va/octp.admin", + "wa/octp.admin", + "wv/octp.admin", + "wi/octp.admin", + "wy/octp.admin", + "coun/admin", + "al/coun.admin", + "ar/coun.admin", + "az/coun.admin", + "co/coun.admin", + "ct/coun.admin", + "dc/coun.admin", + "de/coun.admin", + "fl/coun.admin", + "ga/coun.admin", + "ia/coun.admin", + "in/coun.admin", + "ks/coun.admin", + "ky/coun.admin", + "la/coun.admin", + "me/coun.admin", + "md/coun.admin", + "mn/coun.admin", + "ms/coun.admin", + "mo/coun.admin", + "mt/coun.admin", + "ne/coun.admin", + "nh/coun.admin", + "nj/coun.admin", + "nc/coun.admin", + "nd/coun.admin", + "oh/coun.admin", + "ok/coun.admin", + "ri/coun.admin", + "sc/coun.admin", + "sd/coun.admin", + "tn/coun.admin", + "ut/coun.admin", + "vt/coun.admin", + "va/coun.admin", + "wa/coun.admin", + "wv/coun.admin", + "wi/coun.admin", + "wy/coun.admin" + ] + } + ] }, - "patch": { + "delete": { "parameters": [ - { - "name": "Authorization", - "in": "header", - "required": true, - "schema": { - "type": "string" - } - }, { "name": "compact", "in": "path", @@ -2093,23 +3471,155 @@ } }, { - "name": "providerId", + "name": "userId", "in": "path", "required": true, "schema": { "type": "string" } - }, - { - "name": "jurisdiction", - "in": "path", - "required": true, - "schema": { - "type": "string" + } + ], + "responses": { + "404": { + "description": "404 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TestALicenEMkErOzoY9dd" + } + } } }, + "200": { + "description": "200 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TestALicenEMkErOzoY9dd" + } + } + } + } + }, + "security": [ { - "name": "licenseType", + "TestBackendPipelineStackTestAPIStackLicenseApiStaffUsersPoolAuthorizer084A18F1": [ + "aslp/admin", + "al/aslp.admin", + "ak/aslp.admin", + "ar/aslp.admin", + "co/aslp.admin", + "de/aslp.admin", + "fl/aslp.admin", + "ga/aslp.admin", + "id/aslp.admin", + "in/aslp.admin", + "ia/aslp.admin", + "ks/aslp.admin", + "ky/aslp.admin", + "la/aslp.admin", + "me/aslp.admin", + "md/aslp.admin", + "mn/aslp.admin", + "ms/aslp.admin", + "mo/aslp.admin", + "mt/aslp.admin", + "ne/aslp.admin", + "nh/aslp.admin", + "nc/aslp.admin", + "oh/aslp.admin", + "ok/aslp.admin", + "ri/aslp.admin", + "sc/aslp.admin", + "tn/aslp.admin", + "ut/aslp.admin", + "vt/aslp.admin", + "va/aslp.admin", + "vi/aslp.admin", + "wa/aslp.admin", + "wv/aslp.admin", + "wi/aslp.admin", + "wy/aslp.admin", + "octp/admin", + "al/octp.admin", + "ar/octp.admin", + "az/octp.admin", + "co/octp.admin", + "de/octp.admin", + "ga/octp.admin", + "ia/octp.admin", + "in/octp.admin", + "ky/octp.admin", + "la/octp.admin", + "me/octp.admin", + "md/octp.admin", + "mn/octp.admin", + "ms/octp.admin", + "mo/octp.admin", + "mt/octp.admin", + "ne/octp.admin", + "nh/octp.admin", + "nc/octp.admin", + "nd/octp.admin", + "oh/octp.admin", + "ri/octp.admin", + "sc/octp.admin", + "sd/octp.admin", + "tn/octp.admin", + "ut/octp.admin", + "vt/octp.admin", + "va/octp.admin", + "wa/octp.admin", + "wv/octp.admin", + "wi/octp.admin", + "wy/octp.admin", + "coun/admin", + "al/coun.admin", + "ar/coun.admin", + "az/coun.admin", + "co/coun.admin", + "ct/coun.admin", + "dc/coun.admin", + "de/coun.admin", + "fl/coun.admin", + "ga/coun.admin", + "ia/coun.admin", + "in/coun.admin", + "ks/coun.admin", + "ky/coun.admin", + "la/coun.admin", + "me/coun.admin", + "md/coun.admin", + "mn/coun.admin", + "ms/coun.admin", + "mo/coun.admin", + "mt/coun.admin", + "ne/coun.admin", + "nh/coun.admin", + "nj/coun.admin", + "nc/coun.admin", + "nd/coun.admin", + "oh/coun.admin", + "ok/coun.admin", + "ri/coun.admin", + "sc/coun.admin", + "sd/coun.admin", + "tn/coun.admin", + "ut/coun.admin", + "vt/coun.admin", + "va/coun.admin", + "wa/coun.admin", + "wv/coun.admin", + "wi/coun.admin", + "wy/coun.admin" + ] + } + ] + }, + "patch": { + "parameters": [ + { + "name": "compact", "in": "path", "required": true, "schema": { @@ -2117,7 +3627,7 @@ } }, { - "name": "encumbranceId", + "name": "userId", "in": "path", "required": true, "schema": { @@ -2129,19 +3639,36 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/SandboLicenFqy1VQNhsjvi" + "$ref": "#/components/schemas/TestALicenOPOzFOrHHN9b" } } }, "required": true }, "responses": { + "404": { + "description": "404 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TestALicenEMkErOzoY9dd" + } + } + } + }, "200": { "description": "200 response", + "headers": { + "Access-Control-Allow-Origin": { + "schema": { + "type": "string" + } + } + }, "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/SandboLicenTMQQKAeKTKQR" + "$ref": "#/components/schemas/TestALicenY0wRQnCr1lHl" } } } @@ -2149,13 +3676,19 @@ }, "security": [ { - "SandboxAPIStackLicenseApiStaffUsersPoolAuthorizer14A84A9B": [ + "TestBackendPipelineStackTestAPIStackLicenseApiStaffUsersPoolAuthorizer084A18F1": [ "aslp/admin", "al/aslp.admin", "ak/aslp.admin", "ar/aslp.admin", "co/aslp.admin", "de/aslp.admin", + "fl/aslp.admin", + "ga/aslp.admin", + "id/aslp.admin", + "in/aslp.admin", + "ia/aslp.admin", + "ks/aslp.admin", "ky/aslp.admin", "la/aslp.admin", "me/aslp.admin", @@ -2163,32 +3696,102 @@ "mn/aslp.admin", "ms/aslp.admin", "mo/aslp.admin", + "mt/aslp.admin", "ne/aslp.admin", + "nh/aslp.admin", + "nc/aslp.admin", "oh/aslp.admin", + "ok/aslp.admin", + "ri/aslp.admin", + "sc/aslp.admin", + "tn/aslp.admin", + "ut/aslp.admin", + "vt/aslp.admin", + "va/aslp.admin", + "vi/aslp.admin", + "wa/aslp.admin", + "wv/aslp.admin", + "wi/aslp.admin", + "wy/aslp.admin", "octp/admin", "al/octp.admin", "ar/octp.admin", + "az/octp.admin", + "co/octp.admin", + "de/octp.admin", + "ga/octp.admin", + "ia/octp.admin", + "in/octp.admin", "ky/octp.admin", "la/octp.admin", + "me/octp.admin", + "md/octp.admin", + "mn/octp.admin", "ms/octp.admin", + "mo/octp.admin", + "mt/octp.admin", "ne/octp.admin", + "nh/octp.admin", + "nc/octp.admin", + "nd/octp.admin", "oh/octp.admin", + "ri/octp.admin", + "sc/octp.admin", + "sd/octp.admin", + "tn/octp.admin", + "ut/octp.admin", + "vt/octp.admin", + "va/octp.admin", + "wa/octp.admin", + "wv/octp.admin", + "wi/octp.admin", + "wy/octp.admin", "coun/admin", "al/coun.admin", "ar/coun.admin", + "az/coun.admin", + "co/coun.admin", + "ct/coun.admin", + "dc/coun.admin", + "de/coun.admin", "fl/coun.admin", "ga/coun.admin", + "ia/coun.admin", + "in/coun.admin", + "ks/coun.admin", "ky/coun.admin", + "la/coun.admin", + "me/coun.admin", + "md/coun.admin", + "mn/coun.admin", + "ms/coun.admin", + "mo/coun.admin", + "mt/coun.admin", "ne/coun.admin", + "nh/coun.admin", + "nj/coun.admin", + "nc/coun.admin", + "nd/coun.admin", "oh/coun.admin", - "ut/coun.admin" + "ok/coun.admin", + "ri/coun.admin", + "sc/coun.admin", + "sd/coun.admin", + "tn/coun.admin", + "ut/coun.admin", + "vt/coun.admin", + "va/coun.admin", + "wa/coun.admin", + "wv/coun.admin", + "wi/coun.admin", + "wy/coun.admin" ] } ] } }, - "/v1/compacts/{compact}/providers/{providerId}/privileges/jurisdiction": { - "options": { + "/v1/compacts/{compact}/staff-users/{userId}/reinvite": { + "post": { "parameters": [ { "name": "compact", @@ -2199,7 +3802,7 @@ } }, { - "name": "providerId", + "name": "userId", "in": "path", "required": true, "schema": { @@ -2208,33 +3811,141 @@ } ], "responses": { - "204": { - "description": "204 response", - "headers": { - "Access-Control-Allow-Origin": { - "schema": { - "type": "string" - } - }, - "Access-Control-Allow-Methods": { - "schema": { - "type": "string" - } - }, - "Vary": { + "404": { + "description": "404 response", + "content": { + "application/json": { "schema": { - "type": "string" + "$ref": "#/components/schemas/TestALicenEMkErOzoY9dd" } - }, - "Access-Control-Allow-Headers": { + } + } + }, + "200": { + "description": "200 response", + "content": { + "application/json": { "schema": { - "type": "string" + "$ref": "#/components/schemas/TestALicenEMkErOzoY9dd" } } - }, - "content": {} + } } - } + }, + "security": [ + { + "TestBackendPipelineStackTestAPIStackLicenseApiStaffUsersPoolAuthorizer084A18F1": [ + "aslp/admin", + "al/aslp.admin", + "ak/aslp.admin", + "ar/aslp.admin", + "co/aslp.admin", + "de/aslp.admin", + "fl/aslp.admin", + "ga/aslp.admin", + "id/aslp.admin", + "in/aslp.admin", + "ia/aslp.admin", + "ks/aslp.admin", + "ky/aslp.admin", + "la/aslp.admin", + "me/aslp.admin", + "md/aslp.admin", + "mn/aslp.admin", + "ms/aslp.admin", + "mo/aslp.admin", + "mt/aslp.admin", + "ne/aslp.admin", + "nh/aslp.admin", + "nc/aslp.admin", + "oh/aslp.admin", + "ok/aslp.admin", + "ri/aslp.admin", + "sc/aslp.admin", + "tn/aslp.admin", + "ut/aslp.admin", + "vt/aslp.admin", + "va/aslp.admin", + "vi/aslp.admin", + "wa/aslp.admin", + "wv/aslp.admin", + "wi/aslp.admin", + "wy/aslp.admin", + "octp/admin", + "al/octp.admin", + "ar/octp.admin", + "az/octp.admin", + "co/octp.admin", + "de/octp.admin", + "ga/octp.admin", + "ia/octp.admin", + "in/octp.admin", + "ky/octp.admin", + "la/octp.admin", + "me/octp.admin", + "md/octp.admin", + "mn/octp.admin", + "ms/octp.admin", + "mo/octp.admin", + "mt/octp.admin", + "ne/octp.admin", + "nh/octp.admin", + "nc/octp.admin", + "nd/octp.admin", + "oh/octp.admin", + "ri/octp.admin", + "sc/octp.admin", + "sd/octp.admin", + "tn/octp.admin", + "ut/octp.admin", + "vt/octp.admin", + "va/octp.admin", + "wa/octp.admin", + "wv/octp.admin", + "wi/octp.admin", + "wy/octp.admin", + "coun/admin", + "al/coun.admin", + "ar/coun.admin", + "az/coun.admin", + "co/coun.admin", + "ct/coun.admin", + "dc/coun.admin", + "de/coun.admin", + "fl/coun.admin", + "ga/coun.admin", + "ia/coun.admin", + "in/coun.admin", + "ks/coun.admin", + "ky/coun.admin", + "la/coun.admin", + "me/coun.admin", + "md/coun.admin", + "mn/coun.admin", + "ms/coun.admin", + "mo/coun.admin", + "mt/coun.admin", + "ne/coun.admin", + "nh/coun.admin", + "nj/coun.admin", + "nc/coun.admin", + "nd/coun.admin", + "oh/coun.admin", + "ok/coun.admin", + "ri/coun.admin", + "sc/coun.admin", + "sd/coun.admin", + "tn/coun.admin", + "ut/coun.admin", + "vt/coun.admin", + "va/coun.admin", + "wa/coun.admin", + "wv/coun.admin", + "wi/coun.admin", + "wy/coun.admin" + ] + } + ] } }, "/v1/flags/{flagId}/check": { @@ -2253,7 +3964,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/SandboLicenpl2UadOfjgNJ" + "$ref": "#/components/schemas/TestALicenanIFyyDEX5VN" } } }, @@ -2265,18 +3976,46 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/SandboLicen113ZVRfz1NW8" + "$ref": "#/components/schemas/TestALicen0lJK4nRHLlAP" } } } } } - }, - "options": { + } + }, + "/v1/provider-users/initiateRecovery": { + "post": { + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TestALicen6kP6aQ5vHZvs" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "200 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TestALicenEMkErOzoY9dd" + } + } + } + } + } + } + }, + "/v1/provider-users/me": { + "get": { "parameters": [ { - "name": "flagId", - "in": "path", + "name": "Authorization", + "in": "header", "required": true, "schema": { "type": "string" @@ -2284,140 +4023,160 @@ } ], "responses": { - "204": { - "description": "204 response", - "headers": { - "Access-Control-Allow-Origin": { - "schema": { - "type": "string" - } - }, - "Access-Control-Allow-Methods": { - "schema": { - "type": "string" - } - }, - "Vary": { - "schema": { - "type": "string" - } - }, - "Access-Control-Allow-Headers": { + "200": { + "description": "200 response", + "content": { + "application/json": { "schema": { - "type": "string" + "$ref": "#/components/schemas/TestALicenbtMyL0MqSVUK" } } - }, - "content": {} + } } - } + }, + "security": [ + { + "TestBackendPipelineStackTestAPIStackLicenseApiProviderUsersPoolAuthorizer246E3F74": [] + } + ] } }, - "/v1/compacts/{compact}/jurisdictions/{jurisdiction}/licenses": { - "options": { + "/v1/provider-users/me/email": { + "patch": { "parameters": [ { - "name": "compact", - "in": "path", + "name": "Authorization", + "in": "header", "required": true, "schema": { "type": "string" } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TestALicenWt4HR1PmbEfh" + } + } }, + "required": true + }, + "responses": { + "200": { + "description": "200 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TestALicenEMkErOzoY9dd" + } + } + } + } + }, + "security": [ { - "name": "jurisdiction", - "in": "path", + "TestBackendPipelineStackTestAPIStackLicenseApiProviderUsersPoolAuthorizer246E3F74": [] + } + ] + } + }, + "/v1/provider-users/me/email/verify": { + "post": { + "parameters": [ + { + "name": "Authorization", + "in": "header", "required": true, "schema": { "type": "string" } } ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TestALicenYI0jBXxNPcZZ" + } + } + }, + "required": true + }, "responses": { - "204": { - "description": "204 response", - "headers": { - "Access-Control-Allow-Origin": { - "schema": { - "type": "string" - } - }, - "Access-Control-Allow-Methods": { - "schema": { - "type": "string" - } - }, - "Vary": { - "schema": { - "type": "string" - } - }, - "Access-Control-Allow-Headers": { + "200": { + "description": "200 response", + "content": { + "application/json": { "schema": { - "type": "string" + "$ref": "#/components/schemas/TestALicenEMkErOzoY9dd" } } - }, - "content": {} + } } - } + }, + "security": [ + { + "TestBackendPipelineStackTestAPIStackLicenseApiProviderUsersPoolAuthorizer246E3F74": [] + } + ] } }, - "/v1/flags/{flagId}": { - "options": { + "/v1/provider-users/me/home-jurisdiction": { + "put": { "parameters": [ { - "name": "flagId", - "in": "path", + "name": "Authorization", + "in": "header", "required": true, "schema": { "type": "string" } } ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TestALicenzBTYlBMP0tPe" + } + } + }, + "required": true + }, "responses": { - "204": { - "description": "204 response", - "headers": { - "Access-Control-Allow-Origin": { - "schema": { - "type": "string" - } - }, - "Access-Control-Allow-Methods": { - "schema": { - "type": "string" - } - }, - "Vary": { - "schema": { - "type": "string" - } - }, - "Access-Control-Allow-Headers": { + "200": { + "description": "200 response", + "content": { + "application/json": { "schema": { - "type": "string" + "$ref": "#/components/schemas/TestALicenEMkErOzoY9dd" } } - }, - "content": {} + } } - } + }, + "security": [ + { + "TestBackendPipelineStackTestAPIStackLicenseApiProviderUsersPoolAuthorizer246E3F74": [] + } + ] } }, - "/v1/compacts/{compact}/providers/{providerId}/privileges/jurisdiction/{jurisdiction}": { - "options": { + "/v1/provider-users/me/jurisdiction/{jurisdiction}/licenseType/{licenseType}/history": { + "get": { "parameters": [ { - "name": "compact", - "in": "path", + "name": "Authorization", + "in": "header", "required": true, "schema": { "type": "string" } }, { - "name": "providerId", + "name": "jurisdiction", "in": "path", "required": true, "schema": { @@ -2425,7 +4184,7 @@ } }, { - "name": "jurisdiction", + "name": "licenseType", "in": "path", "required": true, "schema": { @@ -2434,36 +4193,25 @@ } ], "responses": { - "204": { - "description": "204 response", - "headers": { - "Access-Control-Allow-Origin": { - "schema": { - "type": "string" - } - }, - "Access-Control-Allow-Methods": { - "schema": { - "type": "string" - } - }, - "Vary": { - "schema": { - "type": "string" - } - }, - "Access-Control-Allow-Headers": { + "200": { + "description": "200 response", + "content": { + "application/json": { "schema": { - "type": "string" + "$ref": "#/components/schemas/TestALicenxX5qS7HBoRtp" } } - }, - "content": {} + } } - } + }, + "security": [ + { + "TestBackendPipelineStackTestAPIStackLicenseApiProviderUsersPoolAuthorizer246E3F74": [] + } + ] } }, - "/v1/compacts/{compact}/providers/{providerId}/privileges/jurisdiction/{jurisdiction}/licenseType/{licenseType}/encumbrance": { + "/v1/provider-users/me/military-affiliation": { "post": { "parameters": [ { @@ -2473,34 +4221,41 @@ "schema": { "type": "string" } - }, - { - "name": "compact", - "in": "path", - "required": true, - "schema": { - "type": "string" + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TestALicenWc4n78hZdCvx" + } } }, - { - "name": "providerId", - "in": "path", - "required": true, - "schema": { - "type": "string" + "required": true + }, + "responses": { + "200": { + "description": "200 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TestALicenE4fTNc2ubRUZ" + } + } } - }, + } + }, + "security": [ { - "name": "jurisdiction", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - }, + "TestBackendPipelineStackTestAPIStackLicenseApiProviderUsersPoolAuthorizer246E3F74": [] + } + ] + }, + "patch": { + "parameters": [ { - "name": "licenseType", - "in": "path", + "name": "Authorization", + "in": "header", "required": true, "schema": { "type": "string" @@ -2511,7 +4266,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/SandboLicenv4avK8ok4P45" + "$ref": "#/components/schemas/TestALicen9Wt8WsJp6OoB" } } }, @@ -2523,7 +4278,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/SandboLicenTMQQKAeKTKQR" + "$ref": "#/components/schemas/TestALicenEMkErOzoY9dd" } } } @@ -2531,71 +4286,68 @@ }, "security": [ { - "SandboxAPIStackLicenseApiStaffUsersPoolAuthorizer14A84A9B": [ - "aslp/admin", - "al/aslp.admin", - "ak/aslp.admin", - "ar/aslp.admin", - "co/aslp.admin", - "de/aslp.admin", - "ky/aslp.admin", - "la/aslp.admin", - "me/aslp.admin", - "md/aslp.admin", - "mn/aslp.admin", - "ms/aslp.admin", - "mo/aslp.admin", - "ne/aslp.admin", - "oh/aslp.admin", - "octp/admin", - "al/octp.admin", - "ar/octp.admin", - "ky/octp.admin", - "la/octp.admin", - "ms/octp.admin", - "ne/octp.admin", - "oh/octp.admin", - "coun/admin", - "al/coun.admin", - "ar/coun.admin", - "fl/coun.admin", - "ga/coun.admin", - "ky/coun.admin", - "ne/coun.admin", - "oh/coun.admin", - "ut/coun.admin" - ] + "TestBackendPipelineStackTestAPIStackLicenseApiProviderUsersPoolAuthorizer246E3F74": [] } ] - }, - "options": { - "parameters": [ - { - "name": "compact", - "in": "path", - "required": true, - "schema": { - "type": "string" + } + }, + "/v1/provider-users/registration": { + "post": { + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TestALicenp2MhBzi1qTM3" + } } }, - { - "name": "providerId", - "in": "path", - "required": true, - "schema": { - "type": "string" + "required": true + }, + "responses": { + "200": { + "description": "200 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TestALicenEMkErOzoY9dd" + } + } } - }, - { - "name": "jurisdiction", - "in": "path", - "required": true, - "schema": { - "type": "string" + } + } + } + }, + "/v1/provider-users/verifyRecovery": { + "post": { + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TestALicenynVI2HNp6D1V" + } } }, + "required": true + }, + "responses": { + "200": { + "description": "200 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TestALicenEMkErOzoY9dd" + } + } + } + } + } + } + }, + "/v1/public/compacts/{compact}/jurisdictions": { + "get": { + "parameters": [ { - "name": "licenseType", + "name": "compact", "in": "path", "required": true, "schema": { @@ -2604,37 +4356,21 @@ } ], "responses": { - "204": { - "description": "204 response", - "headers": { - "Access-Control-Allow-Origin": { - "schema": { - "type": "string" - } - }, - "Access-Control-Allow-Methods": { - "schema": { - "type": "string" - } - }, - "Vary": { - "schema": { - "type": "string" - } - }, - "Access-Control-Allow-Headers": { + "200": { + "description": "200 response", + "content": { + "application/json": { "schema": { - "type": "string" + "$ref": "#/components/schemas/TestALicenuoi86SrA9xKo" } } - }, - "content": {} + } } } } }, - "/v1/compacts/{compact}/providers/{providerId}/licenses/jurisdiction/{jurisdiction}/licenseType/{licenseType}/investigation/{investigationId}": { - "options": { + "/v1/public/compacts/{compact}/providers/query": { + "post": { "parameters": [ { "name": "compact", @@ -2643,25 +4379,37 @@ "schema": { "type": "string" } - }, - { - "name": "providerId", - "in": "path", - "required": true, - "schema": { - "type": "string" + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TestALicendW7jPEWN2hvi" + } } }, - { - "name": "jurisdiction", - "in": "path", - "required": true, - "schema": { - "type": "string" + "required": true + }, + "responses": { + "200": { + "description": "200 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TestALicenuRXWAw0laeRO" + } + } } - }, + } + } + } + }, + "/v1/public/compacts/{compact}/providers/{providerId}": { + "get": { + "parameters": [ { - "name": "licenseType", + "name": "compact", "in": "path", "required": true, "schema": { @@ -2669,7 +4417,7 @@ } }, { - "name": "investigationId", + "name": "providerId", "in": "path", "required": true, "schema": { @@ -2678,44 +4426,22 @@ } ], "responses": { - "204": { - "description": "204 response", - "headers": { - "Access-Control-Allow-Origin": { - "schema": { - "type": "string" - } - }, - "Access-Control-Allow-Methods": { - "schema": { - "type": "string" - } - }, - "Vary": { - "schema": { - "type": "string" - } - }, - "Access-Control-Allow-Headers": { + "200": { + "description": "200 response", + "content": { + "application/json": { "schema": { - "type": "string" + "$ref": "#/components/schemas/TestALicenIdbN1DIAV4zt" } } - }, - "content": {} + } } } - }, - "patch": { + } + }, + "/v1/public/compacts/{compact}/providers/{providerId}/jurisdiction/{jurisdiction}/licenseType/{licenseType}/history": { + "get": { "parameters": [ - { - "name": "Authorization", - "in": "header", - "required": true, - "schema": { - "type": "string" - } - }, { "name": "compact", "in": "path", @@ -2747,199 +4473,28 @@ "schema": { "type": "string" } - }, - { - "name": "investigationId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } } ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/SandboLicennuxBDueZ6Trv" - } - } - }, - "required": true - }, "responses": { "200": { "description": "200 response", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/SandboLicenTMQQKAeKTKQR" - } - } - } - } - }, - "security": [ - { - "SandboxAPIStackLicenseApiStaffUsersPoolAuthorizer14A84A9B": [ - "aslp/admin", - "al/aslp.admin", - "ak/aslp.admin", - "ar/aslp.admin", - "co/aslp.admin", - "de/aslp.admin", - "ky/aslp.admin", - "la/aslp.admin", - "me/aslp.admin", - "md/aslp.admin", - "mn/aslp.admin", - "ms/aslp.admin", - "mo/aslp.admin", - "ne/aslp.admin", - "oh/aslp.admin", - "octp/admin", - "al/octp.admin", - "ar/octp.admin", - "ky/octp.admin", - "la/octp.admin", - "ms/octp.admin", - "ne/octp.admin", - "oh/octp.admin", - "coun/admin", - "al/coun.admin", - "ar/coun.admin", - "fl/coun.admin", - "ga/coun.admin", - "ky/coun.admin", - "ne/coun.admin", - "oh/coun.admin", - "ut/coun.admin" - ] - } - ] - } - }, - "/": { - "options": { - "responses": { - "204": { - "description": "204 response", - "headers": { - "Access-Control-Allow-Origin": { - "schema": { - "type": "string" - } - }, - "Access-Control-Allow-Methods": { - "schema": { - "type": "string" - } - }, - "Vary": { - "schema": { - "type": "string" - } - }, - "Access-Control-Allow-Headers": { - "schema": { - "type": "string" + "$ref": "#/components/schemas/TestALicenxX5qS7HBoRtp" } } - }, - "content": {} - } - } - } - }, - "/v1/compacts/{compact}/providers/{providerId}/licenses/jurisdiction/{jurisdiction}": { - "options": { - "parameters": [ - { - "name": "compact", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - }, - { - "name": "providerId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - }, - { - "name": "jurisdiction", - "in": "path", - "required": true, - "schema": { - "type": "string" } } - ], - "responses": { - "204": { - "description": "204 response", - "headers": { - "Access-Control-Allow-Origin": { - "schema": { - "type": "string" - } - }, - "Access-Control-Allow-Methods": { - "schema": { - "type": "string" - } - }, - "Vary": { - "schema": { - "type": "string" - } - }, - "Access-Control-Allow-Headers": { - "schema": { - "type": "string" - } - } - }, - "content": {} - } } } }, - "/v1/public/compacts/{compact}/providers/{providerId}/jurisdiction/{jurisdiction}/licenseType/{licenseType}/history": { + "/v1/public/jurisdictions/live": { "get": { "parameters": [ { "name": "compact", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - }, - { - "name": "providerId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - }, - { - "name": "jurisdiction", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - }, - { - "name": "licenseType", - "in": "path", - "required": true, + "in": "query", "schema": { "type": "string" } @@ -2951,79 +4506,15 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/SandboLicen0XcLtKpj7p28" + "$ref": "#/components/schemas/TestALicenJ1nyIpzJ1crX" } } } } } - }, - "options": { - "parameters": [ - { - "name": "compact", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - }, - { - "name": "providerId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - }, - { - "name": "jurisdiction", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - }, - { - "name": "licenseType", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "204": { - "description": "204 response", - "headers": { - "Access-Control-Allow-Origin": { - "schema": { - "type": "string" - } - }, - "Access-Control-Allow-Methods": { - "schema": { - "type": "string" - } - }, - "Vary": { - "schema": { - "type": "string" - } - }, - "Access-Control-Allow-Headers": { - "schema": { - "type": "string" - } - } - }, - "content": {} - } - } } }, - "/v1/compacts/{compact}/providers/{providerId}/privileges/jurisdiction/{jurisdiction}/licenseType/{licenseType}/deactivate": { + "/v1/purchases/privileges": { "post": { "parameters": [ { @@ -3033,45 +4524,13 @@ "schema": { "type": "string" } - }, - { - "name": "compact", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - }, - { - "name": "providerId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - }, - { - "name": "jurisdiction", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - }, - { - "name": "licenseType", - "in": "path", - "required": true, - "schema": { - "type": "string" - } } ], "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/SandboLicenG4CMW3C4eZNN" + "$ref": "#/components/schemas/TestALicenpBPN4v10TUZV" } } }, @@ -3083,7 +4542,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/SandboLicenTMQQKAeKTKQR" + "$ref": "#/components/schemas/TestALicenv7ltYQFiTkrz" } } } @@ -3091,72 +4550,17 @@ }, "security": [ { - "SandboxAPIStackLicenseApiStaffUsersPoolAuthorizer14A84A9B": [ - "aslp/admin", - "al/aslp.admin", - "ak/aslp.admin", - "ar/aslp.admin", - "co/aslp.admin", - "de/aslp.admin", - "ky/aslp.admin", - "la/aslp.admin", - "me/aslp.admin", - "md/aslp.admin", - "mn/aslp.admin", - "ms/aslp.admin", - "mo/aslp.admin", - "ne/aslp.admin", - "oh/aslp.admin", - "octp/admin", - "al/octp.admin", - "ar/octp.admin", - "ky/octp.admin", - "la/octp.admin", - "ms/octp.admin", - "ne/octp.admin", - "oh/octp.admin", - "coun/admin", - "al/coun.admin", - "ar/coun.admin", - "fl/coun.admin", - "ga/coun.admin", - "ky/coun.admin", - "ne/coun.admin", - "oh/coun.admin", - "ut/coun.admin" - ] + "TestBackendPipelineStackTestAPIStackLicenseApiProviderUsersPoolAuthorizer246E3F74": [] } ] - }, - "options": { + } + }, + "/v1/purchases/privileges/options": { + "get": { "parameters": [ { - "name": "compact", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - }, - { - "name": "providerId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - }, - { - "name": "jurisdiction", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - }, - { - "name": "licenseType", - "in": "path", + "name": "Authorization", + "in": "header", "required": true, "schema": { "type": "string" @@ -3164,160 +4568,98 @@ } ], "responses": { - "204": { - "description": "204 response", - "headers": { - "Access-Control-Allow-Origin": { - "schema": { - "type": "string" - } - }, - "Access-Control-Allow-Methods": { - "schema": { - "type": "string" - } - }, - "Vary": { - "schema": { - "type": "string" - } - }, - "Access-Control-Allow-Headers": { + "200": { + "description": "200 response", + "content": { + "application/json": { "schema": { - "type": "string" + "$ref": "#/components/schemas/TestALicenVzpuR2smrSJF" } } - }, - "content": {} + } } - } + }, + "security": [ + { + "TestBackendPipelineStackTestAPIStackLicenseApiProviderUsersPoolAuthorizer246E3F74": [] + } + ] } }, - "/v1/provider-users": { - "options": { + "/v1/staff-users/me": { + "get": { "responses": { - "204": { - "description": "204 response", - "headers": { - "Access-Control-Allow-Origin": { - "schema": { - "type": "string" - } - }, - "Access-Control-Allow-Methods": { - "schema": { - "type": "string" - } - }, - "Vary": { - "schema": { - "type": "string" - } - }, - "Access-Control-Allow-Headers": { + "404": { + "description": "404 response", + "content": { + "application/json": { "schema": { - "type": "string" + "$ref": "#/components/schemas/TestALicenEMkErOzoY9dd" } } - }, - "content": {} - } - } - } - }, - "/v1/compacts/{compact}/providers/{providerId}/militaryAudit": { - "options": { - "parameters": [ - { - "name": "compact", - "in": "path", - "required": true, - "schema": { - "type": "string" } }, - { - "name": "providerId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "204": { - "description": "204 response", + "200": { + "description": "200 response", "headers": { "Access-Control-Allow-Origin": { "schema": { "type": "string" } - }, - "Access-Control-Allow-Methods": { - "schema": { - "type": "string" - } - }, - "Vary": { - "schema": { - "type": "string" - } - }, - "Access-Control-Allow-Headers": { + } + }, + "content": { + "application/json": { "schema": { - "type": "string" + "$ref": "#/components/schemas/TestALicenY0wRQnCr1lHl" } } - }, - "content": {} - } - } - }, - "patch": { - "parameters": [ - { - "name": "Authorization", - "in": "header", - "required": true, - "schema": { - "type": "string" - } - }, - { - "name": "compact", - "in": "path", - "required": true, - "schema": { - "type": "string" } - }, + } + }, + "security": [ { - "name": "providerId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } + "TestBackendPipelineStackTestAPIStackLicenseApiStaffUsersPoolAuthorizer084A18F1": [ + "profile" + ] } - ], + ] + }, + "patch": { "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/SandboLicenZnCDqNvEXOu2" + "$ref": "#/components/schemas/TestALicenEI4gvKnGhPzF" } } }, "required": true }, "responses": { + "404": { + "description": "404 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TestALicenEMkErOzoY9dd" + } + } + } + }, "200": { "description": "200 response", + "headers": { + "Access-Control-Allow-Origin": { + "schema": { + "type": "string" + } + } + }, "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/SandboLicenTMQQKAeKTKQR" + "$ref": "#/components/schemas/TestALicenY0wRQnCr1lHl" } } } @@ -3325,6213 +4667,2332 @@ }, "security": [ { - "SandboxAPIStackLicenseApiStaffUsersPoolAuthorizer14A84A9B": [ - "aslp/admin", - "al/aslp.admin", - "ak/aslp.admin", - "ar/aslp.admin", - "co/aslp.admin", - "de/aslp.admin", - "ky/aslp.admin", - "la/aslp.admin", - "me/aslp.admin", - "md/aslp.admin", - "mn/aslp.admin", - "ms/aslp.admin", - "mo/aslp.admin", - "ne/aslp.admin", - "oh/aslp.admin", - "octp/admin", - "al/octp.admin", - "ar/octp.admin", - "ky/octp.admin", - "la/octp.admin", - "ms/octp.admin", - "ne/octp.admin", - "oh/octp.admin", - "coun/admin", - "al/coun.admin", - "ar/coun.admin", - "fl/coun.admin", - "ga/coun.admin", - "ky/coun.admin", - "ne/coun.admin", - "oh/coun.admin", - "ut/coun.admin" + "TestBackendPipelineStackTestAPIStackLicenseApiStaffUsersPoolAuthorizer084A18F1": [ + "profile" ] } ] } - }, - "/v1/provider-users/me/jurisdiction/{jurisdiction}": { - "options": { - "parameters": [ - { - "name": "jurisdiction", - "in": "path", - "required": true, - "schema": { - "type": "string" - } + } + }, + "components": { + "schemas": { + "TestALicenFQqmK0F4LBsK": { + "type": "object", + "properties": {} + }, + "TestALicenVGKXMT4yfZHA": { + "required": [ + "deactivationNote" + ], + "type": "object", + "properties": { + "deactivationNote": { + "maxLength": 256, + "type": "string", + "description": "Note describing why the privilege is being deactivated" } + }, + "additionalProperties": false + }, + "TestALicenoxYwJpjtTvcP": { + "required": [ + "effectiveLiftDate" ], - "responses": { - "204": { - "description": "204 response", - "headers": { - "Access-Control-Allow-Origin": { - "schema": { - "type": "string" - } - }, - "Access-Control-Allow-Methods": { - "schema": { - "type": "string" - } - }, - "Vary": { - "schema": { - "type": "string" - } - }, - "Access-Control-Allow-Headers": { - "schema": { - "type": "string" - } - } - }, - "content": {} - } - } - } - }, - "/v1/compacts/{compact}/providers/{providerId}/privileges/jurisdiction/{jurisdiction}/licenseType/{licenseType}/investigation/{investigationId}": { - "options": { - "parameters": [ - { - "name": "compact", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - }, - { - "name": "providerId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - }, - { - "name": "jurisdiction", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - }, - { - "name": "licenseType", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - }, - { - "name": "investigationId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } + "type": "object", + "properties": { + "effectiveLiftDate": { + "pattern": "^[12]{1}[0-9]{3}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$", + "type": "string", + "description": "The effective date when the encumbrance will be lifted", + "format": "date" } + }, + "additionalProperties": false + }, + "TestALicen0lJK4nRHLlAP": { + "required": [ + "enabled" ], - "responses": { - "204": { - "description": "204 response", - "headers": { - "Access-Control-Allow-Origin": { - "schema": { - "type": "string" - } - }, - "Access-Control-Allow-Methods": { - "schema": { - "type": "string" - } - }, - "Vary": { - "schema": { - "type": "string" - } - }, - "Access-Control-Allow-Headers": { - "schema": { - "type": "string" - } - } - }, - "content": {} + "type": "object", + "properties": { + "enabled": { + "type": "boolean", + "description": "Whether the feature flag is enabled" } } }, - "patch": { - "parameters": [ - { - "name": "Authorization", - "in": "header", - "required": true, - "schema": { - "type": "string" - } - }, - { - "name": "compact", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - }, - { - "name": "providerId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - }, - { - "name": "jurisdiction", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - }, - { - "name": "licenseType", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - }, - { - "name": "investigationId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } + "TestALicenJ1nyIpzJ1crX": { + "type": "object", + "additionalProperties": { + "type": "array", + "items": { + "type": "string", + "enum": [ + "al", + "ak", + "az", + "ar", + "ca", + "co", + "ct", + "de", + "dc", + "fl", + "ga", + "hi", + "id", + "il", + "in", + "ia", + "ks", + "ky", + "la", + "me", + "md", + "ma", + "mi", + "mn", + "ms", + "mo", + "mt", + "ne", + "nv", + "nh", + "nj", + "nm", + "ny", + "nc", + "nd", + "oh", + "ok", + "or", + "pa", + "pr", + "ri", + "sc", + "sd", + "tn", + "tx", + "ut", + "vt", + "va", + "vi", + "wa", + "wv", + "wi", + "wy" + ] } + } + }, + "TestALicen9Wt8WsJp6OoB": { + "required": [ + "status" ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/SandboLicenUONaWXXjz4K6" - } - } - }, - "required": true - }, - "responses": { - "200": { - "description": "200 response", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/SandboLicenTMQQKAeKTKQR" - } - } - } + "type": "object", + "properties": { + "status": { + "type": "string", + "description": "The status to set the military affiliation to.", + "enum": [ + "inactive" + ] } }, - "security": [ - { - "SandboxAPIStackLicenseApiStaffUsersPoolAuthorizer14A84A9B": [ - "aslp/admin", - "al/aslp.admin", - "ak/aslp.admin", - "ar/aslp.admin", - "co/aslp.admin", - "de/aslp.admin", - "ky/aslp.admin", - "la/aslp.admin", - "me/aslp.admin", - "md/aslp.admin", - "mn/aslp.admin", - "ms/aslp.admin", - "mo/aslp.admin", - "ne/aslp.admin", - "oh/aslp.admin", - "octp/admin", - "al/octp.admin", - "ar/octp.admin", - "ky/octp.admin", - "la/octp.admin", - "ms/octp.admin", - "ne/octp.admin", - "oh/octp.admin", - "coun/admin", - "al/coun.admin", - "ar/coun.admin", - "fl/coun.admin", - "ga/coun.admin", - "ky/coun.admin", - "ne/coun.admin", - "oh/coun.admin", - "ut/coun.admin" - ] - } - ] - } - }, - "/v1/compacts/{compact}/staff-users/{userId}/reinvite": { - "post": { - "parameters": [ - { - "name": "compact", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - }, - { - "name": "userId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } + "additionalProperties": false + }, + "TestALicenvD3siA7fe7r2": { + "required": [ + "ssn" ], - "responses": { - "404": { - "description": "404 response", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/SandboLicenTMQQKAeKTKQR" - } - } - } - }, - "200": { - "description": "200 response", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/SandboLicenTMQQKAeKTKQR" - } - } - } - } - }, - "security": [ - { - "SandboxAPIStackLicenseApiStaffUsersPoolAuthorizer14A84A9B": [ - "aslp/admin", - "al/aslp.admin", - "ak/aslp.admin", - "ar/aslp.admin", - "co/aslp.admin", - "de/aslp.admin", - "ky/aslp.admin", - "la/aslp.admin", - "me/aslp.admin", - "md/aslp.admin", - "mn/aslp.admin", - "ms/aslp.admin", - "mo/aslp.admin", - "ne/aslp.admin", - "oh/aslp.admin", - "octp/admin", - "al/octp.admin", - "ar/octp.admin", - "ky/octp.admin", - "la/octp.admin", - "ms/octp.admin", - "ne/octp.admin", - "oh/octp.admin", - "coun/admin", - "al/coun.admin", - "ar/coun.admin", - "fl/coun.admin", - "ga/coun.admin", - "ky/coun.admin", - "ne/coun.admin", - "oh/coun.admin", - "ut/coun.admin" - ] + "type": "object", + "properties": { + "ssn": { + "pattern": "^[0-9]{3}-[0-9]{2}-[0-9]{4}$", + "type": "string", + "description": "The provider's social security number" } - ] + } }, - "options": { - "parameters": [ - { - "name": "compact", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - }, - { - "name": "userId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } + "TestALicendW7jPEWN2hvi": { + "required": [ + "query" ], - "responses": { - "204": { - "description": "204 response", - "headers": { - "Access-Control-Allow-Origin": { - "schema": { - "type": "string" - } + "type": "object", + "properties": { + "pagination": { + "type": "object", + "properties": { + "lastKey": { + "maxLength": 1024, + "minLength": 1, + "type": "string" }, - "Access-Control-Allow-Methods": { - "schema": { - "type": "string" - } + "pageSize": { + "maximum": 100, + "minimum": 5, + "type": "integer" + } + }, + "additionalProperties": false + }, + "query": { + "type": "object", + "properties": { + "providerId": { + "pattern": "[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab]{1}[0-9a-f]{3}-[0-9a-f]{12}", + "type": "string", + "description": "Internal UUID for the provider" }, - "Vary": { - "schema": { - "type": "string" - } + "jurisdiction": { + "type": "string", + "description": "Filter for providers with privilege/license in a jurisdiction", + "enum": [ + "al", + "ak", + "az", + "ar", + "ca", + "co", + "ct", + "de", + "dc", + "fl", + "ga", + "hi", + "id", + "il", + "in", + "ia", + "ks", + "ky", + "la", + "me", + "md", + "ma", + "mi", + "mn", + "ms", + "mo", + "mt", + "ne", + "nv", + "nh", + "nj", + "nm", + "ny", + "nc", + "nd", + "oh", + "ok", + "or", + "pa", + "pr", + "ri", + "sc", + "sd", + "tn", + "tx", + "ut", + "vt", + "va", + "vi", + "wa", + "wv", + "wi", + "wy" + ] }, - "Access-Control-Allow-Headers": { - "schema": { - "type": "string" - } + "givenName": { + "maxLength": 100, + "type": "string", + "description": "Filter for providers with a given name (familyName is required if givenName is provided)" + }, + "familyName": { + "maxLength": 100, + "type": "string", + "description": "Filter for providers with a family name" } }, - "content": {} - } - } - } - }, - "/v1/compacts/{compact}/staff-users": { - "get": { - "parameters": [ - { - "name": "compact", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "200 response", - "headers": { - "Access-Control-Allow-Origin": { - "schema": { - "type": "string" - } - } - }, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/SandboLicenxXSBED7FZUQN" - } - } - } - } - }, - "security": [ - { - "SandboxAPIStackLicenseApiStaffUsersPoolAuthorizer14A84A9B": [ - "aslp/admin", - "al/aslp.admin", - "ak/aslp.admin", - "ar/aslp.admin", - "co/aslp.admin", - "de/aslp.admin", - "ky/aslp.admin", - "la/aslp.admin", - "me/aslp.admin", - "md/aslp.admin", - "mn/aslp.admin", - "ms/aslp.admin", - "mo/aslp.admin", - "ne/aslp.admin", - "oh/aslp.admin", - "octp/admin", - "al/octp.admin", - "ar/octp.admin", - "ky/octp.admin", - "la/octp.admin", - "ms/octp.admin", - "ne/octp.admin", - "oh/octp.admin", - "coun/admin", - "al/coun.admin", - "ar/coun.admin", - "fl/coun.admin", - "ga/coun.admin", - "ky/coun.admin", - "ne/coun.admin", - "oh/coun.admin", - "ut/coun.admin" - ] - } - ] - }, - "post": { - "parameters": [ - { - "name": "compact", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/SandboLicenPfWQpg9qdCvm" - } - } + "additionalProperties": false, + "description": "The query parameters" }, - "required": true - }, - "responses": { - "200": { - "description": "200 response", - "headers": { - "Access-Control-Allow-Origin": { - "schema": { - "type": "string" - } + "sorting": { + "required": [ + "key" + ], + "type": "object", + "properties": { + "key": { + "type": "string", + "description": "The key to sort results by", + "enum": [ + "dateOfUpdate", + "familyName" + ] + }, + "direction": { + "type": "string", + "description": "Direction to sort results by", + "enum": [ + "ascending", + "descending" + ] } }, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/SandboLicen2qPPtuQWh8hv" - } - } - } + "description": "How to sort results" } }, - "security": [ - { - "SandboxAPIStackLicenseApiStaffUsersPoolAuthorizer14A84A9B": [ - "aslp/admin", - "al/aslp.admin", - "ak/aslp.admin", - "ar/aslp.admin", - "co/aslp.admin", - "de/aslp.admin", - "ky/aslp.admin", - "la/aslp.admin", - "me/aslp.admin", - "md/aslp.admin", - "mn/aslp.admin", - "ms/aslp.admin", - "mo/aslp.admin", - "ne/aslp.admin", - "oh/aslp.admin", - "octp/admin", - "al/octp.admin", - "ar/octp.admin", - "ky/octp.admin", - "la/octp.admin", - "ms/octp.admin", - "ne/octp.admin", - "oh/octp.admin", - "coun/admin", - "al/coun.admin", - "ar/coun.admin", - "fl/coun.admin", - "ga/coun.admin", - "ky/coun.admin", - "ne/coun.admin", - "oh/coun.admin", - "ut/coun.admin" - ] - } - ] + "additionalProperties": false }, - "options": { - "parameters": [ - { - "name": "compact", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } + "TestALicenuRXWAw0laeRO": { + "required": [ + "pagination", + "providers" ], - "responses": { - "204": { - "description": "204 response", - "headers": { - "Access-Control-Allow-Origin": { - "schema": { - "type": "string" - } - }, - "Access-Control-Allow-Methods": { - "schema": { - "type": "string" - } - }, - "Vary": { - "schema": { - "type": "string" - } - }, - "Access-Control-Allow-Headers": { - "schema": { - "type": "string" - } - } - }, - "content": {} - } - } - } - }, - "/v1/public/compacts": { - "options": { - "responses": { - "204": { - "description": "204 response", - "headers": { - "Access-Control-Allow-Origin": { - "schema": { - "type": "string" - } - }, - "Access-Control-Allow-Methods": { - "schema": { - "type": "string" - } - }, - "Vary": { - "schema": { - "type": "string" - } - }, - "Access-Control-Allow-Headers": { - "schema": { - "type": "string" - } - } - }, - "content": {} - } - } - } - }, - "/v1/staff-users": { - "options": { - "responses": { - "204": { - "description": "204 response", - "headers": { - "Access-Control-Allow-Origin": { - "schema": { - "type": "string" - } - }, - "Access-Control-Allow-Methods": { - "schema": { - "type": "string" - } + "type": "object", + "properties": { + "pagination": { + "type": "object", + "properties": { + "prevLastKey": { + "maxLength": 1024, + "minLength": 1, + "type": "object" }, - "Vary": { - "schema": { - "type": "string" - } + "lastKey": { + "maxLength": 1024, + "minLength": 1, + "type": "object" }, - "Access-Control-Allow-Headers": { - "schema": { - "type": "string" - } - } - }, - "content": {} - } - } - } - }, - "/v1/staff-users/me": { - "get": { - "responses": { - "404": { - "description": "404 response", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/SandboLicenTMQQKAeKTKQR" - } + "pageSize": { + "maximum": 100, + "minimum": 5, + "type": "integer" } } }, - "200": { - "description": "200 response", - "headers": { - "Access-Control-Allow-Origin": { - "schema": { - "type": "string" - } - } - }, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/SandboLicen2qPPtuQWh8hv" - } - } - } - } - }, - "security": [ - { - "SandboxAPIStackLicenseApiStaffUsersPoolAuthorizer14A84A9B": [ - "profile" - ] - } - ] - }, - "options": { - "responses": { - "204": { - "description": "204 response", - "headers": { - "Access-Control-Allow-Origin": { - "schema": { - "type": "string" - } + "query": { + "type": "object", + "properties": { + "providerId": { + "pattern": "[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab]{1}[0-9a-f]{3}-[0-9a-f]{12}", + "type": "string", + "description": "Internal UUID for the provider" }, - "Access-Control-Allow-Methods": { - "schema": { - "type": "string" - } + "jurisdiction": { + "type": "string", + "description": "Filter for providers with privilege/license in a jurisdiction", + "enum": [ + "al", + "ak", + "az", + "ar", + "ca", + "co", + "ct", + "de", + "dc", + "fl", + "ga", + "hi", + "id", + "il", + "in", + "ia", + "ks", + "ky", + "la", + "me", + "md", + "ma", + "mi", + "mn", + "ms", + "mo", + "mt", + "ne", + "nv", + "nh", + "nj", + "nm", + "ny", + "nc", + "nd", + "oh", + "ok", + "or", + "pa", + "pr", + "ri", + "sc", + "sd", + "tn", + "tx", + "ut", + "vt", + "va", + "vi", + "wa", + "wv", + "wi", + "wy" + ] }, - "Vary": { - "schema": { - "type": "string" - } + "givenName": { + "maxLength": 100, + "type": "string", + "description": "Filter for providers with a given name" }, - "Access-Control-Allow-Headers": { - "schema": { - "type": "string" - } - } - }, - "content": {} - } - } - }, - "patch": { - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/SandboLicendz0gMqUAh7qN" - } - } - }, - "required": true - }, - "responses": { - "404": { - "description": "404 response", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/SandboLicenTMQQKAeKTKQR" - } - } - } - }, - "200": { - "description": "200 response", - "headers": { - "Access-Control-Allow-Origin": { - "schema": { - "type": "string" - } - } - }, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/SandboLicen2qPPtuQWh8hv" - } + "familyName": { + "maxLength": 100, + "type": "string", + "description": "Filter for providers with a family name" } } - } - }, - "security": [ - { - "SandboxAPIStackLicenseApiStaffUsersPoolAuthorizer14A84A9B": [ - "profile" - ] - } - ] - } - }, - "/v1/public/compacts/{compact}/providers/{providerId}/jurisdiction": { - "options": { - "parameters": [ - { - "name": "compact", - "in": "path", - "required": true, - "schema": { - "type": "string" - } }, - { - "name": "providerId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "204": { - "description": "204 response", - "headers": { - "Access-Control-Allow-Origin": { - "schema": { - "type": "string" - } - }, - "Access-Control-Allow-Methods": { - "schema": { - "type": "string" - } - }, - "Vary": { - "schema": { - "type": "string" - } + "sorting": { + "required": [ + "key" + ], + "type": "object", + "properties": { + "key": { + "type": "string", + "description": "The key to sort results by", + "enum": [ + "dateOfUpdate", + "familyName" + ] }, - "Access-Control-Allow-Headers": { - "schema": { - "type": "string" - } + "direction": { + "type": "string", + "description": "Direction to sort results by", + "enum": [ + "ascending", + "descending" + ] } }, - "content": {} - } - } - } - }, - "/v1/compacts/{compact}/staff-users/{userId}": { - "get": { - "parameters": [ - { - "name": "compact", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - }, - { - "name": "userId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "404": { - "description": "404 response", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/SandboLicenTMQQKAeKTKQR" - } - } - } + "description": "How to sort results" }, - "200": { - "description": "200 response", - "headers": { - "Access-Control-Allow-Origin": { - "schema": { - "type": "string" - } - } - }, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/SandboLicen2qPPtuQWh8hv" - } - } - } - } - }, - "security": [ - { - "SandboxAPIStackLicenseApiStaffUsersPoolAuthorizer14A84A9B": [ - "aslp/admin", - "al/aslp.admin", - "ak/aslp.admin", - "ar/aslp.admin", - "co/aslp.admin", - "de/aslp.admin", - "ky/aslp.admin", - "la/aslp.admin", - "me/aslp.admin", - "md/aslp.admin", - "mn/aslp.admin", - "ms/aslp.admin", - "mo/aslp.admin", - "ne/aslp.admin", - "oh/aslp.admin", - "octp/admin", - "al/octp.admin", - "ar/octp.admin", - "ky/octp.admin", - "la/octp.admin", - "ms/octp.admin", - "ne/octp.admin", - "oh/octp.admin", - "coun/admin", - "al/coun.admin", - "ar/coun.admin", - "fl/coun.admin", - "ga/coun.admin", - "ky/coun.admin", - "ne/coun.admin", - "oh/coun.admin", - "ut/coun.admin" - ] - } - ] - }, - "delete": { - "parameters": [ - { - "name": "compact", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - }, - { - "name": "userId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "404": { - "description": "404 response", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/SandboLicenTMQQKAeKTKQR" - } - } - } - }, - "200": { - "description": "200 response", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/SandboLicenTMQQKAeKTKQR" - } - } - } - } - }, - "security": [ - { - "SandboxAPIStackLicenseApiStaffUsersPoolAuthorizer14A84A9B": [ - "aslp/admin", - "al/aslp.admin", - "ak/aslp.admin", - "ar/aslp.admin", - "co/aslp.admin", - "de/aslp.admin", - "ky/aslp.admin", - "la/aslp.admin", - "me/aslp.admin", - "md/aslp.admin", - "mn/aslp.admin", - "ms/aslp.admin", - "mo/aslp.admin", - "ne/aslp.admin", - "oh/aslp.admin", - "octp/admin", - "al/octp.admin", - "ar/octp.admin", - "ky/octp.admin", - "la/octp.admin", - "ms/octp.admin", - "ne/octp.admin", - "oh/octp.admin", - "coun/admin", - "al/coun.admin", - "ar/coun.admin", - "fl/coun.admin", - "ga/coun.admin", - "ky/coun.admin", - "ne/coun.admin", - "oh/coun.admin", - "ut/coun.admin" - ] - } - ] - }, - "options": { - "parameters": [ - { - "name": "compact", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - }, - { - "name": "userId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "204": { - "description": "204 response", - "headers": { - "Access-Control-Allow-Origin": { - "schema": { + "providers": { + "maxLength": 100, + "type": "array", + "items": { + "required": [ + "compact", + "familyName", + "givenName", + "licenseJurisdiction", + "privilegeJurisdictions", + "providerId", + "type" + ], + "type": "object", + "properties": { + "licenseJurisdiction": { + "type": "string", + "enum": [ + "al", + "ak", + "az", + "ar", + "ca", + "co", + "ct", + "de", + "dc", + "fl", + "ga", + "hi", + "id", + "il", + "in", + "ia", + "ks", + "ky", + "la", + "me", + "md", + "ma", + "mi", + "mn", + "ms", + "mo", + "mt", + "ne", + "nv", + "nh", + "nj", + "nm", + "ny", + "nc", + "nd", + "oh", + "ok", + "or", + "pa", + "pr", + "ri", + "sc", + "sd", + "tn", + "tx", + "ut", + "vt", + "va", + "vi", + "wa", + "wv", + "wi", + "wy" + ] + }, + "compact": { + "type": "string", + "enum": [ + "aslp", + "octp", + "coun" + ] + }, + "providerId": { + "pattern": "[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab]{1}[0-9a-f]{3}-[0-9a-f]{12}", "type": "string" - } - }, - "Access-Control-Allow-Methods": { - "schema": { + }, + "npi": { + "pattern": "^[0-9]{10}$", "type": "string" - } - }, - "Vary": { - "schema": { + }, + "givenName": { + "maxLength": 100, + "minLength": 1, "type": "string" - } - }, - "Access-Control-Allow-Headers": { - "schema": { + }, + "familyName": { + "maxLength": 100, + "minLength": 1, "type": "string" - } - } - }, - "content": {} - } - } - }, - "patch": { - "parameters": [ - { - "name": "compact", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - }, - { - "name": "userId", - "in": "path", - "required": true, - "schema": { - "type": "string" + }, + "middleName": { + "maxLength": 100, + "minLength": 1, + "type": "string" + }, + "privilegeJurisdictions": { + "type": "array", + "items": { + "type": "string", + "enum": [ + "al", + "ak", + "az", + "ar", + "ca", + "co", + "ct", + "de", + "dc", + "fl", + "ga", + "hi", + "id", + "il", + "in", + "ia", + "ks", + "ky", + "la", + "me", + "md", + "ma", + "mi", + "mn", + "ms", + "mo", + "mt", + "ne", + "nv", + "nh", + "nj", + "nm", + "ny", + "nc", + "nd", + "oh", + "ok", + "or", + "pa", + "pr", + "ri", + "sc", + "sd", + "tn", + "tx", + "ut", + "vt", + "va", + "vi", + "wa", + "wv", + "wi", + "wy" + ] + } + }, + "type": { + "type": "string", + "enum": [ + "provider" + ] + }, + "suffix": { + "maxLength": 100, + "minLength": 1, + "type": "string" + }, + "currentHomeJurisdiction": { + "type": "string", + "description": "The current jurisdiction postal abbreviation if known.", + "enum": [ + "al", + "ak", + "az", + "ar", + "ca", + "co", + "ct", + "de", + "dc", + "fl", + "ga", + "hi", + "id", + "il", + "in", + "ia", + "ks", + "ky", + "la", + "me", + "md", + "ma", + "mi", + "mn", + "ms", + "mo", + "mt", + "ne", + "nv", + "nh", + "nj", + "nm", + "ny", + "nc", + "nd", + "oh", + "ok", + "or", + "pa", + "pr", + "ri", + "sc", + "sd", + "tn", + "tx", + "ut", + "vt", + "va", + "vi", + "wa", + "wv", + "wi", + "wy", + "other", + "unknown" + ] + }, + "dateOfUpdate": { + "type": "string", + "format": "date-time" + } + } } } + } + }, + "TestALicenXybIHf2p94sK": { + "required": [ + "compact", + "jurisdictionAdverseActionsNotificationEmails", + "jurisdictionName", + "jurisdictionOperationsTeamEmails", + "jurisdictionSummaryReportNotificationEmails", + "jurisprudenceRequirements", + "licenseeRegistrationEnabled", + "postalAbbreviation", + "privilegeFees" ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/SandboLicengqvhPQz7ywKX" + "type": "object", + "properties": { + "privilegeFees": { + "type": "array", + "description": "The fees for the privileges by license type", + "items": { + "required": [ + "amount", + "licenseTypeAbbreviation" + ], + "type": "object", + "properties": { + "amount": { + "type": "number" + }, + "militaryRate": { + "description": "Optional military rate for the privilege fee.", + "oneOf": [ + { + "minimum": 0, + "type": "number" + }, + null + ] + }, + "licenseTypeAbbreviation": { + "type": "string", + "enum": [ + "aud", + "slp", + "ot", + "ota", + "lpc" + ] + } } } }, - "required": true - }, - "responses": { - "404": { - "description": "404 response", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/SandboLicenTMQQKAeKTKQR" - } - } + "postalAbbreviation": { + "type": "string", + "description": "The postal abbreviation of the jurisdiction" + }, + "jurisdictionAdverseActionsNotificationEmails": { + "type": "array", + "description": "List of email addresses for adverse actions notifications", + "items": { + "type": "string", + "format": "email" } }, - "200": { - "description": "200 response", - "headers": { - "Access-Control-Allow-Origin": { - "schema": { - "type": "string" - } - } - }, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/SandboLicen2qPPtuQWh8hv" - } - } + "jurisdictionOperationsTeamEmails": { + "type": "array", + "description": "List of email addresses for operations team notifications", + "items": { + "type": "string", + "format": "email" } - } - }, - "security": [ - { - "SandboxAPIStackLicenseApiStaffUsersPoolAuthorizer14A84A9B": [ - "aslp/admin", - "al/aslp.admin", - "ak/aslp.admin", - "ar/aslp.admin", - "co/aslp.admin", - "de/aslp.admin", - "ky/aslp.admin", - "la/aslp.admin", - "me/aslp.admin", - "md/aslp.admin", - "mn/aslp.admin", - "ms/aslp.admin", - "mo/aslp.admin", - "ne/aslp.admin", - "oh/aslp.admin", - "octp/admin", - "al/octp.admin", - "ar/octp.admin", - "ky/octp.admin", - "la/octp.admin", - "ms/octp.admin", - "ne/octp.admin", - "oh/octp.admin", - "coun/admin", - "al/coun.admin", - "ar/coun.admin", - "fl/coun.admin", - "ga/coun.admin", - "ky/coun.admin", - "ne/coun.admin", - "oh/coun.admin", - "ut/coun.admin" + }, + "compact": { + "type": "string", + "description": "The compact this jurisdiction configuration belongs to", + "enum": [ + "aslp", + "octp", + "coun" ] - } - ] - } - }, - "/v1/compacts/{compact}/jurisdictions/{jurisdiction}": { - "get": { - "parameters": [ - { - "name": "Authorization", - "in": "header", - "required": true, - "schema": { - "type": "string" - } }, - { - "name": "compact", - "in": "path", - "required": true, - "schema": { - "type": "string" + "jurisprudenceRequirements": { + "required": [ + "required" + ], + "type": "object", + "properties": { + "linkToDocumentation": { + "description": "Optional link to jurisprudence documentation", + "oneOf": [ + { + "type": "string" + }, + null + ] + }, + "required": { + "type": "boolean", + "description": "Whether jurisprudence requirements exist" + } } }, - { - "name": "jurisdiction", - "in": "path", - "required": true, - "schema": { - "type": "string" + "licenseeRegistrationEnabled": { + "type": "boolean", + "description": "Denotes whether licensee registration is enabled" + }, + "jurisdictionName": { + "type": "string", + "description": "The name of the jurisdiction" + }, + "jurisdictionSummaryReportNotificationEmails": { + "type": "array", + "description": "List of email addresses for summary report notifications", + "items": { + "type": "string", + "format": "email" } } + } + }, + "TestALicenAPZgnted7k9a": { + "required": [ + "militaryStatus" ], - "responses": { - "200": { - "description": "200 response", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/SandboLiceniWnENMKPoAG6" - } - } - } - } - }, - "security": [ - { - "SandboxAPIStackLicenseApiStaffUsersPoolAuthorizer14A84A9B": [ - "aslp/readGeneral", - "octp/readGeneral", - "coun/readGeneral" + "type": "object", + "properties": { + "militaryStatusNote": { + "maxLength": 5000, + "type": "string", + "description": "Optional note from the admin (typically for declines)" + }, + "militaryStatus": { + "type": "string", + "description": "The audit result for the military documentation", + "enum": [ + "approved", + "declined" ] } - ] + }, + "additionalProperties": false }, - "put": { - "parameters": [ - { - "name": "Authorization", - "in": "header", - "required": true, - "schema": { - "type": "string" + "TestALicenrrveP2TCwpbO": { + "required": [ + "jurisdictionAdverseActionsNotificationEmails", + "jurisdictionOperationsTeamEmails", + "jurisdictionSummaryReportNotificationEmails", + "jurisprudenceRequirements", + "licenseeRegistrationEnabled", + "privilegeFees" + ], + "type": "object", + "properties": { + "privilegeFees": { + "type": "array", + "description": "The fees for the privileges by license type", + "items": { + "required": [ + "amount", + "licenseTypeAbbreviation" + ], + "type": "object", + "properties": { + "amount": { + "minimum": 0, + "type": "number" + }, + "militaryRate": { + "description": "Optional military rate for the privilege fee.", + "oneOf": [ + { + "minimum": 0, + "type": "number" + }, + null + ] + }, + "licenseTypeAbbreviation": { + "type": "string", + "enum": [ + "aud", + "slp", + "ot", + "ota", + "lpc" + ] + } + }, + "additionalProperties": false } }, - { - "name": "compact", - "in": "path", - "required": true, - "schema": { - "type": "string" + "jurisdictionAdverseActionsNotificationEmails": { + "maxItems": 10, + "minItems": 1, + "uniqueItems": true, + "type": "array", + "description": "List of email addresses for adverse actions notifications", + "items": { + "type": "string", + "format": "email" } }, - { - "name": "jurisdiction", - "in": "path", - "required": true, - "schema": { - "type": "string" + "jurisdictionOperationsTeamEmails": { + "maxItems": 10, + "minItems": 1, + "uniqueItems": true, + "type": "array", + "description": "List of email addresses for operations team notifications", + "items": { + "type": "string", + "format": "email" } - } - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/SandboLicenJwiQTQPNiltz" + }, + "jurisprudenceRequirements": { + "required": [ + "required" + ], + "type": "object", + "properties": { + "linkToDocumentation": { + "description": "Optional link to jurisprudence documentation", + "oneOf": [ + { + "type": "string" + }, + null + ] + }, + "required": { + "type": "boolean", + "description": "Whether jurisprudence requirements exist" } - } + }, + "additionalProperties": false }, - "required": true + "licenseeRegistrationEnabled": { + "type": "boolean", + "description": "Denotes whether licensee registration is enabled" + }, + "jurisdictionSummaryReportNotificationEmails": { + "maxItems": 10, + "minItems": 1, + "uniqueItems": true, + "type": "array", + "description": "List of email addresses for summary report notifications", + "items": { + "type": "string", + "format": "email" + } + } }, - "responses": { - "200": { - "description": "200 response", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/SandboLicenTMQQKAeKTKQR" - } - } - } - } - }, - "security": [ - { - "SandboxAPIStackLicenseApiStaffUsersPoolAuthorizer14A84A9B": [ - "aslp/admin", - "al/aslp.admin", - "ak/aslp.admin", - "ar/aslp.admin", - "co/aslp.admin", - "de/aslp.admin", - "ky/aslp.admin", - "la/aslp.admin", - "me/aslp.admin", - "md/aslp.admin", - "mn/aslp.admin", - "ms/aslp.admin", - "mo/aslp.admin", - "ne/aslp.admin", - "oh/aslp.admin", - "octp/admin", - "al/octp.admin", - "ar/octp.admin", - "ky/octp.admin", - "la/octp.admin", - "ms/octp.admin", - "ne/octp.admin", - "oh/octp.admin", - "coun/admin", - "al/coun.admin", - "ar/coun.admin", - "fl/coun.admin", - "ga/coun.admin", - "ky/coun.admin", - "ne/coun.admin", - "oh/coun.admin", - "ut/coun.admin" - ] - } - ] + "additionalProperties": false }, - "options": { - "parameters": [ - { - "name": "compact", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - }, - { - "name": "jurisdiction", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "204": { - "description": "204 response", - "headers": { - "Access-Control-Allow-Origin": { - "schema": { - "type": "string" - } - }, - "Access-Control-Allow-Methods": { - "schema": { - "type": "string" - } - }, - "Vary": { - "schema": { - "type": "string" - } - }, - "Access-Control-Allow-Headers": { - "schema": { - "type": "string" - } - } - }, - "content": {} - } - } - } - }, - "/v1/purchases": { - "options": { - "responses": { - "204": { - "description": "204 response", - "headers": { - "Access-Control-Allow-Origin": { - "schema": { - "type": "string" - } - }, - "Access-Control-Allow-Methods": { - "schema": { - "type": "string" - } - }, - "Vary": { - "schema": { - "type": "string" - } - }, - "Access-Control-Allow-Headers": { - "schema": { - "type": "string" - } - } - }, - "content": {} - } - } - } - }, - "/v1/public/compacts/{compact}/providers": { - "options": { - "parameters": [ - { - "name": "compact", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } + "TestALicenVzpuR2smrSJF": { + "required": [ + "items", + "pagination" ], - "responses": { - "204": { - "description": "204 response", - "headers": { - "Access-Control-Allow-Origin": { - "schema": { - "type": "string" - } - }, - "Access-Control-Allow-Methods": { - "schema": { - "type": "string" - } - }, - "Vary": { - "schema": { - "type": "string" - } - }, - "Access-Control-Allow-Headers": { - "schema": { - "type": "string" - } - } - }, - "content": {} - } - } - } - }, - "/v1/provider-users/me/email": { - "options": { - "responses": { - "204": { - "description": "204 response", - "headers": { - "Access-Control-Allow-Origin": { - "schema": { - "type": "string" - } - }, - "Access-Control-Allow-Methods": { - "schema": { - "type": "string" - } + "type": "object", + "properties": { + "pagination": { + "type": "object", + "properties": { + "prevLastKey": { + "maxLength": 1024, + "minLength": 1, + "type": "object" }, - "Vary": { - "schema": { - "type": "string" - } + "lastKey": { + "maxLength": 1024, + "minLength": 1, + "type": "object" }, - "Access-Control-Allow-Headers": { - "schema": { - "type": "string" - } - } - }, - "content": {} - } - } - }, - "patch": { - "parameters": [ - { - "name": "Authorization", - "in": "header", - "required": true, - "schema": { - "type": "string" - } - } - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/SandboLicenudbEF4n02FXU" + "pageSize": { + "maximum": 100, + "minimum": 5, + "type": "integer" } } }, - "required": true - }, - "responses": { - "200": { - "description": "200 response", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/SandboLicenTMQQKAeKTKQR" - } - } - } - } - }, - "security": [ - { - "SandboxAPIStackLicenseApiProviderUsersPoolAuthorizerEB7523BA": [] - } - ] - } - }, - "/v1/compacts/{compact}/credentials": { - "options": { - "parameters": [ - { - "name": "compact", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "204": { - "description": "204 response", - "headers": { - "Access-Control-Allow-Origin": { - "schema": { - "type": "string" - } - }, - "Access-Control-Allow-Methods": { - "schema": { - "type": "string" - } - }, - "Vary": { - "schema": { - "type": "string" - } - }, - "Access-Control-Allow-Headers": { - "schema": { - "type": "string" - } - } - }, - "content": {} - } - } - } - }, - "/v1/provider-users/me/jurisdiction": { - "options": { - "responses": { - "204": { - "description": "204 response", - "headers": { - "Access-Control-Allow-Origin": { - "schema": { - "type": "string" - } - }, - "Access-Control-Allow-Methods": { - "schema": { - "type": "string" - } - }, - "Vary": { - "schema": { - "type": "string" - } - }, - "Access-Control-Allow-Headers": { - "schema": { - "type": "string" + "items": { + "maxLength": 100, + "type": "array", + "items": { + "type": "object", + "oneOf": [ + { + "required": [ + "compactAbbr", + "compactCommissionFee", + "compactName", + "isSandbox", + "paymentProcessorPublicFields", + "transactionFeeConfiguration", + "type" + ], + "type": "object", + "properties": { + "compactCommissionFee": { + "required": [ + "feeAmount", + "feeType" + ], + "type": "object", + "properties": { + "feeAmount": { + "type": "number" + }, + "feeType": { + "type": "string", + "enum": [ + "FLAT_RATE" + ] + } + } + }, + "compactAbbr": { + "type": "string", + "description": "The abbreviation of the compact" + }, + "paymentProcessorPublicFields": { + "required": [ + "apiLoginId", + "publicClientKey" + ], + "type": "object", + "properties": { + "publicClientKey": { + "type": "string", + "description": "The public client key for the payment processor" + }, + "apiLoginId": { + "type": "string", + "description": "The API login ID for the payment processor" + } + } + }, + "type": { + "type": "string", + "enum": [ + "compact" + ] + }, + "transactionFeeConfiguration": { + "required": [ + "licenseeCharges" + ], + "type": "object", + "properties": { + "licenseeCharges": { + "required": [ + "active", + "chargeAmount", + "chargeType" + ], + "type": "object", + "properties": { + "chargeType": { + "type": "string", + "description": "The type of transaction fee charge", + "enum": [ + "FLAT_FEE_PER_PRIVILEGE" + ] + }, + "active": { + "type": "boolean", + "description": "Whether the compact is charging licensees transaction fees" + }, + "chargeAmount": { + "type": "number", + "description": "The amount to charge per privilege purchased" + } + } + } + } + }, + "isSandbox": { + "type": "boolean", + "description": "Whether the compact is in sandbox mode" + }, + "compactName": { + "type": "string", + "description": "The full name of the compact" + } + } + }, + { + "required": [ + "jurisdictionName", + "jurisprudenceRequirements", + "postalAbbreviation", + "privilegeFees", + "type" + ], + "type": "object", + "properties": { + "privilegeFees": { + "type": "array", + "description": "The fees for the privileges", + "items": { + "required": [ + "amount", + "licenseTypeAbbreviation" + ], + "type": "object", + "properties": { + "amount": { + "type": "number" + }, + "militaryRate": { + "description": "Optional military rate for the privilege fee.", + "oneOf": [ + { + "minimum": 0, + "type": "number" + }, + null + ] + }, + "licenseTypeAbbreviation": { + "type": "string" + } + } + } + }, + "postalAbbreviation": { + "type": "string", + "description": "The postal abbreviation of the jurisdiction" + }, + "jurisprudenceRequirements": { + "required": [ + "required" + ], + "type": "object", + "properties": { + "linkToDocumentation": { + "description": "Optional link to jurisprudence documentation", + "oneOf": [ + { + "type": "string" + }, + null + ] + }, + "required": { + "type": "boolean", + "description": "Whether jurisprudence requirements exist" + } + } + }, + "jurisdictionName": { + "type": "string", + "description": "The name of the jurisdiction" + }, + "type": { + "type": "string", + "enum": [ + "jurisdiction" + ] + } + } } - } - }, - "content": {} - } - } - } - }, - "/v1/compacts/{compact}/jurisdictions": { - "get": { - "parameters": [ - { - "name": "Authorization", - "in": "header", - "required": true, - "schema": { - "type": "string" - } - }, - { - "name": "compact", - "in": "path", - "required": true, - "schema": { - "type": "string" + ] } } + } + }, + "TestALicenfZAZJXjiwCtn": { + "required": [ + "upload" ], - "responses": { - "200": { - "description": "200 response", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/SandboLicenLnkOp52kvwLg" + "type": "object", + "properties": { + "upload": { + "required": [ + "fields", + "url" + ], + "type": "object", + "properties": { + "fields": { + "type": "object", + "additionalProperties": { + "type": "string" } + }, + "url": { + "type": "string" } } } - }, - "security": [ - { - "SandboxAPIStackLicenseApiStaffUsersPoolAuthorizer14A84A9B": [ - "aslp/readGeneral", - "octp/readGeneral", - "coun/readGeneral" - ] - } - ] + } }, - "options": { - "parameters": [ - { - "name": "compact", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } + "TestALicenxX5qS7HBoRtp": { + "required": [ + "compact", + "events", + "jurisdiction", + "licenseType", + "privilegeId", + "providerId" ], - "responses": { - "204": { - "description": "204 response", - "headers": { - "Access-Control-Allow-Origin": { - "schema": { - "type": "string" - } - }, - "Access-Control-Allow-Methods": { - "schema": { - "type": "string" - } - }, - "Vary": { - "schema": { - "type": "string" - } - }, - "Access-Control-Allow-Headers": { - "schema": { + "type": "object", + "properties": { + "licenseType": { + "type": "string", + "enum": [ + "audiologist", + "speech-language pathologist", + "occupational therapist", + "occupational therapy assistant", + "licensed professional counselor" + ] + }, + "compact": { + "type": "string", + "enum": [ + "aslp", + "octp", + "coun" + ] + }, + "privilegeId": { + "type": "string" + }, + "providerId": { + "pattern": "[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab]{1}[0-9a-f]{3}-[0-9a-f]{12}", + "type": "string" + }, + "jurisdiction": { + "type": "string", + "enum": [ + "al", + "ak", + "az", + "ar", + "ca", + "co", + "ct", + "de", + "dc", + "fl", + "ga", + "hi", + "id", + "il", + "in", + "ia", + "ks", + "ky", + "la", + "me", + "md", + "ma", + "mi", + "mn", + "ms", + "mo", + "mt", + "ne", + "nv", + "nh", + "nj", + "nm", + "ny", + "nc", + "nd", + "oh", + "ok", + "or", + "pa", + "pr", + "ri", + "sc", + "sd", + "tn", + "tx", + "ut", + "vt", + "va", + "vi", + "wa", + "wv", + "wi", + "wy" + ] + }, + "events": { + "type": "array", + "items": { + "required": [ + "createDate", + "dateOfUpdate", + "effectiveDate", + "type", + "updateType" + ], + "type": "object", + "properties": { + "note": { "type": "string" + }, + "npdbCategories": { + "type": "array", + "description": "The categories of clinical privilege action for encumbrance events", + "items": { + "type": "string" + } + }, + "type": { + "type": "string", + "enum": [ + "privilegeUpdate" + ] + }, + "dateOfUpdate": { + "type": "string", + "format": "date-time" + }, + "effectiveDate": { + "pattern": "^[12]{1}[0-9]{3}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$", + "type": "string", + "format": "date" + }, + "updateType": { + "type": "string", + "enum": [ + "deactivation", + "expiration", + "issuance", + "other", + "renewal", + "encumbrance", + "homeJurisdictionChange", + "registration", + "lifting_encumbrance", + "licenseDeactivation", + "emailChange" + ] + }, + "createDate": { + "type": "string", + "format": "date-time" } } - }, - "content": {} - } - } - } - }, - "/v1/compacts/{compact}": { - "get": { - "parameters": [ - { - "name": "Authorization", - "in": "header", - "required": true, - "schema": { - "type": "string" - } - }, - { - "name": "compact", - "in": "path", - "required": true, - "schema": { - "type": "string" } } + } + }, + "TestALicenQaU7r1ltReBC": { + "required": [ + "apiLoginId", + "processor", + "transactionKey" ], - "responses": { - "200": { - "description": "200 response", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/SandboLicenRy0ye4gsVdf9" - } - } - } - } - }, - "security": [ - { - "SandboxAPIStackLicenseApiStaffUsersPoolAuthorizer14A84A9B": [ - "aslp/readGeneral", - "octp/readGeneral", - "coun/readGeneral" + "type": "object", + "properties": { + "apiLoginId": { + "maxLength": 100, + "minLength": 1, + "type": "string", + "description": "The api login id for the payment processor" + }, + "transactionKey": { + "maxLength": 100, + "minLength": 1, + "type": "string", + "description": "The transaction key for the payment processor" + }, + "processor": { + "type": "string", + "description": "The type of payment processor", + "enum": [ + "authorize.net" ] } - ] + }, + "additionalProperties": false }, - "put": { - "parameters": [ - { - "name": "Authorization", - "in": "header", - "required": true, - "schema": { - "type": "string" - } - }, - { - "name": "compact", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/SandboLicenZx6a73xGIfzu" + "TestALicenpBPN4v10TUZV": { + "required": [ + "attestations", + "licenseType", + "orderInformation", + "selectedJurisdictions" + ], + "type": "object", + "properties": { + "licenseType": { + "type": "string", + "description": "The type of license the provider is purchasing a privilege for.", + "enum": [ + "audiologist", + "speech-language pathologist", + "occupational therapist", + "occupational therapy assistant", + "licensed professional counselor" + ] + }, + "attestations": { + "type": "array", + "description": "List of attestations that the user has agreed to", + "items": { + "required": [ + "attestationId", + "version" + ], + "type": "object", + "properties": { + "attestationId": { + "maxLength": 100, + "type": "string", + "description": "The ID of the attestation" + }, + "version": { + "maxLength": 10, + "pattern": "^\\d+$", + "type": "string", + "description": "The version of the attestation" + } } } }, - "required": true - }, - "responses": { - "200": { - "description": "200 response", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/SandboLicenTMQQKAeKTKQR" + "orderInformation": { + "required": [ + "opaqueData" + ], + "type": "object", + "properties": { + "opaqueData": { + "required": [ + "dataDescriptor", + "dataValue" + ], + "type": "object", + "properties": { + "dataValue": { + "maxLength": 1000, + "type": "string", + "description": "The opaque data value token returned by Authorize.Net Accept UI" + }, + "dataDescriptor": { + "maxLength": 100, + "type": "string", + "description": "The opaque data descriptor returned by Authorize.Net Accept UI" + } } } } - } - }, - "security": [ - { - "SandboxAPIStackLicenseApiStaffUsersPoolAuthorizer14A84A9B": [ - "aslp/admin", - "al/aslp.admin", - "ak/aslp.admin", - "ar/aslp.admin", - "co/aslp.admin", - "de/aslp.admin", - "ky/aslp.admin", - "la/aslp.admin", - "me/aslp.admin", - "md/aslp.admin", - "mn/aslp.admin", - "ms/aslp.admin", - "mo/aslp.admin", - "ne/aslp.admin", - "oh/aslp.admin", - "octp/admin", - "al/octp.admin", - "ar/octp.admin", - "ky/octp.admin", - "la/octp.admin", - "ms/octp.admin", - "ne/octp.admin", - "oh/octp.admin", - "coun/admin", - "al/coun.admin", - "ar/coun.admin", - "fl/coun.admin", - "ga/coun.admin", - "ky/coun.admin", - "ne/coun.admin", - "oh/coun.admin", - "ut/coun.admin" - ] - } - ] - }, - "options": { - "parameters": [ - { - "name": "compact", - "in": "path", - "required": true, - "schema": { - "type": "string" + }, + "selectedJurisdictions": { + "maxLength": 20, + "type": "array", + "items": { + "type": "string", + "description": "Jurisdictions a provider has selected to purchase privileges in.", + "enum": [ + "al", + "ak", + "az", + "ar", + "ca", + "co", + "ct", + "de", + "dc", + "fl", + "ga", + "hi", + "id", + "il", + "in", + "ia", + "ks", + "ky", + "la", + "me", + "md", + "ma", + "mi", + "mn", + "ms", + "mo", + "mt", + "ne", + "nv", + "nh", + "nj", + "nm", + "ny", + "nc", + "nd", + "oh", + "ok", + "or", + "pa", + "pr", + "ri", + "sc", + "sd", + "tn", + "tx", + "ut", + "vt", + "va", + "vi", + "wa", + "wv", + "wi", + "wy" + ] } } + } + }, + "TestALicenPVQzJn8rvFIW": { + "required": [ + "effectiveLiftDate" ], - "responses": { - "204": { - "description": "204 response", - "headers": { - "Access-Control-Allow-Origin": { - "schema": { - "type": "string" - } - }, - "Access-Control-Allow-Methods": { - "schema": { - "type": "string" - } - }, - "Vary": { - "schema": { - "type": "string" - } - }, - "Access-Control-Allow-Headers": { - "schema": { - "type": "string" - } - } - }, - "content": {} + "type": "object", + "properties": { + "effectiveLiftDate": { + "pattern": "^[12]{1}[0-9]{3}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$", + "type": "string", + "description": "The effective date when the encumbrance will be lifted", + "format": "date" } - } - } - }, - "/v1/flags": { - "options": { - "responses": { - "204": { - "description": "204 response", - "headers": { - "Access-Control-Allow-Origin": { - "schema": { - "type": "string" - } - }, - "Access-Control-Allow-Methods": { - "schema": { - "type": "string" - } - }, - "Vary": { - "schema": { - "type": "string" - } - }, - "Access-Control-Allow-Headers": { - "schema": { - "type": "string" - } - } + }, + "additionalProperties": false + }, + "TestALicenuoi86SrA9xKo": { + "type": "array", + "items": { + "required": [ + "compact", + "jurisdictionName", + "postalAbbreviation" + ], + "type": "object", + "properties": { + "postalAbbreviation": { + "type": "string", + "description": "The postal abbreviation of the jurisdiction" + }, + "compact": { + "type": "string" }, - "content": {} + "jurisdictionName": { + "type": "string", + "description": "The name of the jurisdiction" + } } } - } - }, - "/v1/compacts": { - "options": { - "responses": { - "204": { - "description": "204 response", - "headers": { - "Access-Control-Allow-Origin": { - "schema": { - "type": "string" - } - }, - "Access-Control-Allow-Methods": { - "schema": { - "type": "string" - } - }, - "Vary": { - "schema": { - "type": "string" + }, + "TestALicenOPOzFOrHHN9b": { + "type": "object", + "properties": { + "permissions": { + "type": "object", + "additionalProperties": { + "type": "object", + "properties": { + "actions": { + "type": "object", + "properties": { + "readPrivate": { + "type": "boolean" + }, + "admin": { + "type": "boolean" + }, + "readSSN": { + "type": "boolean" + } + } + }, + "jurisdictions": { + "type": "object", + "additionalProperties": { + "type": "object", + "properties": { + "actions": { + "type": "object", + "properties": { + "readPrivate": { + "type": "boolean" + }, + "admin": { + "type": "boolean" + }, + "write": { + "type": "boolean" + }, + "readSSN": { + "type": "boolean" + } + }, + "additionalProperties": false + } + } + } } }, - "Access-Control-Allow-Headers": { - "schema": { - "type": "string" - } - } - }, - "content": {} - } - } - } - }, - "/v1/provider-users/me/jurisdiction/{jurisdiction}/licenseType/{licenseType}": { - "options": { - "parameters": [ - { - "name": "jurisdiction", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - }, - { - "name": "licenseType", - "in": "path", - "required": true, - "schema": { - "type": "string" + "additionalProperties": false } } + }, + "additionalProperties": false + }, + "TestALicenuCn4gplRNmBC": { + "required": [ + "attributes", + "permissions" ], - "responses": { - "204": { - "description": "204 response", - "headers": { - "Access-Control-Allow-Origin": { - "schema": { - "type": "string" - } - }, - "Access-Control-Allow-Methods": { - "schema": { - "type": "string" - } - }, - "Vary": { - "schema": { - "type": "string" + "type": "object", + "properties": { + "permissions": { + "type": "object", + "additionalProperties": { + "type": "object", + "properties": { + "actions": { + "type": "object", + "properties": { + "readPrivate": { + "type": "boolean" + }, + "admin": { + "type": "boolean" + }, + "readSSN": { + "type": "boolean" + } + } + }, + "jurisdictions": { + "type": "object", + "additionalProperties": { + "type": "object", + "properties": { + "actions": { + "type": "object", + "properties": { + "readPrivate": { + "type": "boolean" + }, + "admin": { + "type": "boolean" + }, + "write": { + "type": "boolean" + }, + "readSSN": { + "type": "boolean" + } + }, + "additionalProperties": false + } + } + } } }, - "Access-Control-Allow-Headers": { - "schema": { - "type": "string" - } - } - }, - "content": {} - } - } - } - }, - "/v1/compacts/{compact}/providers/{providerId}/licenses/jurisdiction": { - "options": { - "parameters": [ - { - "name": "compact", - "in": "path", - "required": true, - "schema": { - "type": "string" + "additionalProperties": false } }, - { - "name": "providerId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "204": { - "description": "204 response", - "headers": { - "Access-Control-Allow-Origin": { - "schema": { - "type": "string" - } - }, - "Access-Control-Allow-Methods": { - "schema": { - "type": "string" - } + "attributes": { + "required": [ + "email", + "familyName", + "givenName" + ], + "type": "object", + "properties": { + "givenName": { + "maxLength": 100, + "minLength": 1, + "type": "string" }, - "Vary": { - "schema": { - "type": "string" - } + "familyName": { + "maxLength": 100, + "minLength": 1, + "type": "string" }, - "Access-Control-Allow-Headers": { - "schema": { - "type": "string" - } + "email": { + "maxLength": 100, + "minLength": 5, + "type": "string" } }, - "content": {} + "additionalProperties": false } - } - } - }, - "/v1/provider-users/registration": { - "post": { - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/SandboLicenmftfBe6vPEA8" - } - } - }, - "required": true }, - "responses": { - "200": { - "description": "200 response", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/SandboLicenTMQQKAeKTKQR" - } - } - } + "additionalProperties": false + }, + "TestALicenv7ltYQFiTkrz": { + "required": [ + "transactionId" + ], + "type": "object", + "properties": { + "message": { + "type": "string", + "description": "A message about the transaction" + }, + "transactionId": { + "type": "string", + "description": "The transaction id for the purchase" } } }, - "options": { - "responses": { - "204": { - "description": "204 response", - "headers": { - "Access-Control-Allow-Origin": { - "schema": { - "type": "string" - } - }, - "Access-Control-Allow-Methods": { - "schema": { - "type": "string" - } - }, - "Vary": { - "schema": { - "type": "string" + "TestALicenP6vvlXwvuH66": { + "required": [ + "compactAbbr", + "compactAdverseActionsNotificationEmails", + "compactCommissionFee", + "compactName", + "compactOperationsTeamEmails", + "compactSummaryReportNotificationEmails", + "configuredStates", + "licenseeRegistrationEnabled" + ], + "type": "object", + "properties": { + "configuredStates": { + "type": "array", + "description": "List of states that have submitted configurations and their live status", + "items": { + "required": [ + "isLive", + "postalAbbreviation" + ], + "type": "object", + "properties": { + "postalAbbreviation": { + "type": "string", + "description": "The postal abbreviation of the jurisdiction", + "enum": [ + "al", + "ak", + "az", + "ar", + "ca", + "co", + "ct", + "de", + "dc", + "fl", + "ga", + "hi", + "id", + "il", + "in", + "ia", + "ks", + "ky", + "la", + "me", + "md", + "ma", + "mi", + "mn", + "ms", + "mo", + "mt", + "ne", + "nv", + "nh", + "nj", + "nm", + "ny", + "nc", + "nd", + "oh", + "ok", + "or", + "pa", + "pr", + "ri", + "sc", + "sd", + "tn", + "tx", + "ut", + "vt", + "va", + "vi", + "wa", + "wv", + "wi", + "wy" + ] + }, + "isLive": { + "type": "boolean", + "description": "Whether the state is live and available for registrations." } + } + } + }, + "compactCommissionFee": { + "required": [ + "feeAmount", + "feeType" + ], + "type": "object", + "properties": { + "feeAmount": { + "type": "number" }, - "Access-Control-Allow-Headers": { - "schema": { - "type": "string" - } + "feeType": { + "type": "string", + "enum": [ + "FLAT_RATE" + ] } - }, - "content": {} - } - } - } - }, - "/v1/public/compacts/{compact}/providers/{providerId}/jurisdiction/{jurisdiction}/licenseType": { - "options": { - "parameters": [ - { - "name": "compact", - "in": "path", - "required": true, - "schema": { - "type": "string" } }, - { - "name": "providerId", - "in": "path", - "required": true, - "schema": { - "type": "string" + "compactSummaryReportNotificationEmails": { + "type": "array", + "description": "List of email addresses for summary report notifications", + "items": { + "type": "string", + "format": "email" } }, - { - "name": "jurisdiction", - "in": "path", - "required": true, - "schema": { - "type": "string" + "compactAdverseActionsNotificationEmails": { + "type": "array", + "description": "List of email addresses for adverse actions notifications", + "items": { + "type": "string", + "format": "email" } - } - ], - "responses": { - "204": { - "description": "204 response", - "headers": { - "Access-Control-Allow-Origin": { - "schema": { - "type": "string" - } - }, - "Access-Control-Allow-Methods": { - "schema": { - "type": "string" - } - }, - "Vary": { - "schema": { - "type": "string" - } - }, - "Access-Control-Allow-Headers": { - "schema": { - "type": "string" + }, + "licenseeRegistrationEnabled": { + "type": "boolean", + "description": "Denotes whether licensee registration is enabled" + }, + "compactAbbr": { + "type": "string", + "description": "The abbreviation of the compact" + }, + "transactionFeeConfiguration": { + "type": "object", + "properties": { + "licenseeCharges": { + "required": [ + "active", + "chargeAmount", + "chargeType" + ], + "type": "object", + "properties": { + "chargeType": { + "type": "string", + "description": "The type of transaction fee charge", + "enum": [ + "FLAT_FEE_PER_PRIVILEGE" + ] + }, + "active": { + "type": "boolean", + "description": "Whether the compact is charging licensees transaction fees" + }, + "chargeAmount": { + "type": "number", + "description": "The amount to charge per privilege purchased" + } } } - }, - "content": {} - } - } - } - }, - "/v1/public/compacts/{compact}/providers/{providerId}/jurisdiction/{jurisdiction}": { - "options": { - "parameters": [ - { - "name": "compact", - "in": "path", - "required": true, - "schema": { - "type": "string" } }, - { - "name": "providerId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } + "compactName": { + "type": "string", + "description": "The full name of the compact" }, - { - "name": "jurisdiction", - "in": "path", - "required": true, - "schema": { - "type": "string" + "compactOperationsTeamEmails": { + "type": "array", + "description": "List of email addresses for operations team notifications", + "items": { + "type": "string", + "format": "email" } } + } + }, + "TestALicen1SjCp0NSNbsm": { + "type": "object", + "properties": {} + }, + "TestALicenYbbH6xLqpmhB": { + "required": [ + "message" ], - "responses": { - "204": { - "description": "204 response", - "headers": { - "Access-Control-Allow-Origin": { - "schema": { - "type": "string" - } - }, - "Access-Control-Allow-Methods": { - "schema": { - "type": "string" - } - }, - "Vary": { - "schema": { - "type": "string" - } - }, - "Access-Control-Allow-Headers": { - "schema": { - "type": "string" - } - } - }, - "content": {} + "type": "object", + "properties": { + "message": { + "type": "string", + "description": "A message about the request" } } - } - }, - "/v1/compacts/{compact}/providers/{providerId}/licenses/jurisdiction/{jurisdiction}/licenseType/{licenseType}/encumbrance": { - "post": { - "parameters": [ - { - "name": "Authorization", - "in": "header", - "required": true, - "schema": { - "type": "string" - } - }, - { - "name": "compact", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - }, - { - "name": "providerId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - }, - { - "name": "jurisdiction", - "in": "path", - "required": true, - "schema": { - "type": "string" - } + }, + "TestALicentD9lPRRh4cxz": { + "required": [ + "clinicalPrivilegeActionCategories", + "encumbranceEffectiveDate", + "encumbranceType" + ], + "type": "object", + "properties": { + "encumbranceEffectiveDate": { + "pattern": "^[12]{1}[0-9]{3}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$", + "type": "string", + "description": "The effective date of the encumbrance", + "format": "date" }, - { - "name": "licenseType", - "in": "path", - "required": true, - "schema": { + "clinicalPrivilegeActionCategories": { + "type": "array", + "description": "The categories of clinical privilege action", + "items": { "type": "string" } - } - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/SandboLicenmKXS1L8tLsGF" - } - } }, - "required": true - }, - "responses": { - "200": { - "description": "200 response", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/SandboLicenTMQQKAeKTKQR" - } - } - } - } - }, - "security": [ - { - "SandboxAPIStackLicenseApiStaffUsersPoolAuthorizer14A84A9B": [ - "aslp/admin", - "al/aslp.admin", - "ak/aslp.admin", - "ar/aslp.admin", - "co/aslp.admin", - "de/aslp.admin", - "ky/aslp.admin", - "la/aslp.admin", - "me/aslp.admin", - "md/aslp.admin", - "mn/aslp.admin", - "ms/aslp.admin", - "mo/aslp.admin", - "ne/aslp.admin", - "oh/aslp.admin", - "octp/admin", - "al/octp.admin", - "ar/octp.admin", - "ky/octp.admin", - "la/octp.admin", - "ms/octp.admin", - "ne/octp.admin", - "oh/octp.admin", - "coun/admin", - "al/coun.admin", - "ar/coun.admin", - "fl/coun.admin", - "ga/coun.admin", - "ky/coun.admin", - "ne/coun.admin", - "oh/coun.admin", - "ut/coun.admin" + "encumbranceType": { + "type": "string", + "description": "The type of encumbrance", + "enum": [ + "fine", + "reprimand", + "required supervision", + "completion of continuing education", + "public reprimand", + "probation", + "injunctive action", + "suspension", + "revocation", + "denial", + "surrender of license", + "modification of previous action-extension", + "modification of previous action-reduction", + "other monitoring", + "other adjudicated action not listed" ] } - ] + }, + "additionalProperties": false, + "description": "Encumbrance data to create" }, - "options": { - "parameters": [ - { - "name": "compact", - "in": "path", - "required": true, - "schema": { - "type": "string" - } + "TestALicenynVI2HNp6D1V": { + "required": [ + "compact", + "providerId", + "recaptchaToken", + "recoveryToken" + ], + "type": "object", + "properties": { + "compact": { + "type": "string", + "description": "Compact abbreviation", + "enum": [ + "aslp", + "octp", + "coun" + ] }, - { - "name": "providerId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } + "providerId": { + "pattern": "[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab]{1}[0-9a-f]{3}-[0-9a-f]{12}", + "type": "string", + "description": "Provider UUID" }, - { - "name": "jurisdiction", - "in": "path", - "required": true, - "schema": { - "type": "string" - } + "recaptchaToken": { + "minLength": 1, + "type": "string", + "description": "ReCAPTCHA token for verification" }, - { - "name": "licenseType", - "in": "path", - "required": true, - "schema": { - "type": "string" - } + "recoveryToken": { + "maxLength": 256, + "minLength": 1, + "type": "string", + "description": "Recovery token from the email link" } + }, + "additionalProperties": false + }, + "TestALicenzBTYlBMP0tPe": { + "required": [ + "jurisdiction" ], - "responses": { - "204": { - "description": "204 response", - "headers": { - "Access-Control-Allow-Origin": { - "schema": { - "type": "string" - } - }, - "Access-Control-Allow-Methods": { - "schema": { - "type": "string" - } - }, - "Vary": { - "schema": { - "type": "string" - } - }, - "Access-Control-Allow-Headers": { - "schema": { - "type": "string" - } - } - }, - "content": {} - } - } - } - }, - "/v1": { - "options": { - "responses": { - "204": { - "description": "204 response", - "headers": { - "Access-Control-Allow-Origin": { - "schema": { - "type": "string" - } - }, - "Access-Control-Allow-Methods": { - "schema": { - "type": "string" - } - }, - "Vary": { - "schema": { - "type": "string" - } - }, - "Access-Control-Allow-Headers": { - "schema": { - "type": "string" - } - } - }, - "content": {} - } - } - } - }, - "/v1/compacts/{compact}/providers/{providerId}/privileges/jurisdiction/{jurisdiction}/licenseType/{licenseType}/history": { - "get": { - "parameters": [ - { - "name": "Authorization", - "in": "header", - "required": true, - "schema": { - "type": "string" - } - }, - { - "name": "compact", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - }, - { - "name": "providerId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - }, - { - "name": "jurisdiction", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - }, - { - "name": "licenseType", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "200 response", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/SandboLicen0XcLtKpj7p28" - } - } - } - } - }, - "security": [ - { - "SandboxAPIStackLicenseApiStaffUsersPoolAuthorizer14A84A9B": [ - "aslp/readGeneral", - "octp/readGeneral", - "coun/readGeneral" + "type": "object", + "properties": { + "jurisdiction": { + "type": "string", + "description": "The jurisdiction postal abbreviation to set as home jurisdiction", + "enum": [ + "al", + "ak", + "az", + "ar", + "ca", + "co", + "ct", + "de", + "dc", + "fl", + "ga", + "hi", + "id", + "il", + "in", + "ia", + "ks", + "ky", + "la", + "me", + "md", + "ma", + "mi", + "mn", + "ms", + "mo", + "mt", + "ne", + "nv", + "nh", + "nj", + "nm", + "ny", + "nc", + "nd", + "oh", + "ok", + "or", + "pa", + "pr", + "ri", + "sc", + "sd", + "tn", + "tx", + "ut", + "vt", + "va", + "vi", + "wa", + "wv", + "wi", + "wy", + "other" ] } - ] + }, + "additionalProperties": false }, - "options": { - "parameters": [ - { - "name": "compact", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - }, - { - "name": "providerId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - }, - { - "name": "jurisdiction", - "in": "path", - "required": true, - "schema": { - "type": "string" - } + "TestALicenWc4n78hZdCvx": { + "required": [ + "affiliationType", + "fileNames" + ], + "type": "object", + "properties": { + "affiliationType": { + "type": "string", + "description": "The type of military affiliation", + "enum": [ + "militaryMember", + "militaryMemberSpouse" + ] }, - { - "name": "licenseType", - "in": "path", - "required": true, - "schema": { - "type": "string" + "fileNames": { + "type": "array", + "description": "List of military affiliation file names", + "items": { + "maxLength": 150, + "type": "string", + "description": "The name of the file being uploaded" } } - ], - "responses": { - "204": { - "description": "204 response", - "headers": { - "Access-Control-Allow-Origin": { - "schema": { - "type": "string" - } - }, - "Access-Control-Allow-Methods": { - "schema": { - "type": "string" - } + }, + "additionalProperties": false + }, + "TestALicenlDVuL3LTR9jI": { + "type": "object", + "properties": { + "pagination": { + "type": "object", + "properties": { + "prevLastKey": { + "maxLength": 1024, + "minLength": 1, + "type": "object" }, - "Vary": { - "schema": { - "type": "string" - } + "lastKey": { + "maxLength": 1024, + "minLength": 1, + "type": "object" }, - "Access-Control-Allow-Headers": { - "schema": { - "type": "string" - } - } - }, - "content": {} - } - } - } - }, - "/v1/public/compacts/{compact}/providers/{providerId}": { - "get": { - "parameters": [ - { - "name": "compact", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - }, - { - "name": "providerId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "200": { - "description": "200 response", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/SandboLicennv4iZSNKxEXN" - } + "pageSize": { + "maximum": 100, + "minimum": 5, + "type": "integer" } } - } - } - }, - "options": { - "parameters": [ - { - "name": "compact", - "in": "path", - "required": true, - "schema": { - "type": "string" - } }, - { - "name": "providerId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "204": { - "description": "204 response", - "headers": { - "Access-Control-Allow-Origin": { - "schema": { - "type": "string" - } - }, - "Access-Control-Allow-Methods": { - "schema": { - "type": "string" - } - }, - "Vary": { - "schema": { + "users": { + "type": "array", + "items": { + "required": [ + "attributes", + "permissions", + "status", + "userId" + ], + "type": "object", + "properties": { + "permissions": { + "type": "object", + "additionalProperties": { + "type": "object", + "properties": { + "actions": { + "type": "object", + "properties": { + "readPrivate": { + "type": "boolean" + }, + "admin": { + "type": "boolean" + }, + "readSSN": { + "type": "boolean" + } + } + }, + "jurisdictions": { + "type": "object", + "additionalProperties": { + "type": "object", + "properties": { + "actions": { + "type": "object", + "properties": { + "readPrivate": { + "type": "boolean" + }, + "admin": { + "type": "boolean" + }, + "write": { + "type": "boolean" + }, + "readSSN": { + "type": "boolean" + } + }, + "additionalProperties": false + } + } + } + } + }, + "additionalProperties": false + } + }, + "attributes": { + "required": [ + "email", + "familyName", + "givenName" + ], + "type": "object", + "properties": { + "givenName": { + "maxLength": 100, + "minLength": 1, + "type": "string" + }, + "familyName": { + "maxLength": 100, + "minLength": 1, + "type": "string" + }, + "email": { + "maxLength": 100, + "minLength": 5, + "type": "string" + } + }, + "additionalProperties": false + }, + "userId": { "type": "string" + }, + "status": { + "type": "string", + "enum": [ + "active", + "inactive" + ] } }, - "Access-Control-Allow-Headers": { - "schema": { - "type": "string" - } - } - }, - "content": {} - } - } - } - }, - "/v1/compacts/{compact}/providers/{providerId}/privileges/jurisdiction/{jurisdiction}/licenseType": { - "options": { - "parameters": [ - { - "name": "compact", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - }, - { - "name": "providerId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - }, - { - "name": "jurisdiction", - "in": "path", - "required": true, - "schema": { - "type": "string" + "additionalProperties": false } } + }, + "additionalProperties": false + }, + "TestALicenBPW27y0J3cSV": { + "required": [ + "action" ], - "responses": { - "204": { - "description": "204 response", - "headers": { - "Access-Control-Allow-Origin": { - "schema": { - "type": "string" - } - }, - "Access-Control-Allow-Methods": { - "schema": { - "type": "string" - } + "type": "object", + "properties": { + "action": { + "type": "string", + "enum": [ + "close" + ] + }, + "encumbrance": { + "required": [ + "clinicalPrivilegeActionCategories", + "encumbranceEffectiveDate", + "encumbranceType" + ], + "type": "object", + "properties": { + "encumbranceEffectiveDate": { + "pattern": "^[12]{1}[0-9]{3}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$", + "type": "string", + "description": "The effective date of the encumbrance", + "format": "date" }, - "Vary": { - "schema": { + "clinicalPrivilegeActionCategories": { + "type": "array", + "description": "The categories of clinical privilege action", + "items": { "type": "string" } }, - "Access-Control-Allow-Headers": { - "schema": { - "type": "string" - } + "encumbranceType": { + "type": "string", + "description": "The type of encumbrance", + "enum": [ + "fine", + "reprimand", + "required supervision", + "completion of continuing education", + "public reprimand", + "probation", + "injunctive action", + "suspension", + "revocation", + "denial", + "surrender of license", + "modification of previous action-extension", + "modification of previous action-reduction", + "other monitoring", + "other adjudicated action not listed" + ] } }, - "content": {} + "additionalProperties": false, + "description": "Encumbrance data to create" } } - } - }, - "/v1/provider-users/me/jurisdiction/{jurisdiction}/licenseType": { - "options": { - "parameters": [ - { - "name": "jurisdiction", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } + }, + "TestALicenbtMyL0MqSVUK": { + "required": [ + "birthMonthDay", + "compact", + "dateOfExpiration", + "dateOfUpdate", + "familyName", + "givenName", + "licenseJurisdiction", + "licenses", + "militaryAffiliations", + "privilegeJurisdictions", + "privileges", + "providerId", + "type" ], - "responses": { - "204": { - "description": "204 response", - "headers": { - "Access-Control-Allow-Origin": { - "schema": { - "type": "string" - } - }, - "Access-Control-Allow-Methods": { - "schema": { - "type": "string" - } - }, - "Vary": { - "schema": { - "type": "string" - } - }, - "Access-Control-Allow-Headers": { - "schema": { - "type": "string" - } - } - }, - "content": {} - } - } - } - }, - "/v1/provider-users/me/email/verify": { - "post": { - "parameters": [ - { - "name": "Authorization", - "in": "header", - "required": true, - "schema": { - "type": "string" - } - } - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/SandboLicenlu9HVFJNEZQz" - } - } - }, - "required": true - }, - "responses": { - "200": { - "description": "200 response", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/SandboLicenTMQQKAeKTKQR" - } - } - } - } - }, - "security": [ - { - "SandboxAPIStackLicenseApiProviderUsersPoolAuthorizerEB7523BA": [] - } - ] - }, - "options": { - "responses": { - "204": { - "description": "204 response", - "headers": { - "Access-Control-Allow-Origin": { - "schema": { - "type": "string" - } - }, - "Access-Control-Allow-Methods": { - "schema": { - "type": "string" - } - }, - "Vary": { - "schema": { - "type": "string" - } - }, - "Access-Control-Allow-Headers": { - "schema": { - "type": "string" - } - } - }, - "content": {} - } - } - } - }, - "/v1/compacts/{compact}/attestations": { - "options": { - "parameters": [ - { - "name": "compact", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "204": { - "description": "204 response", - "headers": { - "Access-Control-Allow-Origin": { - "schema": { - "type": "string" - } - }, - "Access-Control-Allow-Methods": { - "schema": { - "type": "string" - } - }, - "Vary": { - "schema": { - "type": "string" - } - }, - "Access-Control-Allow-Headers": { - "schema": { - "type": "string" - } - } - }, - "content": {} - } - } - } - }, - "/v1/compacts/{compact}/providers/{providerId}/licenses/jurisdiction/{jurisdiction}/licenseType/{licenseType}": { - "options": { - "parameters": [ - { - "name": "compact", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - }, - { - "name": "providerId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - }, - { - "name": "jurisdiction", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - }, - { - "name": "licenseType", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "204": { - "description": "204 response", - "headers": { - "Access-Control-Allow-Origin": { - "schema": { - "type": "string" - } - }, - "Access-Control-Allow-Methods": { - "schema": { - "type": "string" - } - }, - "Vary": { - "schema": { - "type": "string" - } - }, - "Access-Control-Allow-Headers": { - "schema": { - "type": "string" - } - } - }, - "content": {} - } - } - } - }, - "/v1/compacts/{compact}/credentials/payment-processor": { - "post": { - "parameters": [ - { - "name": "Authorization", - "in": "header", - "required": true, - "schema": { - "type": "string" - } - }, - { - "name": "compact", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/SandboLicenmMTXPta5fldR" - } - } - }, - "required": true - }, - "responses": { - "200": { - "description": "200 response", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/SandboLicen9dv1jZzfaVo8" - } - } - } - } - }, - "security": [ - { - "SandboxAPIStackLicenseApiStaffUsersPoolAuthorizer14A84A9B": [ - "aslp/admin", - "al/aslp.admin", - "ak/aslp.admin", - "ar/aslp.admin", - "co/aslp.admin", - "de/aslp.admin", - "ky/aslp.admin", - "la/aslp.admin", - "me/aslp.admin", - "md/aslp.admin", - "mn/aslp.admin", - "ms/aslp.admin", - "mo/aslp.admin", - "ne/aslp.admin", - "oh/aslp.admin", - "octp/admin", - "al/octp.admin", - "ar/octp.admin", - "ky/octp.admin", - "la/octp.admin", - "ms/octp.admin", - "ne/octp.admin", - "oh/octp.admin", - "coun/admin", - "al/coun.admin", - "ar/coun.admin", - "fl/coun.admin", - "ga/coun.admin", - "ky/coun.admin", - "ne/coun.admin", - "oh/coun.admin", - "ut/coun.admin" - ] - } - ] - }, - "options": { - "parameters": [ - { - "name": "compact", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "204": { - "description": "204 response", - "headers": { - "Access-Control-Allow-Origin": { - "schema": { - "type": "string" - } - }, - "Access-Control-Allow-Methods": { - "schema": { - "type": "string" - } - }, - "Vary": { - "schema": { - "type": "string" - } - }, - "Access-Control-Allow-Headers": { - "schema": { - "type": "string" - } - } - }, - "content": {} - } - } - } - }, - "/v1/public/compacts/{compact}/providers/{providerId}/jurisdiction/{jurisdiction}/licenseType/{licenseType}": { - "options": { - "parameters": [ - { - "name": "compact", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - }, - { - "name": "providerId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - }, - { - "name": "jurisdiction", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - }, - { - "name": "licenseType", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "204": { - "description": "204 response", - "headers": { - "Access-Control-Allow-Origin": { - "schema": { - "type": "string" - } - }, - "Access-Control-Allow-Methods": { - "schema": { - "type": "string" - } - }, - "Vary": { - "schema": { - "type": "string" - } - }, - "Access-Control-Allow-Headers": { - "schema": { - "type": "string" - } - } - }, - "content": {} - } - } - } - }, - "/v1/compacts/{compact}/providers/{providerId}/privileges/jurisdiction/{jurisdiction}/licenseType/{licenseType}/encumbrance/{encumbranceId}": { - "options": { - "parameters": [ - { - "name": "compact", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - }, - { - "name": "providerId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - }, - { - "name": "jurisdiction", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - }, - { - "name": "licenseType", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - }, - { - "name": "encumbranceId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "204": { - "description": "204 response", - "headers": { - "Access-Control-Allow-Origin": { - "schema": { - "type": "string" - } - }, - "Access-Control-Allow-Methods": { - "schema": { - "type": "string" - } - }, - "Vary": { - "schema": { - "type": "string" - } - }, - "Access-Control-Allow-Headers": { - "schema": { - "type": "string" - } - } - }, - "content": {} - } - } - }, - "patch": { - "parameters": [ - { - "name": "Authorization", - "in": "header", - "required": true, - "schema": { - "type": "string" - } - }, - { - "name": "compact", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - }, - { - "name": "providerId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - }, - { - "name": "jurisdiction", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - }, - { - "name": "licenseType", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - }, - { - "name": "encumbranceId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/SandboLicenSo5EafP3rZhM" - } - } - }, - "required": true - }, - "responses": { - "200": { - "description": "200 response", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/SandboLicenTMQQKAeKTKQR" - } - } - } - } - }, - "security": [ - { - "SandboxAPIStackLicenseApiStaffUsersPoolAuthorizer14A84A9B": [ - "aslp/admin", - "al/aslp.admin", - "ak/aslp.admin", - "ar/aslp.admin", - "co/aslp.admin", - "de/aslp.admin", - "ky/aslp.admin", - "la/aslp.admin", - "me/aslp.admin", - "md/aslp.admin", - "mn/aslp.admin", - "ms/aslp.admin", - "mo/aslp.admin", - "ne/aslp.admin", - "oh/aslp.admin", - "octp/admin", - "al/octp.admin", - "ar/octp.admin", - "ky/octp.admin", - "la/octp.admin", - "ms/octp.admin", - "ne/octp.admin", - "oh/octp.admin", - "coun/admin", - "al/coun.admin", - "ar/coun.admin", - "fl/coun.admin", - "ga/coun.admin", - "ky/coun.admin", - "ne/coun.admin", - "oh/coun.admin", - "ut/coun.admin" - ] - } - ] - } - }, - "/v1/compacts/{compact}/providers": { - "options": { - "parameters": [ - { - "name": "compact", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "responses": { - "204": { - "description": "204 response", - "headers": { - "Access-Control-Allow-Origin": { - "schema": { - "type": "string" - } - }, - "Access-Control-Allow-Methods": { - "schema": { - "type": "string" - } - }, - "Vary": { - "schema": { - "type": "string" - } - }, - "Access-Control-Allow-Headers": { - "schema": { - "type": "string" - } - } - }, - "content": {} - } - } - } - } - }, - "components": { - "schemas": { - "SandboLicenZnCDqNvEXOu2": { - "required": [ - "militaryStatus" - ], - "type": "object", - "properties": { - "militaryStatusNote": { - "maxLength": 5000, - "type": "string", - "description": "Optional note from the admin (typically for declines)" - }, - "militaryStatus": { - "type": "string", - "description": "The audit result for the military documentation", - "enum": [ - "approved", - "declined" - ] - } - }, - "additionalProperties": false - }, - "SandboLicenudbEF4n02FXU": { - "required": [ - "newEmailAddress" - ], - "type": "object", - "properties": { - "newEmailAddress": { - "maxLength": 100, - "minLength": 5, - "type": "string", - "description": "The new email address to set for the provider", - "format": "email" - } - }, - "additionalProperties": false - }, - "SandboLicenmKXS1L8tLsGF": { - "required": [ - "clinicalPrivilegeActionCategories", - "encumbranceEffectiveDate", - "encumbranceType" - ], - "type": "object", - "properties": { - "encumbranceEffectiveDate": { - "pattern": "^[12]{1}[0-9]{3}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$", - "type": "string", - "description": "The effective date of the encumbrance", - "format": "date" - }, - "clinicalPrivilegeActionCategories": { - "type": "array", - "description": "The categories of clinical privilege action", - "items": { - "type": "string" - } - }, - "encumbranceType": { - "type": "string", - "description": "The type of encumbrance", - "enum": [ - "fine", - "reprimand", - "required supervision", - "completion of continuing education", - "public reprimand", - "probation", - "injunctive action", - "suspension", - "revocation", - "denial", - "surrender of license", - "modification of previous action-extension", - "modification of previous action-reduction", - "other monitoring", - "other adjudicated action not listed" - ] - } - }, - "additionalProperties": false, - "description": "Encumbrance data to create" - }, - "SandboLicennuxBDueZ6Trv": { - "required": [ - "action" - ], - "type": "object", - "properties": { - "action": { - "type": "string", - "enum": [ - "close" - ] - }, - "encumbrance": { - "required": [ - "clinicalPrivilegeActionCategories", - "encumbranceEffectiveDate", - "encumbranceType" - ], - "type": "object", - "properties": { - "encumbranceEffectiveDate": { - "pattern": "^[12]{1}[0-9]{3}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$", - "type": "string", - "description": "The effective date of the encumbrance", - "format": "date" - }, - "clinicalPrivilegeActionCategories": { - "type": "array", - "description": "The categories of clinical privilege action", - "items": { - "type": "string" - } - }, - "encumbranceType": { - "type": "string", - "description": "The type of encumbrance", - "enum": [ - "fine", - "reprimand", - "required supervision", - "completion of continuing education", - "public reprimand", - "probation", - "injunctive action", - "suspension", - "revocation", - "denial", - "surrender of license", - "modification of previous action-extension", - "modification of previous action-reduction", - "other monitoring", - "other adjudicated action not listed" - ] - } - }, - "additionalProperties": false, - "description": "Encumbrance data to create" - } - } - }, - "SandboLicen2qPPtuQWh8hv": { - "required": [ - "attributes", - "permissions", - "status", - "userId" - ], - "type": "object", - "properties": { - "permissions": { - "type": "object", - "additionalProperties": { - "type": "object", - "properties": { - "actions": { - "type": "object", - "properties": { - "readPrivate": { - "type": "boolean" - }, - "admin": { - "type": "boolean" - }, - "readSSN": { - "type": "boolean" - } - } - }, - "jurisdictions": { - "type": "object", - "additionalProperties": { - "type": "object", - "properties": { - "actions": { - "type": "object", - "properties": { - "readPrivate": { - "type": "boolean" - }, - "admin": { - "type": "boolean" - }, - "write": { - "type": "boolean" - }, - "readSSN": { - "type": "boolean" - } - }, - "additionalProperties": false - } - } - } - } - }, - "additionalProperties": false - } - }, - "attributes": { - "required": [ - "email", - "familyName", - "givenName" - ], - "type": "object", - "properties": { - "givenName": { - "maxLength": 100, - "minLength": 1, - "type": "string" - }, - "familyName": { - "maxLength": 100, - "minLength": 1, - "type": "string" - }, - "email": { - "maxLength": 100, - "minLength": 5, - "type": "string" - } - }, - "additionalProperties": false - }, - "userId": { - "type": "string" - }, - "status": { - "type": "string", - "enum": [ - "active", - "inactive" - ] - } - }, - "additionalProperties": false - }, - "SandboLicendz0gMqUAh7qN": { - "type": "object", - "properties": { - "attributes": { - "type": "object", - "properties": { - "givenName": { - "maxLength": 100, - "minLength": 1, - "type": "string" - }, - "familyName": { - "maxLength": 100, - "minLength": 1, - "type": "string" - } - }, - "additionalProperties": false - } - }, - "additionalProperties": false - }, - "SandboLicenztG3aZP1J9M3": { - "required": [ - "status" - ], - "type": "object", - "properties": { - "status": { - "type": "string", - "description": "The status to set the military affiliation to.", - "enum": [ - "inactive" - ] - } - }, - "additionalProperties": false - }, - "SandboLicenKbxrVseriPZY": { - "type": "object", - "properties": {} - }, - "SandboLicenpl2UadOfjgNJ": { - "type": "object", - "properties": { - "context": { - "type": "object", - "properties": { - "userId": { - "maxLength": 100, - "minLength": 1, - "type": "string", - "description": "Optional user ID for feature flag evaluation" - }, - "customAttributes": { - "type": "object", - "additionalProperties": { - "type": "string" - }, - "description": "Optional custom attributes for feature flag evaluation" - } - }, - "additionalProperties": false, - "description": "Optional context for feature flag evaluation" - } - }, - "additionalProperties": false - }, - "SandboLicen9dv1jZzfaVo8": { - "required": [ - "message" - ], - "type": "object", - "properties": { - "message": { - "type": "string", - "description": "A message about the request" - } - } - }, - "SandboLicenSFWw1LC3Pl63": { - "required": [ - "affiliationType", - "dateOfUpdate", - "dateOfUpload", - "documentUploadFields", - "status" - ], - "type": "object", - "properties": { - "dateOfUpload": { - "pattern": "^[12]{1}[0-9]{3}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$", - "type": "string", - "description": "The date the document was uploaded", - "format": "date" - }, - "affiliationType": { - "type": "string", - "description": "The type of military affiliation", - "enum": [ - "militaryMember", - "militaryMemberSpouse" - ] - }, - "fileNames": { - "type": "array", - "description": "List of military affiliation file names", - "items": { - "type": "string", - "description": "The name of the file being uploaded" - } - }, - "dateOfUpdate": { - "type": "string", - "description": "The date the document was last updated", - "format": "date-time" - }, - "status": { - "type": "string", - "description": "The status of the military affiliation" - }, - "documentUploadFields": { - "type": "array", - "description": "The fields used to upload documents", - "items": { - "type": "object", - "properties": { - "fields": { - "type": "object", - "additionalProperties": { - "type": "string" - }, - "description": "The form fields used to upload the document" - }, - "url": { - "type": "string", - "description": "The url to upload the document to" - } - }, - "description": "The fields used to upload a specific document" - } - } - } - }, - "SandboLicenf1YSNMeYKlGD": { - "required": [ - "pagination", - "providers" - ], - "type": "object", - "properties": { - "pagination": { - "type": "object", - "properties": { - "prevLastKey": { - "maxLength": 1024, - "minLength": 1, - "type": "object" - }, - "lastKey": { - "maxLength": 1024, - "minLength": 1, - "type": "object" - }, - "pageSize": { - "maximum": 100, - "minimum": 5, - "type": "integer" - } - } - }, - "query": { - "type": "object", - "properties": { - "providerId": { - "pattern": "[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab]{1}[0-9a-f]{3}-[0-9a-f]{12}", - "type": "string", - "description": "Internal UUID for the provider" - }, - "jurisdiction": { - "type": "string", - "description": "Filter for providers with privilege/license in a jurisdiction", - "enum": [ - "al", - "ak", - "az", - "ar", - "ca", - "co", - "ct", - "de", - "dc", - "fl", - "ga", - "hi", - "id", - "il", - "in", - "ia", - "ks", - "ky", - "la", - "me", - "md", - "ma", - "mi", - "mn", - "ms", - "mo", - "mt", - "ne", - "nv", - "nh", - "nj", - "nm", - "ny", - "nc", - "nd", - "oh", - "ok", - "or", - "pa", - "pr", - "ri", - "sc", - "sd", - "tn", - "tx", - "ut", - "vt", - "va", - "vi", - "wa", - "wv", - "wi", - "wy" - ] - }, - "givenName": { - "maxLength": 100, - "type": "string", - "description": "Filter for providers with a given name" - }, - "familyName": { - "maxLength": 100, - "type": "string", - "description": "Filter for providers with a family name" - } - } - }, - "sorting": { - "required": [ - "key" - ], - "type": "object", - "properties": { - "key": { - "type": "string", - "description": "The key to sort results by", - "enum": [ - "dateOfUpdate", - "familyName" - ] - }, - "direction": { - "type": "string", - "description": "Direction to sort results by", - "enum": [ - "ascending", - "descending" - ] - } - }, - "description": "How to sort results" - }, - "providers": { - "maxLength": 100, - "type": "array", - "items": { - "required": [ - "compact", - "familyName", - "givenName", - "licenseJurisdiction", - "privilegeJurisdictions", - "providerId", - "type" - ], - "type": "object", - "properties": { - "licenseJurisdiction": { - "type": "string", - "enum": [ - "al", - "ak", - "az", - "ar", - "ca", - "co", - "ct", - "de", - "dc", - "fl", - "ga", - "hi", - "id", - "il", - "in", - "ia", - "ks", - "ky", - "la", - "me", - "md", - "ma", - "mi", - "mn", - "ms", - "mo", - "mt", - "ne", - "nv", - "nh", - "nj", - "nm", - "ny", - "nc", - "nd", - "oh", - "ok", - "or", - "pa", - "pr", - "ri", - "sc", - "sd", - "tn", - "tx", - "ut", - "vt", - "va", - "vi", - "wa", - "wv", - "wi", - "wy" - ] - }, - "compact": { - "type": "string", - "enum": [ - "aslp", - "octp", - "coun" - ] - }, - "providerId": { - "pattern": "[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab]{1}[0-9a-f]{3}-[0-9a-f]{12}", - "type": "string" - }, - "npi": { - "pattern": "^[0-9]{10}$", - "type": "string" - }, - "givenName": { - "maxLength": 100, - "minLength": 1, - "type": "string" - }, - "familyName": { - "maxLength": 100, - "minLength": 1, - "type": "string" - }, - "middleName": { - "maxLength": 100, - "minLength": 1, - "type": "string" - }, - "privilegeJurisdictions": { - "type": "array", - "items": { - "type": "string", - "enum": [ - "al", - "ak", - "az", - "ar", - "ca", - "co", - "ct", - "de", - "dc", - "fl", - "ga", - "hi", - "id", - "il", - "in", - "ia", - "ks", - "ky", - "la", - "me", - "md", - "ma", - "mi", - "mn", - "ms", - "mo", - "mt", - "ne", - "nv", - "nh", - "nj", - "nm", - "ny", - "nc", - "nd", - "oh", - "ok", - "or", - "pa", - "pr", - "ri", - "sc", - "sd", - "tn", - "tx", - "ut", - "vt", - "va", - "vi", - "wa", - "wv", - "wi", - "wy" - ] - } - }, - "type": { - "type": "string", - "enum": [ - "provider" - ] - }, - "suffix": { - "maxLength": 100, - "minLength": 1, - "type": "string" - }, - "currentHomeJurisdiction": { - "type": "string", - "description": "The current jurisdiction postal abbreviation if known.", - "enum": [ - "al", - "ak", - "az", - "ar", - "ca", - "co", - "ct", - "de", - "dc", - "fl", - "ga", - "hi", - "id", - "il", - "in", - "ia", - "ks", - "ky", - "la", - "me", - "md", - "ma", - "mi", - "mn", - "ms", - "mo", - "mt", - "ne", - "nv", - "nh", - "nj", - "nm", - "ny", - "nc", - "nd", - "oh", - "ok", - "or", - "pa", - "pr", - "ri", - "sc", - "sd", - "tn", - "tx", - "ut", - "vt", - "va", - "vi", - "wa", - "wv", - "wi", - "wy", - "other", - "unknown" - ] - }, - "dateOfUpdate": { - "type": "string", - "format": "date-time" - } - } - } - } - } - }, - "SandboLicen0TA7m9cBJLpz": { - "type": "object", - "properties": { - "dateCreated": { - "type": "string", - "format": "date-time" - }, - "attestationId": { - "type": "string" - }, - "compact": { - "type": "string", - "enum": [ - "aslp", - "octp", - "coun" - ] - }, - "text": { - "type": "string" - }, - "type": { - "type": "string", - "enum": [ - "attestation" - ] - }, - "locale": { - "type": "string" - }, - "version": { - "type": "string" - }, - "required": { - "type": "boolean" - } - } - }, - "SandboLicenEPhGnRpsTFzc": { - "type": "object", - "additionalProperties": { - "type": "array", - "items": { - "type": "string", - "enum": [ - "al", - "ak", - "az", - "ar", - "ca", - "co", - "ct", - "de", - "dc", - "fl", - "ga", - "hi", - "id", - "il", - "in", - "ia", - "ks", - "ky", - "la", - "me", - "md", - "ma", - "mi", - "mn", - "ms", - "mo", - "mt", - "ne", - "nv", - "nh", - "nj", - "nm", - "ny", - "nc", - "nd", - "oh", - "ok", - "or", - "pa", - "pr", - "ri", - "sc", - "sd", - "tn", - "tx", - "ut", - "vt", - "va", - "vi", - "wa", - "wv", - "wi", - "wy" - ] - } - } - }, - "SandboLicenx7ouhX772atw": { - "required": [ - "ssn" - ], - "type": "object", - "properties": { - "ssn": { - "pattern": "^[0-9]{3}-[0-9]{2}-[0-9]{4}$", - "type": "string", - "description": "The provider's social security number" - } - } - }, - "SandboLicenFqy1VQNhsjvi": { - "required": [ - "effectiveLiftDate" - ], - "type": "object", - "properties": { - "effectiveLiftDate": { - "pattern": "^[12]{1}[0-9]{3}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$", - "type": "string", - "description": "The effective date when the encumbrance will be lifted", - "format": "date" - } - }, - "additionalProperties": false - }, - "SandboLicenJt67zBFIGGPS": { - "required": [ - "compact", - "dob", - "familyName", - "givenName", - "jurisdiction", - "licenseType", - "partialSocial", - "password", - "recaptchaToken", - "username" - ], - "type": "object", - "properties": { - "licenseType": { - "type": "string", - "description": "Type of license", - "enum": [ - "audiologist", - "speech-language pathologist", - "occupational therapist", - "occupational therapy assistant", - "licensed professional counselor" - ] - }, - "password": { - "maxLength": 256, - "minLength": 12, - "type": "string", - "description": "Provider's current password" - }, - "compact": { - "type": "string", - "description": "Compact abbreviation", - "enum": [ - "aslp", - "octp", - "coun" - ] - }, - "dob": { - "pattern": "^[12]{1}[0-9]{3}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$", - "type": "string", - "description": "Date of birth in YYYY-MM-DD format", - "format": "date" - }, - "jurisdiction": { - "type": "string", - "description": "Two-letter jurisdiction code", - "enum": [ - "al", - "ak", - "az", - "ar", - "ca", - "co", - "ct", - "de", - "dc", - "fl", - "ga", - "hi", - "id", - "il", - "in", - "ia", - "ks", - "ky", - "la", - "me", - "md", - "ma", - "mi", - "mn", - "ms", - "mo", - "mt", - "ne", - "nv", - "nh", - "nj", - "nm", - "ny", - "nc", - "nd", - "oh", - "ok", - "or", - "pa", - "pr", - "ri", - "sc", - "sd", - "tn", - "tx", - "ut", - "vt", - "va", - "vi", - "wa", - "wv", - "wi", - "wy" - ] - }, - "givenName": { - "maxLength": 200, - "minLength": 1, - "type": "string", - "description": "Provider's given name" - }, - "familyName": { - "maxLength": 200, - "minLength": 1, - "type": "string", - "description": "Provider's family name" - }, - "recaptchaToken": { - "minLength": 1, - "type": "string", - "description": "ReCAPTCHA token for verification" - }, - "partialSocial": { - "pattern": "^[0-9]{4}$", - "type": "string", - "description": "Last 4 digits of SSN" - }, - "username": { - "maxLength": 100, - "minLength": 5, - "type": "string", - "description": "Provider's email address (username)", - "format": "email" - } - }, - "additionalProperties": false - }, - "SandboLicenZx6a73xGIfzu": { - "required": [ - "compactAdverseActionsNotificationEmails", - "compactCommissionFee", - "compactOperationsTeamEmails", - "compactSummaryReportNotificationEmails", - "configuredStates", - "licenseeRegistrationEnabled" - ], - "type": "object", - "properties": { - "configuredStates": { - "type": "array", - "description": "List of states that have submitted configurations and their live status", - "items": { - "required": [ - "isLive", - "postalAbbreviation" - ], - "type": "object", - "properties": { - "postalAbbreviation": { - "type": "string", - "description": "The postal abbreviation of the jurisdiction", - "enum": [ - "al", - "ak", - "az", - "ar", - "ca", - "co", - "ct", - "de", - "dc", - "fl", - "ga", - "hi", - "id", - "il", - "in", - "ia", - "ks", - "ky", - "la", - "me", - "md", - "ma", - "mi", - "mn", - "ms", - "mo", - "mt", - "ne", - "nv", - "nh", - "nj", - "nm", - "ny", - "nc", - "nd", - "oh", - "ok", - "or", - "pa", - "pr", - "ri", - "sc", - "sd", - "tn", - "tx", - "ut", - "vt", - "va", - "vi", - "wa", - "wv", - "wi", - "wy" - ] - }, - "isLive": { - "type": "boolean", - "description": "Whether the state is live and available for registrations." - } - }, - "additionalProperties": false - } - }, - "compactCommissionFee": { - "required": [ - "feeAmount", - "feeType" - ], - "type": "object", - "properties": { - "feeAmount": { - "minimum": 0, - "type": "number" - }, - "feeType": { - "type": "string", - "enum": [ - "FLAT_RATE" - ] - } - }, - "additionalProperties": false - }, - "compactSummaryReportNotificationEmails": { - "maxItems": 10, - "minItems": 1, - "uniqueItems": true, - "type": "array", - "description": "List of email addresses for summary report notifications", - "items": { - "type": "string", - "format": "email" - } - }, - "compactAdverseActionsNotificationEmails": { - "maxItems": 10, - "minItems": 1, - "uniqueItems": true, - "type": "array", - "description": "List of email addresses for adverse actions notifications", - "items": { - "type": "string", - "format": "email" - } - }, - "licenseeRegistrationEnabled": { - "type": "boolean", - "description": "Denotes whether licensee registration is enabled" - }, - "transactionFeeConfiguration": { - "type": "object", - "properties": { - "licenseeCharges": { - "required": [ - "active", - "chargeAmount", - "chargeType" - ], - "type": "object", - "properties": { - "chargeType": { - "type": "string", - "description": "The type of transaction fee charge", - "enum": [ - "FLAT_FEE_PER_PRIVILEGE" - ] - }, - "active": { - "type": "boolean", - "description": "Whether the compact is charging licensees transaction fees" - }, - "chargeAmount": { - "minimum": 0, - "type": "number", - "description": "The amount to charge per privilege purchased" - } - }, - "additionalProperties": false - } - }, - "additionalProperties": false - }, - "compactOperationsTeamEmails": { - "maxItems": 10, - "minItems": 1, - "uniqueItems": true, - "type": "array", - "description": "List of email addresses for operations team notifications", - "items": { - "type": "string", - "format": "email" - } - } - }, - "additionalProperties": false - }, - "SandboLiceniWnENMKPoAG6": { - "required": [ - "compact", - "jurisdictionAdverseActionsNotificationEmails", - "jurisdictionName", - "jurisdictionOperationsTeamEmails", - "jurisdictionSummaryReportNotificationEmails", - "jurisprudenceRequirements", - "licenseeRegistrationEnabled", - "postalAbbreviation", - "privilegeFees" - ], - "type": "object", - "properties": { - "privilegeFees": { - "type": "array", - "description": "The fees for the privileges by license type", - "items": { - "required": [ - "amount", - "licenseTypeAbbreviation" - ], - "type": "object", - "properties": { - "amount": { - "type": "number" - }, - "militaryRate": { - "description": "Optional military rate for the privilege fee.", - "oneOf": [ - { - "minimum": 0, - "type": "number" - }, - null - ] - }, - "licenseTypeAbbreviation": { - "type": "string", - "enum": [ - "aud", - "slp", - "ot", - "ota", - "lpc" - ] - } - } - } - }, - "postalAbbreviation": { - "type": "string", - "description": "The postal abbreviation of the jurisdiction" - }, - "jurisdictionAdverseActionsNotificationEmails": { - "type": "array", - "description": "List of email addresses for adverse actions notifications", - "items": { - "type": "string", - "format": "email" - } - }, - "jurisdictionOperationsTeamEmails": { - "type": "array", - "description": "List of email addresses for operations team notifications", - "items": { - "type": "string", - "format": "email" - } - }, - "compact": { - "type": "string", - "description": "The compact this jurisdiction configuration belongs to", - "enum": [ - "aslp", - "octp", - "coun" - ] - }, - "jurisprudenceRequirements": { - "required": [ - "required" - ], - "type": "object", - "properties": { - "linkToDocumentation": { - "description": "Optional link to jurisprudence documentation", - "oneOf": [ - { - "type": "string" - }, - null - ] - }, - "required": { - "type": "boolean", - "description": "Whether jurisprudence requirements exist" - } - } - }, - "licenseeRegistrationEnabled": { - "type": "boolean", - "description": "Denotes whether licensee registration is enabled" - }, - "jurisdictionName": { - "type": "string", - "description": "The name of the jurisdiction" - }, - "jurisdictionSummaryReportNotificationEmails": { - "type": "array", - "description": "List of email addresses for summary report notifications", - "items": { - "type": "string", - "format": "email" - } - } - } - }, - "SandboLicenJwiQTQPNiltz": { - "required": [ - "jurisdictionAdverseActionsNotificationEmails", - "jurisdictionOperationsTeamEmails", - "jurisdictionSummaryReportNotificationEmails", - "jurisprudenceRequirements", - "licenseeRegistrationEnabled", - "privilegeFees" - ], - "type": "object", - "properties": { - "privilegeFees": { - "type": "array", - "description": "The fees for the privileges by license type", - "items": { - "required": [ - "amount", - "licenseTypeAbbreviation" - ], - "type": "object", - "properties": { - "amount": { - "minimum": 0, - "type": "number" - }, - "militaryRate": { - "description": "Optional military rate for the privilege fee.", - "oneOf": [ - { - "minimum": 0, - "type": "number" - }, - null - ] - }, - "licenseTypeAbbreviation": { - "type": "string", - "enum": [ - "aud", - "slp", - "ot", - "ota", - "lpc" - ] - } - }, - "additionalProperties": false - } - }, - "jurisdictionAdverseActionsNotificationEmails": { - "maxItems": 10, - "minItems": 1, - "uniqueItems": true, - "type": "array", - "description": "List of email addresses for adverse actions notifications", - "items": { - "type": "string", - "format": "email" - } - }, - "jurisdictionOperationsTeamEmails": { - "maxItems": 10, - "minItems": 1, - "uniqueItems": true, - "type": "array", - "description": "List of email addresses for operations team notifications", - "items": { - "type": "string", - "format": "email" - } - }, - "jurisprudenceRequirements": { - "required": [ - "required" - ], - "type": "object", - "properties": { - "linkToDocumentation": { - "description": "Optional link to jurisprudence documentation", - "oneOf": [ - { - "type": "string" - }, - null - ] - }, - "required": { - "type": "boolean", - "description": "Whether jurisprudence requirements exist" - } - }, - "additionalProperties": false - }, - "licenseeRegistrationEnabled": { - "type": "boolean", - "description": "Denotes whether licensee registration is enabled" - }, - "jurisdictionSummaryReportNotificationEmails": { - "maxItems": 10, - "minItems": 1, - "uniqueItems": true, - "type": "array", - "description": "List of email addresses for summary report notifications", - "items": { - "type": "string", - "format": "email" - } - } - }, - "additionalProperties": false - }, - "SandboLicenTMQQKAeKTKQR": { - "required": [ - "message" - ], - "type": "object", - "properties": { - "message": { - "type": "string", - "description": "A message about the request" - } - } - }, - "SandboLicenlu9HVFJNEZQz": { - "required": [ - "verificationCode" - ], - "type": "object", - "properties": { - "verificationCode": { - "pattern": "^[0-9]{4}$", - "type": "string", - "description": "4-digit verification code" - } - }, - "additionalProperties": false - }, - "SandboLicen0XcLtKpj7p28": { - "required": [ - "compact", - "events", - "jurisdiction", - "licenseType", - "privilegeId", - "providerId" - ], - "type": "object", - "properties": { - "licenseType": { - "type": "string", - "enum": [ - "audiologist", - "speech-language pathologist", - "occupational therapist", - "occupational therapy assistant", - "licensed professional counselor" - ] - }, - "compact": { - "type": "string", - "enum": [ - "aslp", - "octp", - "coun" - ] - }, - "privilegeId": { - "type": "string" - }, - "providerId": { - "pattern": "[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab]{1}[0-9a-f]{3}-[0-9a-f]{12}", - "type": "string" - }, - "jurisdiction": { - "type": "string", - "enum": [ - "al", - "ak", - "az", - "ar", - "ca", - "co", - "ct", - "de", - "dc", - "fl", - "ga", - "hi", - "id", - "il", - "in", - "ia", - "ks", - "ky", - "la", - "me", - "md", - "ma", - "mi", - "mn", - "ms", - "mo", - "mt", - "ne", - "nv", - "nh", - "nj", - "nm", - "ny", - "nc", - "nd", - "oh", - "ok", - "or", - "pa", - "pr", - "ri", - "sc", - "sd", - "tn", - "tx", - "ut", - "vt", - "va", - "vi", - "wa", - "wv", - "wi", - "wy" - ] - }, - "events": { - "type": "array", - "items": { - "required": [ - "createDate", - "dateOfUpdate", - "effectiveDate", - "type", - "updateType" - ], - "type": "object", - "properties": { - "note": { - "type": "string" - }, - "npdbCategories": { - "type": "array", - "description": "The categories of clinical privilege action for encumbrance events", - "items": { - "type": "string" - } - }, - "type": { - "type": "string", - "enum": [ - "privilegeUpdate" - ] - }, - "dateOfUpdate": { - "type": "string", - "format": "date-time" - }, - "effectiveDate": { - "pattern": "^[12]{1}[0-9]{3}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$", - "type": "string", - "format": "date" - }, - "updateType": { - "type": "string", - "enum": [ - "deactivation", - "expiration", - "issuance", - "other", - "renewal", - "encumbrance", - "homeJurisdictionChange", - "registration", - "lifting_encumbrance", - "licenseDeactivation", - "emailChange" - ] - }, - "createDate": { - "type": "string", - "format": "date-time" - } - } - } - } - } - }, - "SandboLicenVHq6DcHpnqp0": { - "required": [ - "transactionId" - ], - "type": "object", - "properties": { - "message": { - "type": "string", - "description": "A message about the transaction" - }, - "transactionId": { - "type": "string", - "description": "The transaction id for the purchase" - } - } - }, - "SandboLicen9VEPwkhZhKem": { - "type": "object", - "properties": {} - }, - "SandboLicenJlHz6gimzgVV": { - "required": [ - "birthMonthDay", - "compact", - "dateOfExpiration", - "dateOfUpdate", - "familyName", - "givenName", - "licenseJurisdiction", - "licenses", - "militaryAffiliations", - "privilegeJurisdictions", - "privileges", - "providerId", - "type" - ], - "type": "object", - "properties": { - "privileges": { - "type": "array", - "items": { - "required": [ - "administratorSetStatus", - "attestations", - "compact", - "compactTransactionId", - "dateOfExpiration", - "dateOfIssuance", - "dateOfRenewal", - "dateOfUpdate", - "history", - "jurisdiction", - "licenseJurisdiction", - "licenseType", - "privilegeId", - "providerId", - "status", - "type" - ], - "type": "object", - "properties": { - "investigationStatus": { - "type": "string", - "description": "Status indicating if the privilege is under investigation", - "enum": [ - "underInvestigation" - ] - }, - "licenseJurisdiction": { - "type": "string", - "enum": [ - "al", - "ak", - "az", - "ar", - "ca", - "co", - "ct", - "de", - "dc", - "fl", - "ga", - "hi", - "id", - "il", - "in", - "ia", - "ks", - "ky", - "la", - "me", - "md", - "ma", - "mi", - "mn", - "ms", - "mo", - "mt", - "ne", - "nv", - "nh", - "nj", - "nm", - "ny", - "nc", - "nd", - "oh", - "ok", - "or", - "pa", - "pr", - "ri", - "sc", - "sd", - "tn", - "tx", - "ut", - "vt", - "va", - "vi", - "wa", - "wv", - "wi", - "wy" - ] - }, - "compact": { - "type": "string", - "enum": [ - "aslp", - "octp", - "coun" - ] - }, - "jurisdiction": { - "type": "string", - "enum": [ - "al", - "ak", - "az", - "ar", - "ca", - "co", - "ct", - "de", - "dc", - "fl", - "ga", - "hi", - "id", - "il", - "in", - "ia", - "ks", - "ky", - "la", - "me", - "md", - "ma", - "mi", - "mn", - "ms", - "mo", - "mt", - "ne", - "nv", - "nh", - "nj", - "nm", - "ny", - "nc", - "nd", - "oh", - "ok", - "or", - "pa", - "pr", - "ri", - "sc", - "sd", - "tn", - "tx", - "ut", - "vt", - "va", - "vi", - "wa", - "wv", - "wi", - "wy" - ] - }, - "attestations": { - "type": "array", - "items": { - "required": [ - "attestationId", - "version" - ], - "type": "object", - "properties": { - "attestationId": { - "maxLength": 100, - "type": "string" - }, - "version": { - "maxLength": 100, - "type": "string" - } - } - } - }, - "investigations": { - "type": "array", - "items": { - "required": [ - "compact", - "creationDate", - "dateOfUpdate", - "investigationId", - "jurisdiction", - "licenseType", - "providerId", - "submittingUser", - "type" - ], - "type": "object", - "properties": { - "licenseType": { - "type": "string" - }, - "investigationId": { - "type": "string" - }, - "compact": { - "type": "string", - "enum": [ - "aslp", - "octp", - "coun" - ] - }, - "providerId": { - "pattern": "[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab]{1}[0-9a-f]{3}-[0-9a-f]{12}", - "type": "string" - }, - "jurisdiction": { - "type": "string", - "enum": [ - "al", - "ak", - "az", - "ar", - "ca", - "co", - "ct", - "de", - "dc", - "fl", - "ga", - "hi", - "id", - "il", - "in", - "ia", - "ks", - "ky", - "la", - "me", - "md", - "ma", - "mi", - "mn", - "ms", - "mo", - "mt", - "ne", - "nv", - "nh", - "nj", - "nm", - "ny", - "nc", - "nd", - "oh", - "ok", - "or", - "pa", - "pr", - "ri", - "sc", - "sd", - "tn", - "tx", - "ut", - "vt", - "va", - "vi", - "wa", - "wv", - "wi", - "wy" - ] - }, - "submittingUser": { - "type": "string" - }, - "type": { - "type": "string", - "enum": [ - "investigation" - ] - }, - "creationDate": { - "type": "string", - "format": "date-time" - }, - "dateOfUpdate": { - "type": "string", - "format": "date-time" - } - } - } - }, - "history": { - "type": "array", - "items": { - "required": [ - "compact", - "dateOfUpdate", - "jurisdiction", - "previous", - "type", - "updateType" - ], - "type": "object", - "properties": { - "removedValues": { - "type": "array", - "description": "List of field names that were present in the previous record but removed in the update", - "items": { - "type": "string" - } - }, - "licenseType": { - "type": "string", - "enum": [ - "audiologist", - "speech-language pathologist", - "occupational therapist", - "occupational therapy assistant", - "licensed professional counselor" - ] - }, - "compact": { - "type": "string", - "enum": [ - "aslp", - "octp", - "coun" - ] - }, - "previous": { - "required": [ - "administratorSetStatus", - "attestations", - "compactTransactionId", - "dateOfExpiration", - "dateOfIssuance", - "dateOfRenewal", - "dateOfUpdate", - "licenseJurisdiction", - "privilegeId" - ], - "type": "object", - "properties": { - "licenseJurisdiction": { - "type": "string", - "enum": [ - "al", - "ak", - "az", - "ar", - "ca", - "co", - "ct", - "de", - "dc", - "fl", - "ga", - "hi", - "id", - "il", - "in", - "ia", - "ks", - "ky", - "la", - "me", - "md", - "ma", - "mi", - "mn", - "ms", - "mo", - "mt", - "ne", - "nv", - "nh", - "nj", - "nm", - "ny", - "nc", - "nd", - "oh", - "ok", - "or", - "pa", - "pr", - "ri", - "sc", - "sd", - "tn", - "tx", - "ut", - "vt", - "va", - "vi", - "wa", - "wv", - "wi", - "wy" - ] - }, - "compact": { - "type": "string", - "enum": [ - "aslp", - "octp", - "coun" - ] - }, - "jurisdiction": { - "type": "string", - "enum": [ - "al", - "ak", - "az", - "ar", - "ca", - "co", - "ct", - "de", - "dc", - "fl", - "ga", - "hi", - "id", - "il", - "in", - "ia", - "ks", - "ky", - "la", - "me", - "md", - "ma", - "mi", - "mn", - "ms", - "mo", - "mt", - "ne", - "nv", - "nh", - "nj", - "nm", - "ny", - "nc", - "nd", - "oh", - "ok", - "or", - "pa", - "pr", - "ri", - "sc", - "sd", - "tn", - "tx", - "ut", - "vt", - "va", - "vi", - "wa", - "wv", - "wi", - "wy" - ] - }, - "attestations": { - "type": "array", - "items": { - "required": [ - "attestationId", - "version" - ], - "type": "object", - "properties": { - "attestationId": { - "maxLength": 100, - "type": "string" - }, - "version": { - "maxLength": 100, - "type": "string" - } - } - } - }, - "type": { - "type": "string", - "enum": [ - "privilege" - ] - }, - "compactTransactionId": { - "type": "string" - }, - "dateOfIssuance": { - "pattern": "^[12]{1}[0-9]{3}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$", - "type": "string", - "format": "date" - }, - "administratorSetStatus": { - "type": "string", - "enum": [ - "active", - "inactive" - ] - }, - "dateOfExpiration": { - "pattern": "^[12]{1}[0-9]{3}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$", - "type": "string", - "format": "date" - }, - "privilegeId": { - "type": "string" - }, - "providerId": { - "pattern": "[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab]{1}[0-9a-f]{3}-[0-9a-f]{12}", - "type": "string" - }, - "dateOfRenewal": { - "pattern": "^[12]{1}[0-9]{3}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$", - "type": "string", - "format": "date" - }, - "dateOfUpdate": { - "type": "string", - "format": "date-time" - }, - "status": { - "type": "string", - "enum": [ - "active", - "inactive" - ] - } - } - }, - "jurisdiction": { - "type": "string", - "enum": [ - "al", - "ak", - "az", - "ar", - "ca", - "co", - "ct", - "de", - "dc", - "fl", - "ga", - "hi", - "id", - "il", - "in", - "ia", - "ks", - "ky", - "la", - "me", - "md", - "ma", - "mi", - "mn", - "ms", - "mo", - "mt", - "ne", - "nv", - "nh", - "nj", - "nm", - "ny", - "nc", - "nd", - "oh", - "ok", - "or", - "pa", - "pr", - "ri", - "sc", - "sd", - "tn", - "tx", - "ut", - "vt", - "va", - "vi", - "wa", - "wv", - "wi", - "wy" - ] - }, - "updatedValues": { - "type": "object", - "properties": { - "licenseJurisdiction": { - "type": "string", - "enum": [ - "al", - "ak", - "az", - "ar", - "ca", - "co", - "ct", - "de", - "dc", - "fl", - "ga", - "hi", - "id", - "il", - "in", - "ia", - "ks", - "ky", - "la", - "me", - "md", - "ma", - "mi", - "mn", - "ms", - "mo", - "mt", - "ne", - "nv", - "nh", - "nj", - "nm", - "ny", - "nc", - "nd", - "oh", - "ok", - "or", - "pa", - "pr", - "ri", - "sc", - "sd", - "tn", - "tx", - "ut", - "vt", - "va", - "vi", - "wa", - "wv", - "wi", - "wy" - ] - }, - "compact": { - "type": "string", - "enum": [ - "aslp", - "octp", - "coun" - ] - }, - "jurisdiction": { - "type": "string", - "enum": [ - "al", - "ak", - "az", - "ar", - "ca", - "co", - "ct", - "de", - "dc", - "fl", - "ga", - "hi", - "id", - "il", - "in", - "ia", - "ks", - "ky", - "la", - "me", - "md", - "ma", - "mi", - "mn", - "ms", - "mo", - "mt", - "ne", - "nv", - "nh", - "nj", - "nm", - "ny", - "nc", - "nd", - "oh", - "ok", - "or", - "pa", - "pr", - "ri", - "sc", - "sd", - "tn", - "tx", - "ut", - "vt", - "va", - "vi", - "wa", - "wv", - "wi", - "wy" - ] - }, - "attestations": { - "type": "array", - "items": { - "required": [ - "attestationId", - "version" - ], - "type": "object", - "properties": { - "attestationId": { - "maxLength": 100, - "type": "string" - }, - "version": { - "maxLength": 100, - "type": "string" - } - } - } - }, - "type": { - "type": "string", - "enum": [ - "privilege" - ] - }, - "compactTransactionId": { - "type": "string" - }, - "dateOfIssuance": { - "pattern": "^[12]{1}[0-9]{3}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$", - "type": "string", - "format": "date" - }, - "administratorSetStatus": { - "type": "string", - "enum": [ - "active", - "inactive" - ] - }, - "dateOfExpiration": { - "pattern": "^[12]{1}[0-9]{3}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$", - "type": "string", - "format": "date" - }, - "privilegeId": { - "type": "string" - }, - "providerId": { - "pattern": "[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab]{1}[0-9a-f]{3}-[0-9a-f]{12}", - "type": "string" - }, - "dateOfRenewal": { - "pattern": "^[12]{1}[0-9]{3}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$", - "type": "string", - "format": "date" - }, - "dateOfUpdate": { - "type": "string", - "format": "date-time" - }, - "status": { - "type": "string", - "enum": [ - "active", - "inactive" - ] - } - } - }, - "type": { - "type": "string", - "enum": [ - "privilegeUpdate" - ] - }, - "dateOfUpdate": { - "type": "string", - "format": "date-time" - }, - "updateType": { - "type": "string", - "enum": [ - "deactivation", - "expiration", - "issuance", - "other", - "renewal", - "encumbrance", - "homeJurisdictionChange", - "registration", - "lifting_encumbrance", - "licenseDeactivation", - "emailChange" - ] - } - } - } - }, - "type": { - "type": "string", - "enum": [ - "privilege" - ] - }, - "compactTransactionId": { - "type": "string" - }, - "dateOfIssuance": { - "pattern": "^[12]{1}[0-9]{3}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$", - "type": "string", - "format": "date" - }, - "licenseType": { - "type": "string", - "enum": [ - "audiologist", - "speech-language pathologist", - "occupational therapist", - "occupational therapy assistant", - "licensed professional counselor" - ] - }, - "administratorSetStatus": { - "type": "string", - "enum": [ - "active", - "inactive" - ] - }, - "dateOfExpiration": { - "pattern": "^[12]{1}[0-9]{3}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$", - "type": "string", - "format": "date" - }, - "privilegeId": { - "type": "string" - }, - "providerId": { - "pattern": "[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab]{1}[0-9a-f]{3}-[0-9a-f]{12}", - "type": "string" - }, - "dateOfRenewal": { - "pattern": "^[12]{1}[0-9]{3}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$", - "type": "string", - "format": "date" - }, - "adverseActions": { - "type": "array", - "items": { - "required": [ - "actionAgainst", - "adverseActionId", - "compact", - "creationDate", - "dateOfUpdate", - "effectiveStartDate", - "encumbranceType", - "jurisdiction", - "licenseType", - "licenseTypeAbbreviation", - "providerId", - "type" - ], - "type": "object", - "properties": { - "clinicalPrivilegeActionCategories": { - "type": "array", - "description": "The categories of clinical privilege action", - "items": { - "type": "string" - } - }, - "compact": { - "type": "string", - "enum": [ - "aslp", - "octp", - "coun" - ] - }, - "jurisdiction": { - "type": "string", - "enum": [ - "al", - "ak", - "az", - "ar", - "ca", - "co", - "ct", - "de", - "dc", - "fl", - "ga", - "hi", - "id", - "il", - "in", - "ia", - "ks", - "ky", - "la", - "me", - "md", - "ma", - "mi", - "mn", - "ms", - "mo", - "mt", - "ne", - "nv", - "nh", - "nj", - "nm", - "ny", - "nc", - "nd", - "oh", - "ok", - "or", - "pa", - "pr", - "ri", - "sc", - "sd", - "tn", - "tx", - "ut", - "vt", - "va", - "vi", - "wa", - "wv", - "wi", - "wy" - ] - }, - "licenseTypeAbbreviation": { - "type": "string" - }, - "type": { - "type": "string", - "enum": [ - "adverseAction" - ] - }, - "creationDate": { - "pattern": "^[12]{1}[0-9]{3}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$", - "type": "string", - "format": "date" - }, - "actionAgainst": { - "type": "string" - }, - "licenseType": { - "type": "string" - }, - "providerId": { - "pattern": "[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab]{1}[0-9a-f]{3}-[0-9a-f]{12}", - "type": "string" - }, - "effectiveStartDate": { - "pattern": "^[12]{1}[0-9]{3}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$", - "type": "string", - "format": "date" - }, - "adverseActionId": { - "type": "string" - }, - "effectiveLiftDate": { - "pattern": "^[12]{1}[0-9]{3}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$", - "type": "string", - "format": "date" - }, - "encumbranceType": { - "type": "string" - }, - "liftingUser": { - "type": "string" - }, - "dateOfUpdate": { - "type": "string", - "format": "date-time" - } - } - } - }, - "dateOfUpdate": { - "type": "string", - "format": "date-time" - }, - "status": { - "type": "string", - "enum": [ - "active", - "inactive" - ] - } - } - } - }, - "licenseJurisdiction": { - "type": "string", - "enum": [ - "al", - "ak", - "az", - "ar", - "ca", - "co", - "ct", - "de", - "dc", - "fl", - "ga", - "hi", - "id", - "il", - "in", - "ia", - "ks", - "ky", - "la", - "me", - "md", - "ma", - "mi", - "mn", - "ms", - "mo", - "mt", - "ne", - "nv", - "nh", - "nj", - "nm", - "ny", - "nc", - "nd", - "oh", - "ok", - "or", - "pa", - "pr", - "ri", - "sc", - "sd", - "tn", - "tx", - "ut", - "vt", - "va", - "vi", - "wa", - "wv", - "wi", - "wy" - ] - }, - "compact": { - "type": "string", - "enum": [ - "aslp", - "octp", - "coun" - ] - }, - "type": { - "type": "string", - "enum": [ - "provider" - ] - }, - "suffix": { - "maxLength": 100, - "minLength": 1, - "type": "string" - }, - "dateOfExpiration": { - "pattern": "^[12]{1}[0-9]{3}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$", - "type": "string", - "format": "date" - }, - "providerId": { - "pattern": "[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab]{1}[0-9a-f]{3}-[0-9a-f]{12}", - "type": "string" - }, - "familyName": { - "maxLength": 100, - "minLength": 1, - "type": "string" - }, - "birthMonthDay": { - "pattern": "^[01]{1}[0-9]{1}-[0-3]{1}[0-9]{1}$", - "type": "string", - "format": "date" - }, - "compactConnectRegisteredEmailAddress": { - "maxLength": 100, - "minLength": 5, - "type": "string", - "format": "email" - }, - "npi": { - "pattern": "^[0-9]{10}$", - "type": "string" - }, - "givenName": { - "maxLength": 100, - "minLength": 1, - "type": "string" - }, - "compactEligibility": { - "type": "string", - "enum": [ - "eligible", - "ineligible" - ] - }, - "jurisdictionUploadedCompactEligibility": { - "type": "string", - "enum": [ - "eligible", - "ineligible" - ] - }, - "dateOfBirth": { - "pattern": "^[12]{1}[0-9]{3}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$", - "type": "string", - "format": "date" - }, - "jurisdictionUploadedLicenseStatus": { - "type": "string", - "enum": [ - "active", - "inactive" - ] - }, - "privilegeJurisdictions": { - "type": "array", - "items": { - "type": "string", - "enum": [ - "al", - "ak", - "az", - "ar", - "ca", - "co", - "ct", - "de", - "dc", - "fl", - "ga", - "hi", - "id", - "il", - "in", - "ia", - "ks", - "ky", - "la", - "me", - "md", - "ma", - "mi", - "mn", - "ms", - "mo", - "mt", - "ne", - "nv", - "nh", - "nj", - "nm", - "ny", - "nc", - "nd", - "oh", - "ok", - "or", - "pa", - "pr", - "ri", - "sc", - "sd", - "tn", - "tx", - "ut", - "vt", - "va", - "vi", - "wa", - "wv", - "wi", - "wy" - ] - } - }, - "currentHomeJurisdiction": { - "type": "string", - "description": "The current jurisdiction postal abbreviation if known.", - "enum": [ - "al", - "ak", - "az", - "ar", - "ca", - "co", - "ct", - "de", - "dc", - "fl", - "ga", - "hi", - "id", - "il", - "in", - "ia", - "ks", - "ky", - "la", - "me", - "md", - "ma", - "mi", - "mn", - "ms", - "mo", - "mt", - "ne", - "nv", - "nh", - "nj", - "nm", - "ny", - "nc", - "nd", - "oh", - "ok", - "or", - "pa", - "pr", - "ri", - "sc", - "sd", - "tn", - "tx", - "ut", - "vt", - "va", - "vi", - "wa", - "wv", - "wi", - "wy", - "other", - "unknown" - ] - }, - "militaryStatusNote": { - "maxLength": 5000, - "type": "string", - "description": "Optional note about the military status (typically for declines)" - }, - "licenses": { + "type": "object", + "properties": { + "privileges": { "type": "array", "items": { "required": [ + "administratorSetStatus", + "attestations", "compact", - "compactEligibility", + "compactTransactionId", "dateOfExpiration", "dateOfIssuance", "dateOfRenewal", "dateOfUpdate", - "familyName", - "givenName", "history", - "homeAddressCity", - "homeAddressPostalCode", - "homeAddressState", - "homeAddressStreet1", "jurisdiction", - "jurisdictionUploadedCompactEligibility", - "jurisdictionUploadedLicenseStatus", - "licenseStatus", + "licenseJurisdiction", "licenseType", - "middleName", + "privilegeId", "providerId", + "status", "type" ], "type": "object", "properties": { + "investigationStatus": { + "type": "string", + "description": "Status indicating if the privilege is under investigation", + "enum": [ + "underInvestigation" + ] + }, + "licenseJurisdiction": { + "type": "string", + "enum": [ + "al", + "ak", + "az", + "ar", + "ca", + "co", + "ct", + "de", + "dc", + "fl", + "ga", + "hi", + "id", + "il", + "in", + "ia", + "ks", + "ky", + "la", + "me", + "md", + "ma", + "mi", + "mn", + "ms", + "mo", + "mt", + "ne", + "nv", + "nh", + "nj", + "nm", + "ny", + "nc", + "nd", + "oh", + "ok", + "or", + "pa", + "pr", + "ri", + "sc", + "sd", + "tn", + "tx", + "ut", + "vt", + "va", + "vi", + "wa", + "wv", + "wi", + "wy" + ] + }, "compact": { "type": "string", "enum": [ @@ -9540,11 +7001,6 @@ "coun" ] }, - "homeAddressStreet2": { - "maxLength": 100, - "minLength": 1, - "type": "string" - }, "jurisdiction": { "type": "string", "enum": [ @@ -9603,10 +7059,25 @@ "wy" ] }, - "homeAddressStreet1": { - "maxLength": 100, - "minLength": 2, - "type": "string" + "attestations": { + "type": "array", + "items": { + "required": [ + "attestationId", + "version" + ], + "type": "object", + "properties": { + "attestationId": { + "maxLength": 100, + "type": "string" + }, + "version": { + "maxLength": 100, + "type": "string" + } + } + } }, "investigations": { "type": "array", @@ -9720,119 +7191,6 @@ } } }, - "type": { - "type": "string", - "enum": [ - "license-home" - ] - }, - "suffix": { - "maxLength": 100, - "minLength": 1, - "type": "string" - }, - "dateOfIssuance": { - "pattern": "^[12]{1}[0-9]{3}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$", - "type": "string", - "format": "date" - }, - "licenseType": { - "type": "string", - "enum": [ - "audiologist", - "speech-language pathologist", - "occupational therapist", - "occupational therapy assistant", - "licensed professional counselor" - ] - }, - "emailAddress": { - "maxLength": 100, - "minLength": 5, - "type": "string", - "format": "email" - }, - "dateOfExpiration": { - "pattern": "^[12]{1}[0-9]{3}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$", - "type": "string", - "format": "date" - }, - "homeAddressState": { - "maxLength": 100, - "minLength": 2, - "type": "string" - }, - "providerId": { - "pattern": "[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab]{1}[0-9a-f]{3}-[0-9a-f]{12}", - "type": "string" - }, - "dateOfRenewal": { - "pattern": "^[12]{1}[0-9]{3}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$", - "type": "string", - "format": "date" - }, - "familyName": { - "maxLength": 100, - "minLength": 1, - "type": "string" - }, - "homeAddressCity": { - "maxLength": 100, - "minLength": 2, - "type": "string" - }, - "licenseNumber": { - "maxLength": 100, - "minLength": 1, - "type": "string" - }, - "investigationStatus": { - "type": "string", - "description": "Status indicating if the license is under investigation", - "enum": [ - "underInvestigation" - ] - }, - "npi": { - "pattern": "^[0-9]{10}$", - "type": "string" - }, - "homeAddressPostalCode": { - "maxLength": 7, - "minLength": 5, - "type": "string" - }, - "compactEligibility": { - "type": "string", - "enum": [ - "eligible", - "ineligible" - ] - }, - "givenName": { - "maxLength": 100, - "minLength": 1, - "type": "string" - }, - "jurisdictionUploadedCompactEligibility": { - "type": "string", - "enum": [ - "eligible", - "ineligible" - ] - }, - "dateOfBirth": { - "pattern": "^[12]{1}[0-9]{3}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$", - "type": "string", - "format": "date" - }, - "jurisdictionUploadedLicenseStatus": { - "type": "string", - "enum": [ - "active", - "inactive" - ] - }, "history": { "type": "array", "items": { @@ -9873,99 +7231,193 @@ }, "previous": { "required": [ + "administratorSetStatus", + "attestations", + "compactTransactionId", "dateOfExpiration", "dateOfIssuance", "dateOfRenewal", - "familyName", - "givenName", - "homeAddressCity", - "homeAddressPostalCode", - "homeAddressState", - "homeAddressStreet1", - "jurisdictionUploadedCompactEligibility", - "jurisdictionUploadedLicenseStatus", - "middleName" + "dateOfUpdate", + "licenseJurisdiction", + "privilegeId" ], "type": "object", "properties": { - "homeAddressStreet2": { - "maxLength": 100, - "minLength": 1, - "type": "string" - }, - "npi": { - "pattern": "^[0-9]{10}$", - "type": "string" + "licenseJurisdiction": { + "type": "string", + "enum": [ + "al", + "ak", + "az", + "ar", + "ca", + "co", + "ct", + "de", + "dc", + "fl", + "ga", + "hi", + "id", + "il", + "in", + "ia", + "ks", + "ky", + "la", + "me", + "md", + "ma", + "mi", + "mn", + "ms", + "mo", + "mt", + "ne", + "nv", + "nh", + "nj", + "nm", + "ny", + "nc", + "nd", + "oh", + "ok", + "or", + "pa", + "pr", + "ri", + "sc", + "sd", + "tn", + "tx", + "ut", + "vt", + "va", + "vi", + "wa", + "wv", + "wi", + "wy" + ] }, - "homeAddressPostalCode": { - "maxLength": 7, - "minLength": 5, - "type": "string" + "compact": { + "type": "string", + "enum": [ + "aslp", + "octp", + "coun" + ] }, - "givenName": { - "maxLength": 100, - "minLength": 1, - "type": "string" + "jurisdiction": { + "type": "string", + "enum": [ + "al", + "ak", + "az", + "ar", + "ca", + "co", + "ct", + "de", + "dc", + "fl", + "ga", + "hi", + "id", + "il", + "in", + "ia", + "ks", + "ky", + "la", + "me", + "md", + "ma", + "mi", + "mn", + "ms", + "mo", + "mt", + "ne", + "nv", + "nh", + "nj", + "nm", + "ny", + "nc", + "nd", + "oh", + "ok", + "or", + "pa", + "pr", + "ri", + "sc", + "sd", + "tn", + "tx", + "ut", + "vt", + "va", + "vi", + "wa", + "wv", + "wi", + "wy" + ] }, - "homeAddressStreet1": { - "maxLength": 100, - "minLength": 2, - "type": "string" + "attestations": { + "type": "array", + "items": { + "required": [ + "attestationId", + "version" + ], + "type": "object", + "properties": { + "attestationId": { + "maxLength": 100, + "type": "string" + }, + "version": { + "maxLength": 100, + "type": "string" + } + } + } }, - "compactEligibility": { + "type": { "type": "string", "enum": [ - "eligible", - "ineligible" + "privilege" ] }, - "jurisdictionUploadedCompactEligibility": { - "type": "string", - "enum": [ - "eligible", - "ineligible" - ] + "compactTransactionId": { + "type": "string" }, - "dateOfBirth": { + "dateOfIssuance": { "pattern": "^[12]{1}[0-9]{3}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$", "type": "string", "format": "date" }, - "jurisdictionUploadedLicenseStatus": { + "administratorSetStatus": { "type": "string", "enum": [ "active", "inactive" ] }, - "suffix": { - "maxLength": 100, - "minLength": 1, - "type": "string" - }, - "dateOfIssuance": { - "pattern": "^[12]{1}[0-9]{3}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$", - "type": "string", - "format": "date" - }, - "emailAddress": { - "maxLength": 100, - "minLength": 5, - "type": "string", - "format": "email" - }, "dateOfExpiration": { "pattern": "^[12]{1}[0-9]{3}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$", "type": "string", "format": "date" }, - "phoneNumber": { - "pattern": "^\\+[0-9]{8,15}$", + "privilegeId": { "type": "string" }, - "homeAddressState": { - "maxLength": 100, - "minLength": 2, + "providerId": { + "pattern": "[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab]{1}[0-9a-f]{3}-[0-9a-f]{12}", "type": "string" }, "dateOfRenewal": { @@ -9973,37 +7425,16 @@ "type": "string", "format": "date" }, - "licenseStatus": { + "dateOfUpdate": { + "type": "string", + "format": "date-time" + }, + "status": { "type": "string", "enum": [ "active", "inactive" ] - }, - "familyName": { - "maxLength": 100, - "minLength": 1, - "type": "string" - }, - "homeAddressCity": { - "maxLength": 100, - "minLength": 2, - "type": "string" - }, - "licenseNumber": { - "maxLength": 100, - "minLength": 1, - "type": "string" - }, - "middleName": { - "maxLength": 100, - "minLength": 1, - "type": "string" - }, - "licenseStatusName": { - "maxLength": 100, - "minLength": 1, - "type": "string" } } }, @@ -10068,59 +7499,157 @@ "updatedValues": { "type": "object", "properties": { - "homeAddressStreet2": { - "maxLength": 100, - "minLength": 1, - "type": "string" - }, - "npi": { - "pattern": "^[0-9]{10}$", - "type": "string" - }, - "homeAddressPostalCode": { - "maxLength": 7, - "minLength": 5, - "type": "string" - }, - "givenName": { - "maxLength": 100, - "minLength": 1, - "type": "string" - }, - "homeAddressStreet1": { - "maxLength": 100, - "minLength": 2, - "type": "string" + "licenseJurisdiction": { + "type": "string", + "enum": [ + "al", + "ak", + "az", + "ar", + "ca", + "co", + "ct", + "de", + "dc", + "fl", + "ga", + "hi", + "id", + "il", + "in", + "ia", + "ks", + "ky", + "la", + "me", + "md", + "ma", + "mi", + "mn", + "ms", + "mo", + "mt", + "ne", + "nv", + "nh", + "nj", + "nm", + "ny", + "nc", + "nd", + "oh", + "ok", + "or", + "pa", + "pr", + "ri", + "sc", + "sd", + "tn", + "tx", + "ut", + "vt", + "va", + "vi", + "wa", + "wv", + "wi", + "wy" + ] }, - "compactEligibility": { + "compact": { "type": "string", "enum": [ - "eligible", - "ineligible" + "aslp", + "octp", + "coun" ] }, - "jurisdictionUploadedCompactEligibility": { + "jurisdiction": { "type": "string", "enum": [ - "eligible", - "ineligible" + "al", + "ak", + "az", + "ar", + "ca", + "co", + "ct", + "de", + "dc", + "fl", + "ga", + "hi", + "id", + "il", + "in", + "ia", + "ks", + "ky", + "la", + "me", + "md", + "ma", + "mi", + "mn", + "ms", + "mo", + "mt", + "ne", + "nv", + "nh", + "nj", + "nm", + "ny", + "nc", + "nd", + "oh", + "ok", + "or", + "pa", + "pr", + "ri", + "sc", + "sd", + "tn", + "tx", + "ut", + "vt", + "va", + "vi", + "wa", + "wv", + "wi", + "wy" ] }, - "dateOfBirth": { - "pattern": "^[12]{1}[0-9]{3}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$", - "type": "string", - "format": "date" + "attestations": { + "type": "array", + "items": { + "required": [ + "attestationId", + "version" + ], + "type": "object", + "properties": { + "attestationId": { + "maxLength": 100, + "type": "string" + }, + "version": { + "maxLength": 100, + "type": "string" + } + } + } }, - "jurisdictionUploadedLicenseStatus": { + "type": { "type": "string", "enum": [ - "active", - "inactive" + "privilege" ] }, - "suffix": { - "maxLength": 100, - "minLength": 1, + "compactTransactionId": { "type": "string" }, "dateOfIssuance": { @@ -10128,24 +7657,23 @@ "type": "string", "format": "date" }, - "emailAddress": { - "maxLength": 100, - "minLength": 5, + "administratorSetStatus": { "type": "string", - "format": "email" + "enum": [ + "active", + "inactive" + ] }, "dateOfExpiration": { "pattern": "^[12]{1}[0-9]{3}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$", "type": "string", "format": "date" }, - "phoneNumber": { - "pattern": "^\\+[0-9]{8,15}$", + "privilegeId": { "type": "string" }, - "homeAddressState": { - "maxLength": 100, - "minLength": 2, + "providerId": { + "pattern": "[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab]{1}[0-9a-f]{3}-[0-9a-f]{12}", "type": "string" }, "dateOfRenewal": { @@ -10153,44 +7681,23 @@ "type": "string", "format": "date" }, - "licenseStatus": { + "dateOfUpdate": { + "type": "string", + "format": "date-time" + }, + "status": { "type": "string", "enum": [ "active", "inactive" ] - }, - "familyName": { - "maxLength": 100, - "minLength": 1, - "type": "string" - }, - "homeAddressCity": { - "maxLength": 100, - "minLength": 2, - "type": "string" - }, - "licenseNumber": { - "maxLength": 100, - "minLength": 1, - "type": "string" - }, - "middleName": { - "maxLength": 100, - "minLength": 1, - "type": "string" - }, - "licenseStatusName": { - "maxLength": 100, - "minLength": 1, - "type": "string" } } }, "type": { "type": "string", "enum": [ - "licenseUpdate" + "privilegeUpdate" ] }, "dateOfUpdate": { @@ -10216,31 +7723,54 @@ } } }, - "ssnLastFour": { - "pattern": "^[0-9]{4}$", - "type": "string" + "type": { + "type": "string", + "enum": [ + "privilege" + ] }, - "phoneNumber": { - "pattern": "^\\+[0-9]{8,15}$", + "compactTransactionId": { "type": "string" }, - "licenseStatus": { + "dateOfIssuance": { + "pattern": "^[12]{1}[0-9]{3}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$", + "type": "string", + "format": "date" + }, + "licenseType": { + "type": "string", + "enum": [ + "audiologist", + "speech-language pathologist", + "occupational therapist", + "occupational therapy assistant", + "licensed professional counselor" + ] + }, + "administratorSetStatus": { "type": "string", "enum": [ "active", "inactive" ] }, - "middleName": { - "maxLength": 100, - "minLength": 1, + "dateOfExpiration": { + "pattern": "^[12]{1}[0-9]{3}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$", + "type": "string", + "format": "date" + }, + "privilegeId": { "type": "string" }, - "licenseStatusName": { - "maxLength": 100, - "minLength": 1, + "providerId": { + "pattern": "[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab]{1}[0-9a-f]{3}-[0-9a-f]{12}", "type": "string" }, + "dateOfRenewal": { + "pattern": "^[12]{1}[0-9]{3}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$", + "type": "string", + "format": "date" + }, "adverseActions": { "type": "array", "items": { @@ -10325,158 +7855,68 @@ "tx", "ut", "vt", - "va", - "vi", - "wa", - "wv", - "wi", - "wy" - ] - }, - "licenseTypeAbbreviation": { - "type": "string" - }, - "type": { - "type": "string", - "enum": [ - "adverseAction" - ] - }, - "creationDate": { - "pattern": "^[12]{1}[0-9]{3}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$", - "type": "string", - "format": "date" - }, - "actionAgainst": { - "type": "string" - }, - "licenseType": { - "type": "string" - }, - "providerId": { - "pattern": "[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab]{1}[0-9a-f]{3}-[0-9a-f]{12}", - "type": "string" - }, - "effectiveStartDate": { - "pattern": "^[12]{1}[0-9]{3}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$", - "type": "string", - "format": "date" - }, - "adverseActionId": { - "type": "string" - }, - "effectiveLiftDate": { - "pattern": "^[12]{1}[0-9]{3}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$", - "type": "string", - "format": "date" - }, - "encumbranceType": { - "type": "string" - }, - "liftingUser": { - "type": "string" - }, - "dateOfUpdate": { - "type": "string", - "format": "date-time" - } - } - } - }, - "dateOfUpdate": { - "type": "string", - "format": "date-time" - } - } - } - }, - "ssnLastFour": { - "pattern": "^[0-9]{4}$", - "type": "string" - }, - "militaryStatus": { - "type": "string", - "description": "Status of military affiliation on the provider record", - "enum": [ - "notApplicable", - "tentative", - "approved", - "declined" - ] - }, - "militaryAffiliations": { - "type": "array", - "items": { - "required": [ - "affiliationType", - "compact", - "dateOfUpdate", - "dateOfUpload", - "fileNames", - "providerId", - "status", - "type" - ], - "type": "object", - "properties": { - "dateOfUpload": { - "pattern": "^[12]{1}[0-9]{3}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$", - "type": "string", - "format": "date" - }, - "compact": { - "type": "string", - "enum": [ - "aslp", - "octp", - "coun" - ] - }, - "downloadLinks": { - "type": "array", - "items": { - "required": [ - "fileName", - "url" - ], - "type": "object", - "properties": { - "fileName": { + "va", + "vi", + "wa", + "wv", + "wi", + "wy" + ] + }, + "licenseTypeAbbreviation": { "type": "string" }, - "url": { + "type": { + "type": "string", + "enum": [ + "adverseAction" + ] + }, + "creationDate": { + "pattern": "^[12]{1}[0-9]{3}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$", + "type": "string", + "format": "date" + }, + "actionAgainst": { + "type": "string" + }, + "licenseType": { + "type": "string" + }, + "providerId": { + "pattern": "[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab]{1}[0-9a-f]{3}-[0-9a-f]{12}", + "type": "string" + }, + "effectiveStartDate": { + "pattern": "^[12]{1}[0-9]{3}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$", + "type": "string", + "format": "date" + }, + "adverseActionId": { + "type": "string" + }, + "effectiveLiftDate": { + "pattern": "^[12]{1}[0-9]{3}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$", + "type": "string", + "format": "date" + }, + "encumbranceType": { + "type": "string" + }, + "liftingUser": { "type": "string" + }, + "dateOfUpdate": { + "type": "string", + "format": "date-time" } } } }, - "providerId": { - "pattern": "[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab]{1}[0-9a-f]{3}-[0-9a-f]{12}", - "type": "string" - }, - "affiliationType": { - "type": "string", - "enum": [ - "militaryMember", - "militaryMemberSpouse" - ] - }, - "type": { - "type": "string", - "enum": [ - "militaryAffiliation" - ] - }, "dateOfUpdate": { "type": "string", "format": "date-time" }, - "fileNames": { - "type": "array", - "items": { - "type": "string" - } - }, "status": { "type": "string", "enum": [ @@ -10487,336 +7927,597 @@ } } }, - "licenseStatus": { + "licenseJurisdiction": { + "type": "string", + "enum": [ + "al", + "ak", + "az", + "ar", + "ca", + "co", + "ct", + "de", + "dc", + "fl", + "ga", + "hi", + "id", + "il", + "in", + "ia", + "ks", + "ky", + "la", + "me", + "md", + "ma", + "mi", + "mn", + "ms", + "mo", + "mt", + "ne", + "nv", + "nh", + "nj", + "nm", + "ny", + "nc", + "nd", + "oh", + "ok", + "or", + "pa", + "pr", + "ri", + "sc", + "sd", + "tn", + "tx", + "ut", + "vt", + "va", + "vi", + "wa", + "wv", + "wi", + "wy" + ] + }, + "compact": { + "type": "string", + "enum": [ + "aslp", + "octp", + "coun" + ] + }, + "type": { + "type": "string", + "enum": [ + "provider" + ] + }, + "suffix": { + "maxLength": 100, + "minLength": 1, + "type": "string" + }, + "dateOfExpiration": { + "pattern": "^[12]{1}[0-9]{3}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$", + "type": "string", + "format": "date" + }, + "providerId": { + "pattern": "[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab]{1}[0-9a-f]{3}-[0-9a-f]{12}", + "type": "string" + }, + "familyName": { + "maxLength": 100, + "minLength": 1, + "type": "string" + }, + "birthMonthDay": { + "pattern": "^[01]{1}[0-9]{1}-[0-3]{1}[0-9]{1}$", + "type": "string", + "format": "date" + }, + "compactConnectRegisteredEmailAddress": { + "maxLength": 100, + "minLength": 5, + "type": "string", + "format": "email" + }, + "npi": { + "pattern": "^[0-9]{10}$", + "type": "string" + }, + "givenName": { + "maxLength": 100, + "minLength": 1, + "type": "string" + }, + "compactEligibility": { + "type": "string", + "enum": [ + "eligible", + "ineligible" + ] + }, + "jurisdictionUploadedCompactEligibility": { + "type": "string", + "enum": [ + "eligible", + "ineligible" + ] + }, + "dateOfBirth": { + "pattern": "^[12]{1}[0-9]{3}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$", + "type": "string", + "format": "date" + }, + "jurisdictionUploadedLicenseStatus": { + "type": "string", + "enum": [ + "active", + "inactive" + ] + }, + "privilegeJurisdictions": { + "type": "array", + "items": { + "type": "string", + "enum": [ + "al", + "ak", + "az", + "ar", + "ca", + "co", + "ct", + "de", + "dc", + "fl", + "ga", + "hi", + "id", + "il", + "in", + "ia", + "ks", + "ky", + "la", + "me", + "md", + "ma", + "mi", + "mn", + "ms", + "mo", + "mt", + "ne", + "nv", + "nh", + "nj", + "nm", + "ny", + "nc", + "nd", + "oh", + "ok", + "or", + "pa", + "pr", + "ri", + "sc", + "sd", + "tn", + "tx", + "ut", + "vt", + "va", + "vi", + "wa", + "wv", + "wi", + "wy" + ] + } + }, + "currentHomeJurisdiction": { "type": "string", + "description": "The current jurisdiction postal abbreviation if known.", "enum": [ - "active", - "inactive" + "al", + "ak", + "az", + "ar", + "ca", + "co", + "ct", + "de", + "dc", + "fl", + "ga", + "hi", + "id", + "il", + "in", + "ia", + "ks", + "ky", + "la", + "me", + "md", + "ma", + "mi", + "mn", + "ms", + "mo", + "mt", + "ne", + "nv", + "nh", + "nj", + "nm", + "ny", + "nc", + "nd", + "oh", + "ok", + "or", + "pa", + "pr", + "ri", + "sc", + "sd", + "tn", + "tx", + "ut", + "vt", + "va", + "vi", + "wa", + "wv", + "wi", + "wy", + "other", + "unknown" ] }, - "middleName": { - "maxLength": 100, - "minLength": 1, - "type": "string" - }, - "dateOfUpdate": { - "type": "string", - "format": "date-time" - } - } - }, - "SandboLicenjfa9vGqBChQd": { - "required": [ - "upload" - ], - "type": "object", - "properties": { - "upload": { - "required": [ - "fields", - "url" - ], - "type": "object", - "properties": { - "fields": { - "type": "object", - "additionalProperties": { - "type": "string" - } - }, - "url": { - "type": "string" - } - } - } - } - }, - "SandboLicenyuZlweRzUTEW": { - "required": [ - "query" - ], - "type": "object", - "properties": { - "pagination": { - "type": "object", - "properties": { - "lastKey": { - "maxLength": 1024, - "minLength": 1, - "type": "string" - }, - "pageSize": { - "maximum": 100, - "minimum": 5, - "type": "integer" - } - }, - "additionalProperties": false - }, - "query": { - "type": "object", - "properties": { - "providerId": { - "pattern": "[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab]{1}[0-9a-f]{3}-[0-9a-f]{12}", - "type": "string", - "description": "Internal UUID for the provider" - }, - "jurisdiction": { - "type": "string", - "description": "Filter for providers with privilege/license in a jurisdiction", - "enum": [ - "al", - "ak", - "az", - "ar", - "ca", - "co", - "ct", - "de", - "dc", - "fl", - "ga", - "hi", - "id", - "il", - "in", - "ia", - "ks", - "ky", - "la", - "me", - "md", - "ma", - "mi", - "mn", - "ms", - "mo", - "mt", - "ne", - "nv", - "nh", - "nj", - "nm", - "ny", - "nc", - "nd", - "oh", - "ok", - "or", - "pa", - "pr", - "ri", - "sc", - "sd", - "tn", - "tx", - "ut", - "vt", - "va", - "vi", - "wa", - "wv", - "wi", - "wy" - ] - }, - "givenName": { - "maxLength": 100, - "type": "string", - "description": "Filter for providers with a given name (familyName is required if givenName is provided)" - }, - "familyName": { - "maxLength": 100, - "type": "string", - "description": "Filter for providers with a family name" - } - }, - "additionalProperties": false, - "description": "The query parameters" - }, - "sorting": { - "required": [ - "key" - ], - "type": "object", - "properties": { - "key": { - "type": "string", - "description": "The key to sort results by", - "enum": [ - "dateOfUpdate", - "familyName" - ] - }, - "direction": { - "type": "string", - "description": "Direction to sort results by", - "enum": [ - "ascending", - "descending" - ] - } - }, - "description": "How to sort results" - } - }, - "additionalProperties": false - }, - "SandboLicennv4iZSNKxEXN": { - "required": [ - "compact", - "dateOfUpdate", - "familyName", - "givenName", - "licenseJurisdiction", - "privilegeJurisdictions", - "providerId", - "type" - ], - "type": "object", - "properties": { - "privileges": { + "militaryStatusNote": { + "maxLength": 5000, + "type": "string", + "description": "Optional note about the military status (typically for declines)" + }, + "licenses": { "type": "array", "items": { "required": [ - "administratorSetStatus", "compact", + "compactEligibility", "dateOfExpiration", "dateOfIssuance", "dateOfRenewal", "dateOfUpdate", + "familyName", + "givenName", + "history", + "homeAddressCity", + "homeAddressPostalCode", + "homeAddressState", + "homeAddressStreet1", "jurisdiction", - "licenseJurisdiction", + "jurisdictionUploadedCompactEligibility", + "jurisdictionUploadedLicenseStatus", + "licenseStatus", "licenseType", - "privilegeId", + "middleName", "providerId", - "status", "type" ], "type": "object", "properties": { - "licenseJurisdiction": { + "compact": { + "type": "string", + "enum": [ + "aslp", + "octp", + "coun" + ] + }, + "homeAddressStreet2": { + "maxLength": 100, + "minLength": 1, + "type": "string" + }, + "jurisdiction": { + "type": "string", + "enum": [ + "al", + "ak", + "az", + "ar", + "ca", + "co", + "ct", + "de", + "dc", + "fl", + "ga", + "hi", + "id", + "il", + "in", + "ia", + "ks", + "ky", + "la", + "me", + "md", + "ma", + "mi", + "mn", + "ms", + "mo", + "mt", + "ne", + "nv", + "nh", + "nj", + "nm", + "ny", + "nc", + "nd", + "oh", + "ok", + "or", + "pa", + "pr", + "ri", + "sc", + "sd", + "tn", + "tx", + "ut", + "vt", + "va", + "vi", + "wa", + "wv", + "wi", + "wy" + ] + }, + "homeAddressStreet1": { + "maxLength": 100, + "minLength": 2, + "type": "string" + }, + "investigations": { + "type": "array", + "items": { + "required": [ + "compact", + "creationDate", + "dateOfUpdate", + "investigationId", + "jurisdiction", + "licenseType", + "providerId", + "submittingUser", + "type" + ], + "type": "object", + "properties": { + "licenseType": { + "type": "string" + }, + "investigationId": { + "type": "string" + }, + "compact": { + "type": "string", + "enum": [ + "aslp", + "octp", + "coun" + ] + }, + "providerId": { + "pattern": "[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab]{1}[0-9a-f]{3}-[0-9a-f]{12}", + "type": "string" + }, + "jurisdiction": { + "type": "string", + "enum": [ + "al", + "ak", + "az", + "ar", + "ca", + "co", + "ct", + "de", + "dc", + "fl", + "ga", + "hi", + "id", + "il", + "in", + "ia", + "ks", + "ky", + "la", + "me", + "md", + "ma", + "mi", + "mn", + "ms", + "mo", + "mt", + "ne", + "nv", + "nh", + "nj", + "nm", + "ny", + "nc", + "nd", + "oh", + "ok", + "or", + "pa", + "pr", + "ri", + "sc", + "sd", + "tn", + "tx", + "ut", + "vt", + "va", + "vi", + "wa", + "wv", + "wi", + "wy" + ] + }, + "submittingUser": { + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "investigation" + ] + }, + "creationDate": { + "type": "string", + "format": "date-time" + }, + "dateOfUpdate": { + "type": "string", + "format": "date-time" + } + } + } + }, + "type": { + "type": "string", + "enum": [ + "license-home" + ] + }, + "suffix": { + "maxLength": 100, + "minLength": 1, + "type": "string" + }, + "dateOfIssuance": { + "pattern": "^[12]{1}[0-9]{3}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$", + "type": "string", + "format": "date" + }, + "licenseType": { + "type": "string", + "enum": [ + "audiologist", + "speech-language pathologist", + "occupational therapist", + "occupational therapy assistant", + "licensed professional counselor" + ] + }, + "emailAddress": { + "maxLength": 100, + "minLength": 5, + "type": "string", + "format": "email" + }, + "dateOfExpiration": { + "pattern": "^[12]{1}[0-9]{3}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$", + "type": "string", + "format": "date" + }, + "homeAddressState": { + "maxLength": 100, + "minLength": 2, + "type": "string" + }, + "providerId": { + "pattern": "[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab]{1}[0-9a-f]{3}-[0-9a-f]{12}", + "type": "string" + }, + "dateOfRenewal": { + "pattern": "^[12]{1}[0-9]{3}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$", + "type": "string", + "format": "date" + }, + "familyName": { + "maxLength": 100, + "minLength": 1, + "type": "string" + }, + "homeAddressCity": { + "maxLength": 100, + "minLength": 2, + "type": "string" + }, + "licenseNumber": { + "maxLength": 100, + "minLength": 1, + "type": "string" + }, + "investigationStatus": { "type": "string", + "description": "Status indicating if the license is under investigation", "enum": [ - "al", - "ak", - "az", - "ar", - "ca", - "co", - "ct", - "de", - "dc", - "fl", - "ga", - "hi", - "id", - "il", - "in", - "ia", - "ks", - "ky", - "la", - "me", - "md", - "ma", - "mi", - "mn", - "ms", - "mo", - "mt", - "ne", - "nv", - "nh", - "nj", - "nm", - "ny", - "nc", - "nd", - "oh", - "ok", - "or", - "pa", - "pr", - "ri", - "sc", - "sd", - "tn", - "tx", - "ut", - "vt", - "va", - "vi", - "wa", - "wv", - "wi", - "wy" + "underInvestigation" ] }, - "compact": { + "npi": { + "pattern": "^[0-9]{10}$", + "type": "string" + }, + "homeAddressPostalCode": { + "maxLength": 7, + "minLength": 5, + "type": "string" + }, + "compactEligibility": { "type": "string", "enum": [ - "aslp", - "octp", - "coun" + "eligible", + "ineligible" ] }, - "jurisdiction": { + "givenName": { + "maxLength": 100, + "minLength": 1, + "type": "string" + }, + "jurisdictionUploadedCompactEligibility": { "type": "string", "enum": [ - "al", - "ak", - "az", - "ar", - "ca", - "co", - "ct", - "de", - "dc", - "fl", - "ga", - "hi", - "id", - "il", - "in", - "ia", - "ks", - "ky", - "la", - "me", - "md", - "ma", - "mi", - "mn", - "ms", - "mo", - "mt", - "ne", - "nv", - "nh", - "nj", - "nm", - "ny", - "nc", - "nd", - "oh", - "ok", - "or", - "pa", - "pr", - "ri", - "sc", - "sd", - "tn", - "tx", - "ut", - "vt", - "va", - "vi", - "wa", - "wv", - "wi", - "wy" + "eligible", + "ineligible" + ] + }, + "dateOfBirth": { + "pattern": "^[12]{1}[0-9]{3}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$", + "type": "string", + "format": "date" + }, + "jurisdictionUploadedLicenseStatus": { + "type": "string", + "enum": [ + "active", + "inactive" ] }, "history": { @@ -10826,15 +8527,19 @@ "compact", "dateOfUpdate", "jurisdiction", - "licenseType", "previous", - "providerId", "type", - "updateType", - "updatedValues" + "updateType" ], "type": "object", "properties": { + "removedValues": { + "type": "array", + "description": "List of field names that were present in the previous record but removed in the update", + "items": { + "type": "string" + } + }, "licenseType": { "type": "string", "enum": [ @@ -10855,87 +8560,99 @@ }, "previous": { "required": [ - "administratorSetStatus", "dateOfExpiration", "dateOfIssuance", "dateOfRenewal", - "dateOfUpdate", - "licenseJurisdiction", - "privilegeId" + "familyName", + "givenName", + "homeAddressCity", + "homeAddressPostalCode", + "homeAddressState", + "homeAddressStreet1", + "jurisdictionUploadedCompactEligibility", + "jurisdictionUploadedLicenseStatus", + "middleName" ], "type": "object", "properties": { - "administratorSetStatus": { + "homeAddressStreet2": { + "maxLength": 100, + "minLength": 1, + "type": "string" + }, + "npi": { + "pattern": "^[0-9]{10}$", + "type": "string" + }, + "homeAddressPostalCode": { + "maxLength": 7, + "minLength": 5, + "type": "string" + }, + "givenName": { + "maxLength": 100, + "minLength": 1, + "type": "string" + }, + "homeAddressStreet1": { + "maxLength": 100, + "minLength": 2, + "type": "string" + }, + "compactEligibility": { + "type": "string", + "enum": [ + "eligible", + "ineligible" + ] + }, + "jurisdictionUploadedCompactEligibility": { + "type": "string", + "enum": [ + "eligible", + "ineligible" + ] + }, + "dateOfBirth": { + "pattern": "^[12]{1}[0-9]{3}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$", + "type": "string", + "format": "date" + }, + "jurisdictionUploadedLicenseStatus": { + "type": "string", + "enum": [ + "active", + "inactive" + ] + }, + "suffix": { + "maxLength": 100, + "minLength": 1, + "type": "string" + }, + "dateOfIssuance": { + "pattern": "^[12]{1}[0-9]{3}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$", + "type": "string", + "format": "date" + }, + "emailAddress": { + "maxLength": 100, + "minLength": 5, "type": "string", - "enum": [ - "active", - "inactive" - ] + "format": "email" }, "dateOfExpiration": { "pattern": "^[12]{1}[0-9]{3}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$", "type": "string", "format": "date" }, - "licenseJurisdiction": { - "type": "string", - "enum": [ - "al", - "ak", - "az", - "ar", - "ca", - "co", - "ct", - "de", - "dc", - "fl", - "ga", - "hi", - "id", - "il", - "in", - "ia", - "ks", - "ky", - "la", - "me", - "md", - "ma", - "mi", - "mn", - "ms", - "mo", - "mt", - "ne", - "nv", - "nh", - "nj", - "nm", - "ny", - "nc", - "nd", - "oh", - "ok", - "or", - "pa", - "pr", - "ri", - "sc", - "sd", - "tn", - "tx", - "ut", - "vt", - "va", - "vi", - "wa", - "wv", - "wi", - "wy" - ] + "phoneNumber": { + "pattern": "^\\+[0-9]{8,15}$", + "type": "string" }, - "privilegeId": { + "homeAddressState": { + "maxLength": 100, + "minLength": 2, "type": "string" }, "dateOfRenewal": { @@ -10943,21 +8660,40 @@ "type": "string", "format": "date" }, - "dateOfIssuance": { - "pattern": "^[12]{1}[0-9]{3}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$", + "licenseStatus": { "type": "string", - "format": "date" + "enum": [ + "active", + "inactive" + ] }, - "dateOfUpdate": { - "type": "string", - "format": "date-time" + "familyName": { + "maxLength": 100, + "minLength": 1, + "type": "string" + }, + "homeAddressCity": { + "maxLength": 100, + "minLength": 2, + "type": "string" + }, + "licenseNumber": { + "maxLength": 100, + "minLength": 1, + "type": "string" + }, + "middleName": { + "maxLength": 100, + "minLength": 1, + "type": "string" + }, + "licenseStatusName": { + "maxLength": 100, + "minLength": 1, + "type": "string" } } }, - "providerId": { - "pattern": "[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab]{1}[0-9a-f]{3}-[0-9a-f]{12}", - "type": "string" - }, "jurisdiction": { "type": "string", "enum": [ @@ -11019,99 +8755,129 @@ "updatedValues": { "type": "object", "properties": { - "administratorSetStatus": { + "homeAddressStreet2": { + "maxLength": 100, + "minLength": 1, + "type": "string" + }, + "npi": { + "pattern": "^[0-9]{10}$", + "type": "string" + }, + "homeAddressPostalCode": { + "maxLength": 7, + "minLength": 5, + "type": "string" + }, + "givenName": { + "maxLength": 100, + "minLength": 1, + "type": "string" + }, + "homeAddressStreet1": { + "maxLength": 100, + "minLength": 2, + "type": "string" + }, + "compactEligibility": { "type": "string", "enum": [ - "active", - "inactive" + "eligible", + "ineligible" ] }, - "dateOfExpiration": { + "jurisdictionUploadedCompactEligibility": { + "type": "string", + "enum": [ + "eligible", + "ineligible" + ] + }, + "dateOfBirth": { "pattern": "^[12]{1}[0-9]{3}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$", "type": "string", "format": "date" }, - "licenseJurisdiction": { + "jurisdictionUploadedLicenseStatus": { "type": "string", "enum": [ - "al", - "ak", - "az", - "ar", - "ca", - "co", - "ct", - "de", - "dc", - "fl", - "ga", - "hi", - "id", - "il", - "in", - "ia", - "ks", - "ky", - "la", - "me", - "md", - "ma", - "mi", - "mn", - "ms", - "mo", - "mt", - "ne", - "nv", - "nh", - "nj", - "nm", - "ny", - "nc", - "nd", - "oh", - "ok", - "or", - "pa", - "pr", - "ri", - "sc", - "sd", - "tn", - "tx", - "ut", - "vt", - "va", - "vi", - "wa", - "wv", - "wi", - "wy" + "active", + "inactive" ] }, - "privilegeId": { + "suffix": { + "maxLength": 100, + "minLength": 1, "type": "string" }, - "dateOfRenewal": { + "dateOfIssuance": { "pattern": "^[12]{1}[0-9]{3}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$", "type": "string", "format": "date" }, - "dateOfIssuance": { + "emailAddress": { + "maxLength": 100, + "minLength": 5, + "type": "string", + "format": "email" + }, + "dateOfExpiration": { + "pattern": "^[12]{1}[0-9]{3}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$", + "type": "string", + "format": "date" + }, + "phoneNumber": { + "pattern": "^\\+[0-9]{8,15}$", + "type": "string" + }, + "homeAddressState": { + "maxLength": 100, + "minLength": 2, + "type": "string" + }, + "dateOfRenewal": { "pattern": "^[12]{1}[0-9]{3}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$", "type": "string", "format": "date" }, - "dateOfUpdate": { - "type": "string", - "format": "date-time" + "licenseStatus": { + "type": "string", + "enum": [ + "active", + "inactive" + ] + }, + "familyName": { + "maxLength": 100, + "minLength": 1, + "type": "string" + }, + "homeAddressCity": { + "maxLength": 100, + "minLength": 2, + "type": "string" + }, + "licenseNumber": { + "maxLength": 100, + "minLength": 1, + "type": "string" + }, + "middleName": { + "maxLength": 100, + "minLength": 1, + "type": "string" + }, + "licenseStatusName": { + "maxLength": 100, + "minLength": 1, + "type": "string" } } }, "type": { "type": "string", "enum": [ - "privilegeUpdate" + "licenseUpdate" ] }, "dateOfUpdate": { @@ -11137,51 +8903,31 @@ } } }, - "type": { - "type": "string", - "enum": [ - "privilege" - ] - }, - "dateOfIssuance": { - "pattern": "^[12]{1}[0-9]{3}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$", - "type": "string", - "format": "date" + "ssnLastFour": { + "pattern": "^[0-9]{4}$", + "type": "string" }, - "licenseType": { - "type": "string", - "enum": [ - "audiologist", - "speech-language pathologist", - "occupational therapist", - "occupational therapy assistant", - "licensed professional counselor" - ] + "phoneNumber": { + "pattern": "^\\+[0-9]{8,15}$", + "type": "string" }, - "administratorSetStatus": { + "licenseStatus": { "type": "string", "enum": [ "active", "inactive" ] }, - "dateOfExpiration": { - "pattern": "^[12]{1}[0-9]{3}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$", - "type": "string", - "format": "date" - }, - "privilegeId": { + "middleName": { + "maxLength": 100, + "minLength": 1, "type": "string" }, - "providerId": { - "pattern": "[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab]{1}[0-9a-f]{3}-[0-9a-f]{12}", + "licenseStatusName": { + "maxLength": 100, + "minLength": 1, "type": "string" }, - "dateOfRenewal": { - "pattern": "^[12]{1}[0-9]{3}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$", - "type": "string", - "format": "date" - }, "adverseActions": { "type": "array", "items": { @@ -11192,6 +8938,7 @@ "creationDate", "dateOfUpdate", "effectiveStartDate", + "encumbranceType", "jurisdiction", "licenseType", "licenseTypeAbbreviation", @@ -11200,8 +8947,12 @@ ], "type": "object", "properties": { - "licenseType": { - "type": "string" + "clinicalPrivilegeActionCategories": { + "type": "array", + "description": "The categories of clinical privilege action", + "items": { + "type": "string" + } }, "compact": { "type": "string", @@ -11211,10 +8962,6 @@ "coun" ] }, - "providerId": { - "pattern": "[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab]{1}[0-9a-f]{3}-[0-9a-f]{12}", - "type": "string" - }, "jurisdiction": { "type": "string", "enum": [ @@ -11273,34 +9020,47 @@ "wy" ] }, - "effectiveStartDate": { + "licenseTypeAbbreviation": { + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "adverseAction" + ] + }, + "creationDate": { "pattern": "^[12]{1}[0-9]{3}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$", "type": "string", "format": "date" }, - "licenseTypeAbbreviation": { + "actionAgainst": { "type": "string" }, - "adverseActionId": { + "licenseType": { "type": "string" }, - "effectiveLiftDate": { + "providerId": { + "pattern": "[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab]{1}[0-9a-f]{3}-[0-9a-f]{12}", + "type": "string" + }, + "effectiveStartDate": { "pattern": "^[12]{1}[0-9]{3}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$", "type": "string", "format": "date" }, - "type": { - "type": "string", - "enum": [ - "adverseAction" - ] + "adverseActionId": { + "type": "string" }, - "creationDate": { + "effectiveLiftDate": { "pattern": "^[12]{1}[0-9]{3}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$", "type": "string", "format": "date" }, - "actionAgainst": { + "encumbranceType": { + "type": "string" + }, + "liftingUser": { "type": "string" }, "dateOfUpdate": { @@ -11313,167 +9073,264 @@ "dateOfUpdate": { "type": "string", "format": "date-time" - }, - "status": { - "type": "string", - "enum": [ - "active", - "inactive" - ] } } } }, - "licenseJurisdiction": { + "ssnLastFour": { + "pattern": "^[0-9]{4}$", + "type": "string" + }, + "militaryStatus": { "type": "string", + "description": "Status of military affiliation on the provider record", "enum": [ - "al", - "ak", - "az", - "ar", - "ca", - "co", - "ct", - "de", - "dc", - "fl", - "ga", - "hi", - "id", - "il", - "in", - "ia", - "ks", - "ky", - "la", - "me", - "md", - "ma", - "mi", - "mn", - "ms", - "mo", - "mt", - "ne", - "nv", - "nh", - "nj", - "nm", - "ny", - "nc", - "nd", - "oh", - "ok", - "or", - "pa", - "pr", - "ri", - "sc", - "sd", - "tn", - "tx", - "ut", - "vt", - "va", - "vi", - "wa", - "wv", - "wi", - "wy" + "notApplicable", + "tentative", + "approved", + "declined" ] }, - "compact": { + "militaryAffiliations": { + "type": "array", + "items": { + "required": [ + "affiliationType", + "compact", + "dateOfUpdate", + "dateOfUpload", + "fileNames", + "providerId", + "status", + "type" + ], + "type": "object", + "properties": { + "dateOfUpload": { + "pattern": "^[12]{1}[0-9]{3}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$", + "type": "string", + "format": "date" + }, + "compact": { + "type": "string", + "enum": [ + "aslp", + "octp", + "coun" + ] + }, + "downloadLinks": { + "type": "array", + "items": { + "required": [ + "fileName", + "url" + ], + "type": "object", + "properties": { + "fileName": { + "type": "string" + }, + "url": { + "type": "string" + } + } + } + }, + "providerId": { + "pattern": "[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab]{1}[0-9a-f]{3}-[0-9a-f]{12}", + "type": "string" + }, + "affiliationType": { + "type": "string", + "enum": [ + "militaryMember", + "militaryMemberSpouse" + ] + }, + "type": { + "type": "string", + "enum": [ + "militaryAffiliation" + ] + }, + "dateOfUpdate": { + "type": "string", + "format": "date-time" + }, + "fileNames": { + "type": "array", + "items": { + "type": "string" + } + }, + "status": { + "type": "string", + "enum": [ + "active", + "inactive" + ] + } + } + } + }, + "licenseStatus": { "type": "string", "enum": [ - "aslp", - "octp", - "coun" + "active", + "inactive" ] }, - "npi": { - "pattern": "^[0-9]{10}$", - "type": "string" - }, - "givenName": { + "middleName": { "maxLength": 100, "minLength": 1, "type": "string" }, - "privilegeJurisdictions": { + "dateOfUpdate": { + "type": "string", + "format": "date-time" + } + } + }, + "TestALicendmGvxmbGgc3i": { + "required": [ + "clinicalPrivilegeActionCategories", + "encumbranceEffectiveDate", + "encumbranceType" + ], + "type": "object", + "properties": { + "encumbranceEffectiveDate": { + "pattern": "^[12]{1}[0-9]{3}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$", + "type": "string", + "description": "The effective date of the encumbrance", + "format": "date" + }, + "clinicalPrivilegeActionCategories": { "type": "array", + "description": "The categories of clinical privilege action", "items": { - "type": "string", - "enum": [ - "al", - "ak", - "az", - "ar", - "ca", - "co", - "ct", - "de", - "dc", - "fl", - "ga", - "hi", - "id", - "il", - "in", - "ia", - "ks", - "ky", - "la", - "me", - "md", - "ma", - "mi", - "mn", - "ms", - "mo", - "mt", - "ne", - "nv", - "nh", - "nj", - "nm", - "ny", - "nc", - "nd", - "oh", - "ok", - "or", - "pa", - "pr", - "ri", - "sc", - "sd", - "tn", - "tx", - "ut", - "vt", - "va", - "vi", - "wa", - "wv", - "wi", - "wy" - ] + "type": "string" } }, - "type": { + "encumbranceType": { "type": "string", + "description": "The type of encumbrance", "enum": [ - "provider" + "fine", + "reprimand", + "required supervision", + "completion of continuing education", + "public reprimand", + "probation", + "injunctive action", + "suspension", + "revocation", + "denial", + "surrender of license", + "modification of previous action-extension", + "modification of previous action-reduction", + "other monitoring", + "other adjudicated action not listed" + ] + } + }, + "additionalProperties": false, + "description": "Encumbrance data to create" + }, + "TestALicenWt4HR1PmbEfh": { + "required": [ + "newEmailAddress" + ], + "type": "object", + "properties": { + "newEmailAddress": { + "maxLength": 100, + "minLength": 5, + "type": "string", + "description": "The new email address to set for the provider", + "format": "email" + } + }, + "additionalProperties": false + }, + "TestALicenanIFyyDEX5VN": { + "type": "object", + "properties": { + "context": { + "type": "object", + "properties": { + "userId": { + "maxLength": 100, + "minLength": 1, + "type": "string", + "description": "Optional user ID for feature flag evaluation" + }, + "customAttributes": { + "type": "object", + "additionalProperties": { + "type": "string" + }, + "description": "Optional custom attributes for feature flag evaluation" + } + }, + "additionalProperties": false, + "description": "Optional context for feature flag evaluation" + } + }, + "additionalProperties": false + }, + "TestALicenp2MhBzi1qTM3": { + "required": [ + "compact", + "dob", + "email", + "familyName", + "givenName", + "jurisdiction", + "licenseType", + "partialSocial", + "token" + ], + "type": "object", + "properties": { + "licenseType": { + "maxLength": 500, + "type": "string", + "description": "Type of license", + "enum": [ + "audiologist", + "speech-language pathologist", + "occupational therapist", + "occupational therapy assistant", + "licensed professional counselor" ] }, - "suffix": { + "compact": { "maxLength": 100, - "minLength": 1, - "type": "string" + "type": "string", + "description": "Compact name" }, - "currentHomeJurisdiction": { + "dob": { + "pattern": "^[12]{1}[0-9]{3}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$", "type": "string", - "description": "The current jurisdiction postal abbreviation if known.", + "description": "Date of birth in YYYY-MM-DD format" + }, + "givenName": { + "maxLength": 200, + "type": "string", + "description": "Provider's given name" + }, + "familyName": { + "maxLength": 200, + "type": "string", + "description": "Provider's family name" + }, + "jurisdiction": { + "maxLength": 2, + "minLength": 2, + "type": "string", + "description": "Two-letter jurisdiction code", "enum": [ "al", "ak", @@ -11527,94 +9384,127 @@ "wa", "wv", "wi", - "wy", - "other", - "unknown" + "wy" ] }, - "providerId": { - "pattern": "[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab]{1}[0-9a-f]{3}-[0-9a-f]{12}", - "type": "string" - }, - "familyName": { - "maxLength": 100, - "minLength": 1, - "type": "string" + "partialSocial": { + "maxLength": 4, + "minLength": 4, + "type": "string", + "description": "Last 4 digits of SSN" }, - "middleName": { + "email": { "maxLength": 100, - "minLength": 1, - "type": "string" + "minLength": 5, + "type": "string", + "description": "Provider's email address", + "format": "email" }, - "dateOfUpdate": { + "token": { "type": "string", - "format": "date-time" + "description": "ReCAPTCHA token" } } }, - "SandboLicen5bneP2wVdz6l": { + "TestALicenE4fTNc2ubRUZ": { "required": [ - "compact", - "providerId", - "recaptchaToken", - "recoveryToken" + "affiliationType", + "dateOfUpdate", + "dateOfUpload", + "documentUploadFields", + "status" ], "type": "object", "properties": { - "compact": { + "dateOfUpload": { + "pattern": "^[12]{1}[0-9]{3}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$", + "type": "string", + "description": "The date the document was uploaded", + "format": "date" + }, + "affiliationType": { "type": "string", - "description": "Compact abbreviation", + "description": "The type of military affiliation", "enum": [ - "aslp", - "octp", - "coun" + "militaryMember", + "militaryMemberSpouse" ] }, - "providerId": { - "pattern": "[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab]{1}[0-9a-f]{3}-[0-9a-f]{12}", - "type": "string", - "description": "Provider UUID" + "fileNames": { + "type": "array", + "description": "List of military affiliation file names", + "items": { + "type": "string", + "description": "The name of the file being uploaded" + } }, - "recaptchaToken": { - "minLength": 1, + "dateOfUpdate": { "type": "string", - "description": "ReCAPTCHA token for verification" + "description": "The date the document was last updated", + "format": "date-time" }, - "recoveryToken": { - "maxLength": 256, - "minLength": 1, + "status": { "type": "string", - "description": "Recovery token from the email link" + "description": "The status of the military affiliation" + }, + "documentUploadFields": { + "type": "array", + "description": "The fields used to upload documents", + "items": { + "type": "object", + "properties": { + "fields": { + "type": "object", + "additionalProperties": { + "type": "string" + }, + "description": "The form fields used to upload the document" + }, + "url": { + "type": "string", + "description": "The url to upload the document to" + } + }, + "description": "The fields used to upload a specific document" + } } - }, - "additionalProperties": false + } }, - "SandboLicenRy0ye4gsVdf9": { + "TestALicenIdbN1DIAV4zt": { "required": [ - "compactAbbr", - "compactAdverseActionsNotificationEmails", - "compactCommissionFee", - "compactName", - "compactOperationsTeamEmails", - "compactSummaryReportNotificationEmails", - "configuredStates", - "licenseeRegistrationEnabled" + "compact", + "dateOfUpdate", + "familyName", + "givenName", + "licenseJurisdiction", + "privilegeJurisdictions", + "providerId", + "type" ], "type": "object", "properties": { - "configuredStates": { + "privileges": { "type": "array", - "description": "List of states that have submitted configurations and their live status", "items": { "required": [ - "isLive", - "postalAbbreviation" + "administratorSetStatus", + "compact", + "dateOfExpiration", + "dateOfIssuance", + "dateOfRenewal", + "dateOfUpdate", + "jurisdiction", + "licenseJurisdiction", + "licenseType", + "privilegeId", + "providerId", + "status", + "type" ], "type": "object", "properties": { - "postalAbbreviation": { + "licenseJurisdiction": { "type": "string", - "description": "The postal abbreviation of the jurisdiction", "enum": [ "al", "ak", @@ -11671,150 +9561,579 @@ "wy" ] }, - "isLive": { - "type": "boolean", - "description": "Whether the state is live and available for registrations." - } - } - } - }, - "compactCommissionFee": { - "required": [ - "feeAmount", - "feeType" - ], - "type": "object", - "properties": { - "feeAmount": { - "type": "number" - }, - "feeType": { - "type": "string", - "enum": [ - "FLAT_RATE" - ] - } - } - }, - "compactSummaryReportNotificationEmails": { - "type": "array", - "description": "List of email addresses for summary report notifications", - "items": { - "type": "string", - "format": "email" - } - }, - "compactAdverseActionsNotificationEmails": { - "type": "array", - "description": "List of email addresses for adverse actions notifications", - "items": { - "type": "string", - "format": "email" - } - }, - "licenseeRegistrationEnabled": { - "type": "boolean", - "description": "Denotes whether licensee registration is enabled" - }, - "compactAbbr": { - "type": "string", - "description": "The abbreviation of the compact" - }, - "transactionFeeConfiguration": { - "type": "object", - "properties": { - "licenseeCharges": { - "required": [ - "active", - "chargeAmount", - "chargeType" - ], - "type": "object", - "properties": { - "chargeType": { - "type": "string", - "description": "The type of transaction fee charge", - "enum": [ - "FLAT_FEE_PER_PRIVILEGE" - ] - }, - "active": { - "type": "boolean", - "description": "Whether the compact is charging licensees transaction fees" - }, - "chargeAmount": { - "type": "number", - "description": "The amount to charge per privilege purchased" + "compact": { + "type": "string", + "enum": [ + "aslp", + "octp", + "coun" + ] + }, + "jurisdiction": { + "type": "string", + "enum": [ + "al", + "ak", + "az", + "ar", + "ca", + "co", + "ct", + "de", + "dc", + "fl", + "ga", + "hi", + "id", + "il", + "in", + "ia", + "ks", + "ky", + "la", + "me", + "md", + "ma", + "mi", + "mn", + "ms", + "mo", + "mt", + "ne", + "nv", + "nh", + "nj", + "nm", + "ny", + "nc", + "nd", + "oh", + "ok", + "or", + "pa", + "pr", + "ri", + "sc", + "sd", + "tn", + "tx", + "ut", + "vt", + "va", + "vi", + "wa", + "wv", + "wi", + "wy" + ] + }, + "history": { + "type": "array", + "items": { + "required": [ + "compact", + "dateOfUpdate", + "jurisdiction", + "licenseType", + "previous", + "providerId", + "type", + "updateType", + "updatedValues" + ], + "type": "object", + "properties": { + "licenseType": { + "type": "string", + "enum": [ + "audiologist", + "speech-language pathologist", + "occupational therapist", + "occupational therapy assistant", + "licensed professional counselor" + ] + }, + "compact": { + "type": "string", + "enum": [ + "aslp", + "octp", + "coun" + ] + }, + "previous": { + "required": [ + "administratorSetStatus", + "dateOfExpiration", + "dateOfIssuance", + "dateOfRenewal", + "dateOfUpdate", + "licenseJurisdiction", + "privilegeId" + ], + "type": "object", + "properties": { + "administratorSetStatus": { + "type": "string", + "enum": [ + "active", + "inactive" + ] + }, + "dateOfExpiration": { + "pattern": "^[12]{1}[0-9]{3}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$", + "type": "string", + "format": "date" + }, + "licenseJurisdiction": { + "type": "string", + "enum": [ + "al", + "ak", + "az", + "ar", + "ca", + "co", + "ct", + "de", + "dc", + "fl", + "ga", + "hi", + "id", + "il", + "in", + "ia", + "ks", + "ky", + "la", + "me", + "md", + "ma", + "mi", + "mn", + "ms", + "mo", + "mt", + "ne", + "nv", + "nh", + "nj", + "nm", + "ny", + "nc", + "nd", + "oh", + "ok", + "or", + "pa", + "pr", + "ri", + "sc", + "sd", + "tn", + "tx", + "ut", + "vt", + "va", + "vi", + "wa", + "wv", + "wi", + "wy" + ] + }, + "privilegeId": { + "type": "string" + }, + "dateOfRenewal": { + "pattern": "^[12]{1}[0-9]{3}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$", + "type": "string", + "format": "date" + }, + "dateOfIssuance": { + "pattern": "^[12]{1}[0-9]{3}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$", + "type": "string", + "format": "date" + }, + "dateOfUpdate": { + "type": "string", + "format": "date-time" + } + } + }, + "providerId": { + "pattern": "[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab]{1}[0-9a-f]{3}-[0-9a-f]{12}", + "type": "string" + }, + "jurisdiction": { + "type": "string", + "enum": [ + "al", + "ak", + "az", + "ar", + "ca", + "co", + "ct", + "de", + "dc", + "fl", + "ga", + "hi", + "id", + "il", + "in", + "ia", + "ks", + "ky", + "la", + "me", + "md", + "ma", + "mi", + "mn", + "ms", + "mo", + "mt", + "ne", + "nv", + "nh", + "nj", + "nm", + "ny", + "nc", + "nd", + "oh", + "ok", + "or", + "pa", + "pr", + "ri", + "sc", + "sd", + "tn", + "tx", + "ut", + "vt", + "va", + "vi", + "wa", + "wv", + "wi", + "wy" + ] + }, + "updatedValues": { + "type": "object", + "properties": { + "administratorSetStatus": { + "type": "string", + "enum": [ + "active", + "inactive" + ] + }, + "dateOfExpiration": { + "pattern": "^[12]{1}[0-9]{3}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$", + "type": "string", + "format": "date" + }, + "licenseJurisdiction": { + "type": "string", + "enum": [ + "al", + "ak", + "az", + "ar", + "ca", + "co", + "ct", + "de", + "dc", + "fl", + "ga", + "hi", + "id", + "il", + "in", + "ia", + "ks", + "ky", + "la", + "me", + "md", + "ma", + "mi", + "mn", + "ms", + "mo", + "mt", + "ne", + "nv", + "nh", + "nj", + "nm", + "ny", + "nc", + "nd", + "oh", + "ok", + "or", + "pa", + "pr", + "ri", + "sc", + "sd", + "tn", + "tx", + "ut", + "vt", + "va", + "vi", + "wa", + "wv", + "wi", + "wy" + ] + }, + "privilegeId": { + "type": "string" + }, + "dateOfRenewal": { + "pattern": "^[12]{1}[0-9]{3}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$", + "type": "string", + "format": "date" + }, + "dateOfIssuance": { + "pattern": "^[12]{1}[0-9]{3}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$", + "type": "string", + "format": "date" + }, + "dateOfUpdate": { + "type": "string", + "format": "date-time" + } + } + }, + "type": { + "type": "string", + "enum": [ + "privilegeUpdate" + ] + }, + "dateOfUpdate": { + "type": "string", + "format": "date-time" + }, + "updateType": { + "type": "string", + "enum": [ + "deactivation", + "expiration", + "issuance", + "other", + "renewal", + "encumbrance", + "homeJurisdictionChange", + "registration", + "lifting_encumbrance", + "licenseDeactivation", + "emailChange" + ] + } + } } - } - } - } - }, - "compactName": { - "type": "string", - "description": "The full name of the compact" - }, - "compactOperationsTeamEmails": { - "type": "array", - "description": "List of email addresses for operations team notifications", - "items": { - "type": "string", - "format": "email" - } - } - } - }, - "SandboLicenmftfBe6vPEA8": { - "required": [ - "compact", - "dob", - "email", - "familyName", - "givenName", - "jurisdiction", - "licenseType", - "partialSocial", - "token" - ], - "type": "object", - "properties": { - "licenseType": { - "maxLength": 500, - "type": "string", - "description": "Type of license", - "enum": [ - "audiologist", - "speech-language pathologist", - "occupational therapist", - "occupational therapy assistant", - "licensed professional counselor" - ] - }, - "compact": { - "maxLength": 100, - "type": "string", - "description": "Compact name" - }, - "dob": { - "pattern": "^[12]{1}[0-9]{3}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$", - "type": "string", - "description": "Date of birth in YYYY-MM-DD format" - }, - "givenName": { - "maxLength": 200, - "type": "string", - "description": "Provider's given name" - }, - "familyName": { - "maxLength": 200, - "type": "string", - "description": "Provider's family name" + }, + "type": { + "type": "string", + "enum": [ + "privilege" + ] + }, + "dateOfIssuance": { + "pattern": "^[12]{1}[0-9]{3}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$", + "type": "string", + "format": "date" + }, + "licenseType": { + "type": "string", + "enum": [ + "audiologist", + "speech-language pathologist", + "occupational therapist", + "occupational therapy assistant", + "licensed professional counselor" + ] + }, + "administratorSetStatus": { + "type": "string", + "enum": [ + "active", + "inactive" + ] + }, + "dateOfExpiration": { + "pattern": "^[12]{1}[0-9]{3}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$", + "type": "string", + "format": "date" + }, + "privilegeId": { + "type": "string" + }, + "providerId": { + "pattern": "[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab]{1}[0-9a-f]{3}-[0-9a-f]{12}", + "type": "string" + }, + "dateOfRenewal": { + "pattern": "^[12]{1}[0-9]{3}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$", + "type": "string", + "format": "date" + }, + "adverseActions": { + "type": "array", + "items": { + "required": [ + "actionAgainst", + "adverseActionId", + "compact", + "creationDate", + "dateOfUpdate", + "effectiveStartDate", + "jurisdiction", + "licenseType", + "licenseTypeAbbreviation", + "providerId", + "type" + ], + "type": "object", + "properties": { + "licenseType": { + "type": "string" + }, + "compact": { + "type": "string", + "enum": [ + "aslp", + "octp", + "coun" + ] + }, + "providerId": { + "pattern": "[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab]{1}[0-9a-f]{3}-[0-9a-f]{12}", + "type": "string" + }, + "jurisdiction": { + "type": "string", + "enum": [ + "al", + "ak", + "az", + "ar", + "ca", + "co", + "ct", + "de", + "dc", + "fl", + "ga", + "hi", + "id", + "il", + "in", + "ia", + "ks", + "ky", + "la", + "me", + "md", + "ma", + "mi", + "mn", + "ms", + "mo", + "mt", + "ne", + "nv", + "nh", + "nj", + "nm", + "ny", + "nc", + "nd", + "oh", + "ok", + "or", + "pa", + "pr", + "ri", + "sc", + "sd", + "tn", + "tx", + "ut", + "vt", + "va", + "vi", + "wa", + "wv", + "wi", + "wy" + ] + }, + "effectiveStartDate": { + "pattern": "^[12]{1}[0-9]{3}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$", + "type": "string", + "format": "date" + }, + "licenseTypeAbbreviation": { + "type": "string" + }, + "adverseActionId": { + "type": "string" + }, + "effectiveLiftDate": { + "pattern": "^[12]{1}[0-9]{3}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$", + "type": "string", + "format": "date" + }, + "type": { + "type": "string", + "enum": [ + "adverseAction" + ] + }, + "creationDate": { + "pattern": "^[12]{1}[0-9]{3}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$", + "type": "string", + "format": "date" + }, + "actionAgainst": { + "type": "string" + }, + "dateOfUpdate": { + "type": "string", + "format": "date-time" + } + } + } + }, + "dateOfUpdate": { + "type": "string", + "format": "date-time" + }, + "status": { + "type": "string", + "enum": [ + "active", + "inactive" + ] + } + } + } }, - "jurisdiction": { - "maxLength": 2, - "minLength": 2, + "licenseJurisdiction": { "type": "string", - "description": "Two-letter jurisdiction code", "enum": [ "al", "ak", @@ -11871,117 +10190,27 @@ "wy" ] }, - "partialSocial": { - "maxLength": 4, - "minLength": 4, - "type": "string", - "description": "Last 4 digits of SSN" - }, - "email": { - "maxLength": 100, - "minLength": 5, - "type": "string", - "description": "Provider's email address", - "format": "email" - }, - "token": { - "type": "string", - "description": "ReCAPTCHA token" - } - } - }, - "SandboLicenSo5EafP3rZhM": { - "required": [ - "effectiveLiftDate" - ], - "type": "object", - "properties": { - "effectiveLiftDate": { - "pattern": "^[12]{1}[0-9]{3}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$", - "type": "string", - "description": "The effective date when the encumbrance will be lifted", - "format": "date" - } - }, - "additionalProperties": false - }, - "SandboLicendZ26vvlCKCbW": { - "required": [ - "attestations", - "licenseType", - "orderInformation", - "selectedJurisdictions" - ], - "type": "object", - "properties": { - "licenseType": { + "compact": { "type": "string", - "description": "The type of license the provider is purchasing a privilege for.", "enum": [ - "audiologist", - "speech-language pathologist", - "occupational therapist", - "occupational therapy assistant", - "licensed professional counselor" + "aslp", + "octp", + "coun" ] }, - "attestations": { - "type": "array", - "description": "List of attestations that the user has agreed to", - "items": { - "required": [ - "attestationId", - "version" - ], - "type": "object", - "properties": { - "attestationId": { - "maxLength": 100, - "type": "string", - "description": "The ID of the attestation" - }, - "version": { - "maxLength": 10, - "pattern": "^\\d+$", - "type": "string", - "description": "The version of the attestation" - } - } - } + "npi": { + "pattern": "^[0-9]{10}$", + "type": "string" }, - "orderInformation": { - "required": [ - "opaqueData" - ], - "type": "object", - "properties": { - "opaqueData": { - "required": [ - "dataDescriptor", - "dataValue" - ], - "type": "object", - "properties": { - "dataValue": { - "maxLength": 1000, - "type": "string", - "description": "The opaque data value token returned by Authorize.Net Accept UI" - }, - "dataDescriptor": { - "maxLength": 100, - "type": "string", - "description": "The opaque data descriptor returned by Authorize.Net Accept UI" - } - } - } - } + "givenName": { + "maxLength": 100, + "minLength": 1, + "type": "string" }, - "selectedJurisdictions": { - "maxLength": 20, + "privilegeJurisdictions": { "type": "array", "items": { "type": "string", - "description": "Jurisdictions a provider has selected to purchase privileges in.", "enum": [ "al", "ak", @@ -12038,400 +10267,340 @@ "wy" ] } + }, + "type": { + "type": "string", + "enum": [ + "provider" + ] + }, + "suffix": { + "maxLength": 100, + "minLength": 1, + "type": "string" + }, + "currentHomeJurisdiction": { + "type": "string", + "description": "The current jurisdiction postal abbreviation if known.", + "enum": [ + "al", + "ak", + "az", + "ar", + "ca", + "co", + "ct", + "de", + "dc", + "fl", + "ga", + "hi", + "id", + "il", + "in", + "ia", + "ks", + "ky", + "la", + "me", + "md", + "ma", + "mi", + "mn", + "ms", + "mo", + "mt", + "ne", + "nv", + "nh", + "nj", + "nm", + "ny", + "nc", + "nd", + "oh", + "ok", + "or", + "pa", + "pr", + "ri", + "sc", + "sd", + "tn", + "tx", + "ut", + "vt", + "va", + "vi", + "wa", + "wv", + "wi", + "wy", + "other", + "unknown" + ] + }, + "providerId": { + "pattern": "[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab]{1}[0-9a-f]{3}-[0-9a-f]{12}", + "type": "string" + }, + "familyName": { + "maxLength": 100, + "minLength": 1, + "type": "string" + }, + "middleName": { + "maxLength": 100, + "minLength": 1, + "type": "string" + }, + "dateOfUpdate": { + "type": "string", + "format": "date-time" } - } - }, - "SandboLicengqvhPQz7ywKX": { - "type": "object", - "properties": { - "permissions": { - "type": "object", - "additionalProperties": { - "type": "object", - "properties": { - "actions": { - "type": "object", - "properties": { - "readPrivate": { - "type": "boolean" - }, - "admin": { - "type": "boolean" - }, - "readSSN": { - "type": "boolean" - } - } - }, - "jurisdictions": { - "type": "object", - "additionalProperties": { - "type": "object", - "properties": { - "actions": { - "type": "object", - "properties": { - "readPrivate": { - "type": "boolean" - }, - "admin": { - "type": "boolean" - }, - "write": { - "type": "boolean" - }, - "readSSN": { - "type": "boolean" - } - }, - "additionalProperties": false - } - } - } - } - }, - "additionalProperties": false - } - } - }, - "additionalProperties": false + } }, - "SandboLicenaiHAuDR162f2": { - "required": [ - "items", - "pagination" - ], + "TestALicenEI4gvKnGhPzF": { "type": "object", "properties": { - "pagination": { + "attributes": { "type": "object", "properties": { - "prevLastKey": { - "maxLength": 1024, + "givenName": { + "maxLength": 100, "minLength": 1, - "type": "object" + "type": "string" }, - "lastKey": { - "maxLength": 1024, + "familyName": { + "maxLength": 100, "minLength": 1, - "type": "object" - }, - "pageSize": { - "maximum": 100, - "minimum": 5, - "type": "integer" + "type": "string" } - } - }, - "items": { - "maxLength": 100, - "type": "array", - "items": { - "type": "object", - "oneOf": [ - { - "required": [ - "compactAbbr", - "compactCommissionFee", - "compactName", - "isSandbox", - "paymentProcessorPublicFields", - "transactionFeeConfiguration", - "type" - ], - "type": "object", - "properties": { - "compactCommissionFee": { - "required": [ - "feeAmount", - "feeType" - ], - "type": "object", - "properties": { - "feeAmount": { - "type": "number" - }, - "feeType": { - "type": "string", - "enum": [ - "FLAT_RATE" - ] - } - } - }, - "compactAbbr": { - "type": "string", - "description": "The abbreviation of the compact" - }, - "paymentProcessorPublicFields": { - "required": [ - "apiLoginId", - "publicClientKey" - ], - "type": "object", - "properties": { - "publicClientKey": { - "type": "string", - "description": "The public client key for the payment processor" - }, - "apiLoginId": { - "type": "string", - "description": "The API login ID for the payment processor" - } - } - }, - "type": { - "type": "string", - "enum": [ - "compact" - ] - }, - "transactionFeeConfiguration": { - "required": [ - "licenseeCharges" - ], - "type": "object", - "properties": { - "licenseeCharges": { - "required": [ - "active", - "chargeAmount", - "chargeType" - ], - "type": "object", - "properties": { - "chargeType": { - "type": "string", - "description": "The type of transaction fee charge", - "enum": [ - "FLAT_FEE_PER_PRIVILEGE" - ] - }, - "active": { - "type": "boolean", - "description": "Whether the compact is charging licensees transaction fees" - }, - "chargeAmount": { - "type": "number", - "description": "The amount to charge per privilege purchased" - } - } - } - } - }, - "isSandbox": { - "type": "boolean", - "description": "Whether the compact is in sandbox mode" - }, - "compactName": { - "type": "string", - "description": "The full name of the compact" - } - } - }, - { - "required": [ - "jurisdictionName", - "jurisprudenceRequirements", - "postalAbbreviation", - "privilegeFees", - "type" - ], - "type": "object", - "properties": { - "privilegeFees": { - "type": "array", - "description": "The fees for the privileges", - "items": { - "required": [ - "amount", - "licenseTypeAbbreviation" - ], - "type": "object", - "properties": { - "amount": { - "type": "number" - }, - "militaryRate": { - "description": "Optional military rate for the privilege fee.", - "oneOf": [ - { - "minimum": 0, - "type": "number" - }, - null - ] - }, - "licenseTypeAbbreviation": { - "type": "string" - } - } - } - }, - "postalAbbreviation": { - "type": "string", - "description": "The postal abbreviation of the jurisdiction" - }, - "jurisprudenceRequirements": { - "required": [ - "required" - ], - "type": "object", - "properties": { - "linkToDocumentation": { - "description": "Optional link to jurisprudence documentation", - "oneOf": [ - { - "type": "string" - }, - null - ] - }, - "required": { - "type": "boolean", - "description": "Whether jurisprudence requirements exist" - } - } - }, - "jurisdictionName": { - "type": "string", - "description": "The name of the jurisdiction" - }, - "type": { - "type": "string", - "enum": [ - "jurisdiction" - ] - } - } - } - ] - } + }, + "additionalProperties": false } - } - }, - "SandboLicenv4avK8ok4P45": { - "required": [ - "clinicalPrivilegeActionCategories", - "encumbranceEffectiveDate", - "encumbranceType" - ], + }, + "additionalProperties": false + }, + "TestALicenbzfo0nLKzHWq": { "type": "object", "properties": { - "encumbranceEffectiveDate": { - "pattern": "^[12]{1}[0-9]{3}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$", + "dateCreated": { "type": "string", - "description": "The effective date of the encumbrance", - "format": "date" + "format": "date-time" }, - "clinicalPrivilegeActionCategories": { - "type": "array", - "description": "The categories of clinical privilege action", - "items": { - "type": "string" - } + "attestationId": { + "type": "string" }, - "encumbranceType": { + "compact": { "type": "string", - "description": "The type of encumbrance", "enum": [ - "fine", - "reprimand", - "required supervision", - "completion of continuing education", - "public reprimand", - "probation", - "injunctive action", - "suspension", - "revocation", - "denial", - "surrender of license", - "modification of previous action-extension", - "modification of previous action-reduction", - "other monitoring", - "other adjudicated action not listed" + "aslp", + "octp", + "coun" + ] + }, + "text": { + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "attestation" ] + }, + "locale": { + "type": "string" + }, + "version": { + "type": "string" + }, + "required": { + "type": "boolean" } - }, - "additionalProperties": false, - "description": "Encumbrance data to create" + } }, - "SandboLicenDTjDt3roB2dM": { + "TestALicenkkd2ugQD7XDr": { "required": [ - "pagination", - "providers" + "action" ], "type": "object", "properties": { - "pagination": { + "action": { + "type": "string", + "enum": [ + "close" + ] + }, + "encumbrance": { + "required": [ + "clinicalPrivilegeActionCategories", + "encumbranceEffectiveDate", + "encumbranceType" + ], "type": "object", "properties": { - "prevLastKey": { - "maxLength": 1024, - "minLength": 1, - "type": "object" + "encumbranceEffectiveDate": { + "pattern": "^[12]{1}[0-9]{3}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$", + "type": "string", + "description": "The effective date of the encumbrance", + "format": "date" }, - "lastKey": { - "maxLength": 1024, - "minLength": 1, - "type": "object" + "clinicalPrivilegeActionCategories": { + "type": "array", + "description": "The categories of clinical privilege action", + "items": { + "type": "string" + } }, - "pageSize": { - "maximum": 100, - "minimum": 5, - "type": "integer" + "encumbranceType": { + "type": "string", + "description": "The type of encumbrance", + "enum": [ + "fine", + "reprimand", + "required supervision", + "completion of continuing education", + "public reprimand", + "probation", + "injunctive action", + "suspension", + "revocation", + "denial", + "surrender of license", + "modification of previous action-extension", + "modification of previous action-reduction", + "other monitoring", + "other adjudicated action not listed" + ] } + }, + "additionalProperties": false, + "description": "Encumbrance data to create" + } + } + }, + "TestALicenY0wRQnCr1lHl": { + "required": [ + "attributes", + "permissions", + "status", + "userId" + ], + "type": "object", + "properties": { + "permissions": { + "type": "object", + "additionalProperties": { + "type": "object", + "properties": { + "actions": { + "type": "object", + "properties": { + "readPrivate": { + "type": "boolean" + }, + "admin": { + "type": "boolean" + }, + "readSSN": { + "type": "boolean" + } + } + }, + "jurisdictions": { + "type": "object", + "additionalProperties": { + "type": "object", + "properties": { + "actions": { + "type": "object", + "properties": { + "readPrivate": { + "type": "boolean" + }, + "admin": { + "type": "boolean" + }, + "write": { + "type": "boolean" + }, + "readSSN": { + "type": "boolean" + } + }, + "additionalProperties": false + } + } + } + } + }, + "additionalProperties": false } }, - "sorting": { + "attributes": { "required": [ - "key" + "email", + "familyName", + "givenName" ], "type": "object", "properties": { - "key": { - "type": "string", - "description": "The key to sort results by", - "enum": [ - "dateOfUpdate", - "familyName" - ] + "givenName": { + "maxLength": 100, + "minLength": 1, + "type": "string" }, - "direction": { - "type": "string", - "description": "Direction to sort results by", - "enum": [ - "ascending", - "descending" - ] + "familyName": { + "maxLength": 100, + "minLength": 1, + "type": "string" + }, + "email": { + "maxLength": 100, + "minLength": 5, + "type": "string" } }, - "description": "How to sort results" + "additionalProperties": false }, - "providers": { - "maxLength": 100, + "userId": { + "type": "string" + }, + "status": { + "type": "string", + "enum": [ + "active", + "inactive" + ] + } + }, + "additionalProperties": false + }, + "TestALicenW6ok160MLwS9": { + "required": [ + "compactAdverseActionsNotificationEmails", + "compactCommissionFee", + "compactOperationsTeamEmails", + "compactSummaryReportNotificationEmails", + "configuredStates", + "licenseeRegistrationEnabled" + ], + "type": "object", + "properties": { + "configuredStates": { "type": "array", + "description": "List of states that have submitted configurations and their live status", "items": { "required": [ - "birthMonthDay", - "compact", - "compactEligibility", - "dateOfExpiration", - "dateOfUpdate", - "familyName", - "givenName", - "jurisdictionUploadedCompactEligibility", - "jurisdictionUploadedLicenseStatus", - "licenseJurisdiction", - "licenseStatus", - "privilegeJurisdictions", - "providerId", - "type" + "isLive", + "postalAbbreviation" ], "type": "object", "properties": { - "licenseJurisdiction": { + "postalAbbreviation": { "type": "string", + "description": "The postal abbreviation of the jurisdiction", "enum": [ "al", "ak", @@ -12488,407 +10657,138 @@ "wy" ] }, - "compact": { - "type": "string", - "enum": [ - "aslp", - "octp", - "coun" - ] - }, - "npi": { - "pattern": "^[0-9]{10}$", - "type": "string" - }, - "givenName": { - "maxLength": 100, - "minLength": 1, - "type": "string" - }, - "compactEligibility": { - "type": "string", - "enum": [ - "eligible", - "ineligible" - ] - }, - "jurisdictionUploadedCompactEligibility": { - "type": "string", - "enum": [ - "eligible", - "ineligible" - ] - }, - "dateOfBirth": { - "pattern": "^[12]{1}[0-9]{3}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$", - "type": "string", - "format": "date" - }, - "jurisdictionUploadedLicenseStatus": { - "type": "string", - "enum": [ - "active", - "inactive" - ] - }, - "privilegeJurisdictions": { - "type": "array", - "items": { - "type": "string", - "enum": [ - "al", - "ak", - "az", - "ar", - "ca", - "co", - "ct", - "de", - "dc", - "fl", - "ga", - "hi", - "id", - "il", - "in", - "ia", - "ks", - "ky", - "la", - "me", - "md", - "ma", - "mi", - "mn", - "ms", - "mo", - "mt", - "ne", - "nv", - "nh", - "nj", - "nm", - "ny", - "nc", - "nd", - "oh", - "ok", - "or", - "pa", - "pr", - "ri", - "sc", - "sd", - "tn", - "tx", - "ut", - "vt", - "va", - "vi", - "wa", - "wv", - "wi", - "wy" - ] - } - }, - "type": { - "type": "string", - "enum": [ - "provider" - ] - }, - "suffix": { - "maxLength": 100, - "minLength": 1, - "type": "string" - }, - "currentHomeJurisdiction": { - "type": "string", - "description": "The current jurisdiction postal abbreviation if known.", - "enum": [ - "al", - "ak", - "az", - "ar", - "ca", - "co", - "ct", - "de", - "dc", - "fl", - "ga", - "hi", - "id", - "il", - "in", - "ia", - "ks", - "ky", - "la", - "me", - "md", - "ma", - "mi", - "mn", - "ms", - "mo", - "mt", - "ne", - "nv", - "nh", - "nj", - "nm", - "ny", - "nc", - "nd", - "oh", - "ok", - "or", - "pa", - "pr", - "ri", - "sc", - "sd", - "tn", - "tx", - "ut", - "vt", - "va", - "vi", - "wa", - "wv", - "wi", - "wy", - "other", - "unknown" - ] - }, - "militaryStatusNote": { - "maxLength": 5000, - "type": "string", - "description": "Optional note about the military status (typically for declines)" - }, - "ssnLastFour": { - "pattern": "^[0-9]{4}$", - "type": "string" - }, - "dateOfExpiration": { - "pattern": "^[12]{1}[0-9]{3}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$", - "type": "string", - "format": "date" - }, - "militaryStatus": { - "type": "string", - "description": "Status of military affiliation on the provider record", - "enum": [ - "notApplicable", - "tentative", - "approved", - "declined" - ] - }, - "providerId": { - "pattern": "[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab]{1}[0-9a-f]{3}-[0-9a-f]{12}", - "type": "string" - }, - "licenseStatus": { - "type": "string", - "enum": [ - "active", - "inactive" - ] - }, - "familyName": { - "maxLength": 100, - "minLength": 1, - "type": "string" - }, - "middleName": { - "maxLength": 100, - "minLength": 1, - "type": "string" - }, - "birthMonthDay": { - "pattern": "^[01]{1}[0-9]{1}-[0-3]{1}[0-9]{1}$", - "type": "string", - "format": "date" - }, - "compactConnectRegisteredEmailAddress": { - "maxLength": 100, - "minLength": 5, - "type": "string", - "format": "email" - }, - "dateOfUpdate": { - "type": "string", - "format": "date-time" + "isLive": { + "type": "boolean", + "description": "Whether the state is live and available for registrations." } - } - } - } - } - }, - "SandboLicenUONaWXXjz4K6": { - "required": [ - "action" - ], - "type": "object", - "properties": { - "action": { - "type": "string", - "enum": [ - "close" - ] + }, + "additionalProperties": false + } }, - "encumbrance": { + "compactCommissionFee": { "required": [ - "clinicalPrivilegeActionCategories", - "encumbranceEffectiveDate", - "encumbranceType" + "feeAmount", + "feeType" ], "type": "object", "properties": { - "encumbranceEffectiveDate": { - "pattern": "^[12]{1}[0-9]{3}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$", - "type": "string", - "description": "The effective date of the encumbrance", - "format": "date" - }, - "clinicalPrivilegeActionCategories": { - "type": "array", - "description": "The categories of clinical privilege action", - "items": { - "type": "string" - } + "feeAmount": { + "minimum": 0, + "type": "number" }, - "encumbranceType": { + "feeType": { "type": "string", - "description": "The type of encumbrance", "enum": [ - "fine", - "reprimand", - "required supervision", - "completion of continuing education", - "public reprimand", - "probation", - "injunctive action", - "suspension", - "revocation", - "denial", - "surrender of license", - "modification of previous action-extension", - "modification of previous action-reduction", - "other monitoring", - "other adjudicated action not listed" + "FLAT_RATE" ] } }, - "additionalProperties": false, - "description": "Encumbrance data to create" + "additionalProperties": false + }, + "compactSummaryReportNotificationEmails": { + "maxItems": 10, + "minItems": 1, + "uniqueItems": true, + "type": "array", + "description": "List of email addresses for summary report notifications", + "items": { + "type": "string", + "format": "email" + } + }, + "compactAdverseActionsNotificationEmails": { + "maxItems": 10, + "minItems": 1, + "uniqueItems": true, + "type": "array", + "description": "List of email addresses for adverse actions notifications", + "items": { + "type": "string", + "format": "email" + } + }, + "licenseeRegistrationEnabled": { + "type": "boolean", + "description": "Denotes whether licensee registration is enabled" + }, + "transactionFeeConfiguration": { + "type": "object", + "properties": { + "licenseeCharges": { + "required": [ + "active", + "chargeAmount", + "chargeType" + ], + "type": "object", + "properties": { + "chargeType": { + "type": "string", + "description": "The type of transaction fee charge", + "enum": [ + "FLAT_FEE_PER_PRIVILEGE" + ] + }, + "active": { + "type": "boolean", + "description": "Whether the compact is charging licensees transaction fees" + }, + "chargeAmount": { + "minimum": 0, + "type": "number", + "description": "The amount to charge per privilege purchased" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + "compactOperationsTeamEmails": { + "maxItems": 10, + "minItems": 1, + "uniqueItems": true, + "type": "array", + "description": "List of email addresses for operations team notifications", + "items": { + "type": "string", + "format": "email" + } } - } + }, + "additionalProperties": false }, - "SandboLicenG4CMW3C4eZNN": { + "TestALicenEMkErOzoY9dd": { "required": [ - "deactivationNote" + "message" ], "type": "object", "properties": { - "deactivationNote": { - "maxLength": 256, + "message": { "type": "string", - "description": "Note describing why the privilege is being deactivated" + "description": "A message about the request" } - }, - "additionalProperties": false + } }, - "SandboLicenPfWQpg9qdCvm": { + "TestALicenYI0jBXxNPcZZ": { "required": [ - "attributes", - "permissions" + "verificationCode" ], "type": "object", "properties": { - "permissions": { - "type": "object", - "additionalProperties": { - "type": "object", - "properties": { - "actions": { - "type": "object", - "properties": { - "readPrivate": { - "type": "boolean" - }, - "admin": { - "type": "boolean" - }, - "readSSN": { - "type": "boolean" - } - } - }, - "jurisdictions": { - "type": "object", - "additionalProperties": { - "type": "object", - "properties": { - "actions": { - "type": "object", - "properties": { - "readPrivate": { - "type": "boolean" - }, - "admin": { - "type": "boolean" - }, - "write": { - "type": "boolean" - }, - "readSSN": { - "type": "boolean" - } - }, - "additionalProperties": false - } - } - } - } - }, - "additionalProperties": false - } - }, - "attributes": { - "required": [ - "email", - "familyName", - "givenName" - ], - "type": "object", - "properties": { - "givenName": { - "maxLength": 100, - "minLength": 1, - "type": "string" - }, - "familyName": { - "maxLength": 100, - "minLength": 1, - "type": "string" - }, - "email": { - "maxLength": 100, - "minLength": 5, - "type": "string" - } - }, - "additionalProperties": false + "verificationCode": { + "pattern": "^[0-9]{4}$", + "type": "string", + "description": "4-digit verification code" } }, "additionalProperties": false }, - "SandboLicenxXSBED7FZUQN": { + "TestALicenRPKcrvQc1Mcd": { + "required": [ + "pagination", + "providers" + ], "type": "object", "properties": { "pagination": { @@ -12911,145 +10811,402 @@ } } }, - "users": { + "sorting": { + "required": [ + "key" + ], + "type": "object", + "properties": { + "key": { + "type": "string", + "description": "The key to sort results by", + "enum": [ + "dateOfUpdate", + "familyName" + ] + }, + "direction": { + "type": "string", + "description": "Direction to sort results by", + "enum": [ + "ascending", + "descending" + ] + } + }, + "description": "How to sort results" + }, + "providers": { + "maxLength": 100, "type": "array", "items": { "required": [ - "attributes", - "permissions", - "status", - "userId" + "birthMonthDay", + "compact", + "compactEligibility", + "dateOfExpiration", + "dateOfUpdate", + "familyName", + "givenName", + "jurisdictionUploadedCompactEligibility", + "jurisdictionUploadedLicenseStatus", + "licenseJurisdiction", + "licenseStatus", + "privilegeJurisdictions", + "providerId", + "type" ], "type": "object", "properties": { - "permissions": { - "type": "object", - "additionalProperties": { - "type": "object", - "properties": { - "actions": { - "type": "object", - "properties": { - "readPrivate": { - "type": "boolean" - }, - "admin": { - "type": "boolean" - }, - "readSSN": { - "type": "boolean" - } - } - }, - "jurisdictions": { - "type": "object", - "additionalProperties": { - "type": "object", - "properties": { - "actions": { - "type": "object", - "properties": { - "readPrivate": { - "type": "boolean" - }, - "admin": { - "type": "boolean" - }, - "write": { - "type": "boolean" - }, - "readSSN": { - "type": "boolean" - } - }, - "additionalProperties": false - } - } - } - } - }, - "additionalProperties": false + "licenseJurisdiction": { + "type": "string", + "enum": [ + "al", + "ak", + "az", + "ar", + "ca", + "co", + "ct", + "de", + "dc", + "fl", + "ga", + "hi", + "id", + "il", + "in", + "ia", + "ks", + "ky", + "la", + "me", + "md", + "ma", + "mi", + "mn", + "ms", + "mo", + "mt", + "ne", + "nv", + "nh", + "nj", + "nm", + "ny", + "nc", + "nd", + "oh", + "ok", + "or", + "pa", + "pr", + "ri", + "sc", + "sd", + "tn", + "tx", + "ut", + "vt", + "va", + "vi", + "wa", + "wv", + "wi", + "wy" + ] + }, + "compact": { + "type": "string", + "enum": [ + "aslp", + "octp", + "coun" + ] + }, + "npi": { + "pattern": "^[0-9]{10}$", + "type": "string" + }, + "givenName": { + "maxLength": 100, + "minLength": 1, + "type": "string" + }, + "compactEligibility": { + "type": "string", + "enum": [ + "eligible", + "ineligible" + ] + }, + "jurisdictionUploadedCompactEligibility": { + "type": "string", + "enum": [ + "eligible", + "ineligible" + ] + }, + "dateOfBirth": { + "pattern": "^[12]{1}[0-9]{3}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$", + "type": "string", + "format": "date" + }, + "jurisdictionUploadedLicenseStatus": { + "type": "string", + "enum": [ + "active", + "inactive" + ] + }, + "privilegeJurisdictions": { + "type": "array", + "items": { + "type": "string", + "enum": [ + "al", + "ak", + "az", + "ar", + "ca", + "co", + "ct", + "de", + "dc", + "fl", + "ga", + "hi", + "id", + "il", + "in", + "ia", + "ks", + "ky", + "la", + "me", + "md", + "ma", + "mi", + "mn", + "ms", + "mo", + "mt", + "ne", + "nv", + "nh", + "nj", + "nm", + "ny", + "nc", + "nd", + "oh", + "ok", + "or", + "pa", + "pr", + "ri", + "sc", + "sd", + "tn", + "tx", + "ut", + "vt", + "va", + "vi", + "wa", + "wv", + "wi", + "wy" + ] } }, - "attributes": { - "required": [ - "email", - "familyName", - "givenName" - ], - "type": "object", - "properties": { - "givenName": { - "maxLength": 100, - "minLength": 1, - "type": "string" - }, - "familyName": { - "maxLength": 100, - "minLength": 1, - "type": "string" - }, - "email": { - "maxLength": 100, - "minLength": 5, - "type": "string" - } - }, - "additionalProperties": false + "type": { + "type": "string", + "enum": [ + "provider" + ] + }, + "suffix": { + "maxLength": 100, + "minLength": 1, + "type": "string" + }, + "currentHomeJurisdiction": { + "type": "string", + "description": "The current jurisdiction postal abbreviation if known.", + "enum": [ + "al", + "ak", + "az", + "ar", + "ca", + "co", + "ct", + "de", + "dc", + "fl", + "ga", + "hi", + "id", + "il", + "in", + "ia", + "ks", + "ky", + "la", + "me", + "md", + "ma", + "mi", + "mn", + "ms", + "mo", + "mt", + "ne", + "nv", + "nh", + "nj", + "nm", + "ny", + "nc", + "nd", + "oh", + "ok", + "or", + "pa", + "pr", + "ri", + "sc", + "sd", + "tn", + "tx", + "ut", + "vt", + "va", + "vi", + "wa", + "wv", + "wi", + "wy", + "other", + "unknown" + ] + }, + "militaryStatusNote": { + "maxLength": 5000, + "type": "string", + "description": "Optional note about the military status (typically for declines)" }, - "userId": { + "ssnLastFour": { + "pattern": "^[0-9]{4}$", "type": "string" }, - "status": { + "dateOfExpiration": { + "pattern": "^[12]{1}[0-9]{3}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$", + "type": "string", + "format": "date" + }, + "militaryStatus": { + "type": "string", + "description": "Status of military affiliation on the provider record", + "enum": [ + "notApplicable", + "tentative", + "approved", + "declined" + ] + }, + "providerId": { + "pattern": "[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab]{1}[0-9a-f]{3}-[0-9a-f]{12}", + "type": "string" + }, + "licenseStatus": { "type": "string", "enum": [ "active", "inactive" ] + }, + "familyName": { + "maxLength": 100, + "minLength": 1, + "type": "string" + }, + "middleName": { + "maxLength": 100, + "minLength": 1, + "type": "string" + }, + "birthMonthDay": { + "pattern": "^[01]{1}[0-9]{1}-[0-3]{1}[0-9]{1}$", + "type": "string", + "format": "date" + }, + "compactConnectRegisteredEmailAddress": { + "maxLength": 100, + "minLength": 5, + "type": "string", + "format": "email" + }, + "dateOfUpdate": { + "type": "string", + "format": "date-time" } - }, - "additionalProperties": false + } } } - }, - "additionalProperties": false + } }, - "SandboLicenoBgekYzIk0Uy": { + "TestALicen6kP6aQ5vHZvs": { "required": [ - "affiliationType", - "fileNames" + "compact", + "dob", + "familyName", + "givenName", + "jurisdiction", + "licenseType", + "partialSocial", + "password", + "recaptchaToken", + "username" ], "type": "object", "properties": { - "affiliationType": { + "licenseType": { "type": "string", - "description": "The type of military affiliation", + "description": "Type of license", "enum": [ - "militaryMember", - "militaryMemberSpouse" + "audiologist", + "speech-language pathologist", + "occupational therapist", + "occupational therapy assistant", + "licensed professional counselor" ] }, - "fileNames": { - "type": "array", - "description": "List of military affiliation file names", - "items": { - "maxLength": 150, - "type": "string", - "description": "The name of the file being uploaded" - } - } - }, - "additionalProperties": false - }, - "SandboLicenJVUAEniGDz2F": { - "required": [ - "jurisdiction" - ], - "type": "object", - "properties": { + "password": { + "maxLength": 256, + "minLength": 12, + "type": "string", + "description": "Provider's current password" + }, + "compact": { + "type": "string", + "description": "Compact abbreviation", + "enum": [ + "aslp", + "octp", + "coun" + ] + }, + "dob": { + "pattern": "^[12]{1}[0-9]{3}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$", + "type": "string", + "description": "Date of birth in YYYY-MM-DD format", + "format": "date" + }, "jurisdiction": { "type": "string", - "description": "The jurisdiction postal abbreviation to set as home jurisdiction", + "description": "Two-letter jurisdiction code", "enum": [ "al", "ak", @@ -13103,88 +11260,50 @@ "wa", "wv", "wi", - "wy", - "other" + "wy" ] - } - }, - "additionalProperties": false - }, - "SandboLicenLnkOp52kvwLg": { - "type": "array", - "items": { - "required": [ - "compact", - "jurisdictionName", - "postalAbbreviation" - ], - "type": "object", - "properties": { - "postalAbbreviation": { - "type": "string", - "description": "The postal abbreviation of the jurisdiction" - }, - "compact": { - "type": "string" - }, - "jurisdictionName": { - "type": "string", - "description": "The name of the jurisdiction" - } - } - } - }, - "SandboLicenmMTXPta5fldR": { - "required": [ - "apiLoginId", - "processor", - "transactionKey" - ], - "type": "object", - "properties": { - "apiLoginId": { - "maxLength": 100, + }, + "givenName": { + "maxLength": 200, "minLength": 1, "type": "string", - "description": "The api login id for the payment processor" + "description": "Provider's given name" }, - "transactionKey": { - "maxLength": 100, + "familyName": { + "maxLength": 200, "minLength": 1, "type": "string", - "description": "The transaction key for the payment processor" + "description": "Provider's family name" }, - "processor": { + "recaptchaToken": { + "minLength": 1, "type": "string", - "description": "The type of payment processor", - "enum": [ - "authorize.net" - ] + "description": "ReCAPTCHA token for verification" + }, + "partialSocial": { + "pattern": "^[0-9]{4}$", + "type": "string", + "description": "Last 4 digits of SSN" + }, + "username": { + "maxLength": 100, + "minLength": 5, + "type": "string", + "description": "Provider's email address (username)", + "format": "email" } }, "additionalProperties": false - }, - "SandboLicen113ZVRfz1NW8": { - "required": [ - "enabled" - ], - "type": "object", - "properties": { - "enabled": { - "type": "boolean", - "description": "Whether the feature flag is enabled" - } - } } }, "securitySchemes": { - "SandboxAPIStackLicenseApiStaffUsersPoolAuthorizer14A84A9B": { + "TestBackendPipelineStackTestAPIStackLicenseApiProviderUsersPoolAuthorizer246E3F74": { "type": "apiKey", "name": "Authorization", "in": "header", "x-amazon-apigateway-authtype": "cognito_user_pools" }, - "SandboxAPIStackLicenseApiProviderUsersPoolAuthorizerEB7523BA": { + "TestBackendPipelineStackTestAPIStackLicenseApiStaffUsersPoolAuthorizer084A18F1": { "type": "apiKey", "name": "Authorization", "in": "header", diff --git a/backend/compact-connect/docs/internal/postman/postman-collection.json b/backend/compact-connect/docs/internal/postman/postman-collection.json index adfde0af90..eb30c002ec 100644 --- a/backend/compact-connect/docs/internal/postman/postman-collection.json +++ b/backend/compact-connect/docs/internal/postman/postman-collection.json @@ -10,7 +10,7 @@ "type": "bearer" }, "info": { - "_postman_id": "a9827ea4-b2f0-4c83-8c75-4c3baed1ee43", + "_postman_id": "20176b48-46af-47ba-949f-0de330a1bda7", "description": { "content": "", "type": "text/plain" @@ -401,7 +401,7 @@ "item": [ { "event": [], - "id": "ab5f5ee5-b84d-457d-a992-f11aaaf1234b", + "id": "911ff2af-a8ea-4415-a737-cde1288e5227", "name": "/v1/compacts/:compact", "protocolProfileBehavior": { "disableBodyPruning": true @@ -444,7 +444,7 @@ "response": [ { "_postman_previewlanguage": "json", - "body": "{\n \"compactAbbr\": \"\",\n \"compactAdverseActionsNotificationEmails\": [\n \"\",\n \"\"\n ],\n \"compactCommissionFee\": {\n \"feeAmount\": \"\",\n \"feeType\": \"FLAT_RATE\"\n },\n \"compactName\": \"\",\n \"compactOperationsTeamEmails\": [\n \"\",\n \"\"\n ],\n \"compactSummaryReportNotificationEmails\": [\n \"\",\n \"\"\n ],\n \"configuredStates\": [\n {\n \"isLive\": \"\",\n \"postalAbbreviation\": \"nh\"\n },\n {\n \"isLive\": \"\",\n \"postalAbbreviation\": \"nv\"\n }\n ],\n \"licenseeRegistrationEnabled\": \"\",\n \"transactionFeeConfiguration\": {\n \"licenseeCharges\": {\n \"active\": \"\",\n \"chargeAmount\": \"\",\n \"chargeType\": \"FLAT_FEE_PER_PRIVILEGE\"\n }\n }\n}", + "body": "{\n \"compactAbbr\": \"\",\n \"compactAdverseActionsNotificationEmails\": [\n \"\",\n \"\"\n ],\n \"compactCommissionFee\": {\n \"feeAmount\": \"\",\n \"feeType\": \"FLAT_RATE\"\n },\n \"compactName\": \"\",\n \"compactOperationsTeamEmails\": [\n \"\",\n \"\"\n ],\n \"compactSummaryReportNotificationEmails\": [\n \"\",\n \"\"\n ],\n \"configuredStates\": [\n {\n \"isLive\": \"\",\n \"postalAbbreviation\": \"va\"\n },\n {\n \"isLive\": \"\",\n \"postalAbbreviation\": \"ks\"\n }\n ],\n \"licenseeRegistrationEnabled\": \"\",\n \"transactionFeeConfiguration\": {\n \"licenseeCharges\": {\n \"active\": \"\",\n \"chargeAmount\": \"\",\n \"chargeType\": \"FLAT_FEE_PER_PRIVILEGE\"\n }\n }\n}", "code": 200, "cookie": [], "header": [ @@ -453,7 +453,7 @@ "value": "application/json" } ], - "id": "ffc3123b-50a8-4c36-8079-4701da829fb2", + "id": "1ad81555-7e7d-42f7-a809-ec9ed1afafb2", "name": "200 response", "originalRequest": { "body": {}, @@ -491,7 +491,7 @@ }, { "event": [], - "id": "38fbb806-4978-4f2f-a698-dfb90e5d465b", + "id": "943197f9-905c-45ef-ab79-c39731f89bb9", "name": "/v1/compacts/:compact", "protocolProfileBehavior": { "disableBodyPruning": true @@ -505,7 +505,7 @@ "language": "json" } }, - "raw": "{\n \"compactAdverseActionsNotificationEmails\": [\n \"\"\n ],\n \"compactCommissionFee\": {\n \"feeAmount\": \"\",\n \"feeType\": \"FLAT_RATE\"\n },\n \"compactOperationsTeamEmails\": [\n \"\"\n ],\n \"compactSummaryReportNotificationEmails\": [\n \"\"\n ],\n \"configuredStates\": [\n {\n \"isLive\": \"\",\n \"postalAbbreviation\": \"vi\"\n },\n {\n \"isLive\": \"\",\n \"postalAbbreviation\": \"ne\"\n }\n ],\n \"licenseeRegistrationEnabled\": \"\",\n \"transactionFeeConfiguration\": {\n \"licenseeCharges\": {\n \"active\": \"\",\n \"chargeAmount\": \"\",\n \"chargeType\": \"FLAT_FEE_PER_PRIVILEGE\"\n }\n }\n}" + "raw": "{\n \"compactAdverseActionsNotificationEmails\": [\n \"\"\n ],\n \"compactCommissionFee\": {\n \"feeAmount\": \"\",\n \"feeType\": \"FLAT_RATE\"\n },\n \"compactOperationsTeamEmails\": [\n \"\"\n ],\n \"compactSummaryReportNotificationEmails\": [\n \"\"\n ],\n \"configuredStates\": [\n {\n \"isLive\": \"\",\n \"postalAbbreviation\": \"nd\"\n },\n {\n \"isLive\": \"\",\n \"postalAbbreviation\": \"wi\"\n }\n ],\n \"licenseeRegistrationEnabled\": \"\",\n \"transactionFeeConfiguration\": {\n \"licenseeCharges\": {\n \"active\": \"\",\n \"chargeAmount\": \"\",\n \"chargeType\": \"FLAT_FEE_PER_PRIVILEGE\"\n }\n }\n}" }, "description": {}, "header": [ @@ -556,7 +556,7 @@ "value": "application/json" } ], - "id": "2e16ec66-8d31-4670-acf7-68d10898c562", + "id": "6d0d7a2f-718b-448f-91da-3573ddff54ce", "name": "200 response", "originalRequest": { "body": { @@ -567,7 +567,7 @@ "language": "json" } }, - "raw": "{\n \"compactAdverseActionsNotificationEmails\": [\n \"\"\n ],\n \"compactCommissionFee\": {\n \"feeAmount\": \"\",\n \"feeType\": \"FLAT_RATE\"\n },\n \"compactOperationsTeamEmails\": [\n \"\"\n ],\n \"compactSummaryReportNotificationEmails\": [\n \"\"\n ],\n \"configuredStates\": [\n {\n \"isLive\": \"\",\n \"postalAbbreviation\": \"vi\"\n },\n {\n \"isLive\": \"\",\n \"postalAbbreviation\": \"ne\"\n }\n ],\n \"licenseeRegistrationEnabled\": \"\",\n \"transactionFeeConfiguration\": {\n \"licenseeCharges\": {\n \"active\": \"\",\n \"chargeAmount\": \"\",\n \"chargeType\": \"FLAT_FEE_PER_PRIVILEGE\"\n }\n }\n}" + "raw": "{\n \"compactAdverseActionsNotificationEmails\": [\n \"\"\n ],\n \"compactCommissionFee\": {\n \"feeAmount\": \"\",\n \"feeType\": \"FLAT_RATE\"\n },\n \"compactOperationsTeamEmails\": [\n \"\"\n ],\n \"compactSummaryReportNotificationEmails\": [\n \"\"\n ],\n \"configuredStates\": [\n {\n \"isLive\": \"\",\n \"postalAbbreviation\": \"nd\"\n },\n {\n \"isLive\": \"\",\n \"postalAbbreviation\": \"wi\"\n }\n ],\n \"licenseeRegistrationEnabled\": \"\",\n \"transactionFeeConfiguration\": {\n \"licenseeCharges\": {\n \"active\": \"\",\n \"chargeAmount\": \"\",\n \"chargeType\": \"FLAT_FEE_PER_PRIVILEGE\"\n }\n }\n}" }, "header": [ { @@ -613,7 +613,7 @@ "item": [ { "event": [], - "id": "bc504781-860a-4391-b086-bd8fd22bf801", + "id": "6c1dab1f-c3d8-4383-97ab-36748d8e09fd", "name": "/v1/compacts/:compact/attestations/:attestationId", "protocolProfileBehavior": { "disableBodyPruning": true @@ -677,7 +677,7 @@ "value": "application/json" } ], - "id": "3883dec3-f380-4382-bc17-8a0a32ee0a5d", + "id": "545ef94f-9c77-4160-854e-34456e81f4e1", "name": "200 response", "originalRequest": { "body": {}, @@ -729,7 +729,7 @@ "item": [ { "event": [], - "id": "ae3ce956-63ee-44af-ba2c-50180670db85", + "id": "da520597-dbf1-4520-97fa-15cdf6ddc7db", "name": "/v1/compacts/:compact/credentials/payment-processor", "protocolProfileBehavior": { "disableBodyPruning": true @@ -796,7 +796,7 @@ "value": "application/json" } ], - "id": "e91a1a84-aaea-4e1b-b10b-31b7e5b3d33d", + "id": "2915fd12-87e0-4bc9-b5a2-849d8d179381", "name": "200 response", "originalRequest": { "body": { @@ -858,7 +858,7 @@ "item": [ { "event": [], - "id": "d7f4cc66-6f21-40b1-923d-ce2b92fe9b11", + "id": "583bb517-e733-4ded-992e-3d938e23a311", "name": "/v1/compacts/:compact/jurisdictions", "protocolProfileBehavior": { "disableBodyPruning": true @@ -911,7 +911,7 @@ "value": "application/json" } ], - "id": "df4850e4-8054-4f5e-8eee-3a388688281a", + "id": "70e21d4c-6b74-472a-b906-dd833b35bc7a", "name": "200 response", "originalRequest": { "body": {}, @@ -984,7 +984,7 @@ } } ], - "id": "f8d50a91-df46-459a-a12c-653da43e468b", + "id": "2f3d8b0a-146d-4012-a36f-54c97cdf9062", "name": "/v1/compacts/:compact/jurisdictions/:jurisdiction/licenses/bulk-upload", "protocolProfileBehavior": { "disableBodyPruning": true @@ -1041,7 +1041,7 @@ "response": [ { "_postman_previewlanguage": "json", - "body": "{\n \"upload\": {\n \"fields\": {\n \"consequat5b\": \"\"\n },\n \"url\": \"\"\n }\n}", + "body": "{\n \"upload\": {\n \"fields\": {\n \"veniam_8e0\": \"\",\n \"ade4\": \"\",\n \"do9a\": \"\"\n },\n \"url\": \"\"\n }\n}", "code": 200, "cookie": [], "header": [ @@ -1050,7 +1050,7 @@ "value": "application/json" } ], - "id": "f0bf2b8d-561b-4c50-94a4-0c08cd7e946a", + "id": "72752185-9ef6-4166-967e-06b3c1785f44", "name": "200 response", "originalRequest": { "body": {}, @@ -1110,7 +1110,7 @@ "item": [ { "event": [], - "id": "d85990e7-6376-4477-9b06-f4b17c5c4489", + "id": "09c68038-4db8-410d-a78b-fce4925840b7", "name": "/v1/compacts/:compact/providers/query", "protocolProfileBehavior": { "disableBodyPruning": true @@ -1124,7 +1124,7 @@ "language": "json" } }, - "raw": "{\n \"query\": {\n \"providerId\": \"8ab484ca-2bcd-4109-8bd1-732bba4947b7\",\n \"jurisdiction\": \"in\",\n \"givenName\": \"\",\n \"familyName\": \"\"\n },\n \"pagination\": {\n \"lastKey\": \"\",\n \"pageSize\": \"\"\n },\n \"sorting\": {\n \"key\": \"dateOfUpdate\",\n \"direction\": \"ascending\"\n }\n}" + "raw": "{\n \"query\": {\n \"providerId\": \"4c7413e0-fa19-4e6c-b44f-b4ac49ffd716\",\n \"jurisdiction\": \"ia\",\n \"givenName\": \"\",\n \"familyName\": \"\"\n },\n \"pagination\": {\n \"lastKey\": \"\",\n \"pageSize\": \"\"\n },\n \"sorting\": {\n \"key\": \"familyName\",\n \"direction\": \"ascending\"\n }\n}" }, "description": {}, "header": [ @@ -1168,7 +1168,7 @@ "response": [ { "_postman_previewlanguage": "json", - "body": "{\n \"pagination\": {\n \"prevLastKey\": {},\n \"lastKey\": {},\n \"pageSize\": \"\"\n },\n \"providers\": [\n {\n \"birthMonthDay\": \"07-22\",\n \"compact\": \"octp\",\n \"compactEligibility\": \"ineligible\",\n \"dateOfExpiration\": \"1187-11-07\",\n \"dateOfUpdate\": \"\",\n \"familyName\": \"\",\n \"givenName\": \"\",\n \"jurisdictionUploadedCompactEligibility\": \"ineligible\",\n \"jurisdictionUploadedLicenseStatus\": \"inactive\",\n \"licenseJurisdiction\": \"dc\",\n \"licenseStatus\": \"active\",\n \"privilegeJurisdictions\": [\n \"mn\",\n \"hi\"\n ],\n \"providerId\": \"a44b4853-1ddc-4a62-bb05-b7eae177385e\",\n \"type\": \"provider\",\n \"npi\": \"1914779016\",\n \"dateOfBirth\": \"1690-08-22\",\n \"suffix\": \"\",\n \"currentHomeJurisdiction\": \"ky\",\n \"ssnLastFour\": \"1423\",\n \"middleName\": \"\",\n \"compactConnectRegisteredEmailAddress\": \"\"\n },\n {\n \"birthMonthDay\": \"04-02\",\n \"compact\": \"aslp\",\n \"compactEligibility\": \"eligible\",\n \"dateOfExpiration\": \"2638-07-15\",\n \"dateOfUpdate\": \"\",\n \"familyName\": \"\",\n \"givenName\": \"\",\n \"jurisdictionUploadedCompactEligibility\": \"ineligible\",\n \"jurisdictionUploadedLicenseStatus\": \"inactive\",\n \"licenseJurisdiction\": \"id\",\n \"licenseStatus\": \"active\",\n \"privilegeJurisdictions\": [\n \"ar\",\n \"al\"\n ],\n \"providerId\": \"7bf12bd6-36ad-40ae-aa00-b7738968348a\",\n \"type\": \"provider\",\n \"npi\": \"9826340686\",\n \"dateOfBirth\": \"2326-11-31\",\n \"suffix\": \"\",\n \"currentHomeJurisdiction\": \"pr\",\n \"ssnLastFour\": \"9693\",\n \"middleName\": \"\",\n \"compactConnectRegisteredEmailAddress\": \"\"\n }\n ],\n \"sorting\": {\n \"key\": \"dateOfUpdate\",\n \"direction\": \"ascending\"\n }\n}", + "body": "{\n \"pagination\": {\n \"prevLastKey\": {},\n \"lastKey\": {},\n \"pageSize\": \"\"\n },\n \"providers\": [\n {\n \"birthMonthDay\": \"19-34\",\n \"compact\": \"octp\",\n \"compactEligibility\": \"ineligible\",\n \"dateOfExpiration\": \"1029-06-30\",\n \"dateOfUpdate\": \"\",\n \"familyName\": \"\",\n \"givenName\": \"\",\n \"jurisdictionUploadedCompactEligibility\": \"ineligible\",\n \"jurisdictionUploadedLicenseStatus\": \"inactive\",\n \"licenseJurisdiction\": \"sd\",\n \"licenseStatus\": \"active\",\n \"privilegeJurisdictions\": [\n \"la\",\n \"dc\"\n ],\n \"providerId\": \"f01ae711-3459-4340-85e9-b04a11902205\",\n \"type\": \"provider\",\n \"npi\": \"8256387531\",\n \"dateOfBirth\": \"2074-10-30\",\n \"suffix\": \"\",\n \"currentHomeJurisdiction\": \"other\",\n \"militaryStatusNote\": \"\",\n \"ssnLastFour\": \"4815\",\n \"militaryStatus\": \"tentative\",\n \"middleName\": \"\",\n \"compactConnectRegisteredEmailAddress\": \"\"\n },\n {\n \"birthMonthDay\": \"14-31\",\n \"compact\": \"aslp\",\n \"compactEligibility\": \"ineligible\",\n \"dateOfExpiration\": \"2041-10-03\",\n \"dateOfUpdate\": \"\",\n \"familyName\": \"\",\n \"givenName\": \"\",\n \"jurisdictionUploadedCompactEligibility\": \"ineligible\",\n \"jurisdictionUploadedLicenseStatus\": \"inactive\",\n \"licenseJurisdiction\": \"ks\",\n \"licenseStatus\": \"inactive\",\n \"privilegeJurisdictions\": [\n \"vi\",\n \"ak\"\n ],\n \"providerId\": \"3a796919-05d3-441d-ad1b-d45462d08ac1\",\n \"type\": \"provider\",\n \"npi\": \"0550824753\",\n \"dateOfBirth\": \"1756-12-18\",\n \"suffix\": \"\",\n \"currentHomeJurisdiction\": \"oh\",\n \"militaryStatusNote\": \"\",\n \"ssnLastFour\": \"0974\",\n \"militaryStatus\": \"declined\",\n \"middleName\": \"\",\n \"compactConnectRegisteredEmailAddress\": \"\"\n }\n ],\n \"sorting\": {\n \"key\": \"dateOfUpdate\",\n \"direction\": \"ascending\"\n }\n}", "code": 200, "cookie": [], "header": [ @@ -1177,7 +1177,7 @@ "value": "application/json" } ], - "id": "dc58ec63-fd5f-456e-873f-f7dd415097ab", + "id": "cbe13aea-9377-491a-a2fc-177641723a73", "name": "200 response", "originalRequest": { "body": { @@ -1188,7 +1188,7 @@ "language": "json" } }, - "raw": "{\n \"query\": {\n \"providerId\": \"8ab484ca-2bcd-4109-8bd1-732bba4947b7\",\n \"jurisdiction\": \"in\",\n \"givenName\": \"\",\n \"familyName\": \"\"\n },\n \"pagination\": {\n \"lastKey\": \"\",\n \"pageSize\": \"\"\n },\n \"sorting\": {\n \"key\": \"dateOfUpdate\",\n \"direction\": \"ascending\"\n }\n}" + "raw": "{\n \"query\": {\n \"providerId\": \"4c7413e0-fa19-4e6c-b44f-b4ac49ffd716\",\n \"jurisdiction\": \"ia\",\n \"givenName\": \"\",\n \"familyName\": \"\"\n },\n \"pagination\": {\n \"lastKey\": \"\",\n \"pageSize\": \"\"\n },\n \"sorting\": {\n \"key\": \"familyName\",\n \"direction\": \"ascending\"\n }\n}" }, "header": [ { @@ -1236,7 +1236,7 @@ "item": [ { "event": [], - "id": "07989aa5-de58-4467-bb09-a81ac12a5d07", + "id": "eed99ac2-c7ce-4b8a-9a0d-891346be6929", "name": "/v1/compacts/:compact/providers/:providerId", "protocolProfileBehavior": { "disableBodyPruning": true @@ -1291,7 +1291,7 @@ "response": [ { "_postman_previewlanguage": "json", - "body": "{\n \"birthMonthDay\": \"11-17\",\n \"compact\": \"aslp\",\n \"dateOfExpiration\": \"1016-04-15\",\n \"dateOfUpdate\": \"\",\n \"familyName\": \"\",\n \"givenName\": \"\",\n \"licenseJurisdiction\": \"mt\",\n \"licenses\": [\n {\n \"compact\": \"octp\",\n \"compactEligibility\": \"ineligible\",\n \"dateOfExpiration\": \"2174-01-13\",\n \"dateOfIssuance\": \"2125-08-07\",\n \"dateOfRenewal\": \"2884-03-30\",\n \"dateOfUpdate\": \"\",\n \"familyName\": \"\",\n \"givenName\": \"\",\n \"history\": [\n {\n \"compact\": \"coun\",\n \"dateOfUpdate\": \"\",\n \"jurisdiction\": \"ok\",\n \"previous\": {\n \"dateOfExpiration\": \"2765-01-27\",\n \"dateOfIssuance\": \"2200-04-07\",\n \"dateOfRenewal\": \"1960-12-31\",\n \"familyName\": \"\",\n \"givenName\": \"\",\n \"homeAddressCity\": \"\",\n \"homeAddressPostalCode\": \"\",\n \"homeAddressState\": \"\",\n \"homeAddressStreet1\": \"\",\n \"jurisdictionUploadedCompactEligibility\": \"eligible\",\n \"jurisdictionUploadedLicenseStatus\": \"active\",\n \"middleName\": \"\",\n \"homeAddressStreet2\": \"\",\n \"npi\": \"4323861062\",\n \"compactEligibility\": \"eligible\",\n \"dateOfBirth\": \"2605-04-10\",\n \"suffix\": \"\",\n \"emailAddress\": \"\",\n \"phoneNumber\": \"+876228646884801\",\n \"licenseStatus\": \"active\",\n \"licenseNumber\": \"\",\n \"licenseStatusName\": \"\"\n },\n \"type\": \"licenseUpdate\",\n \"updateType\": \"licenseDeactivation\",\n \"removedValues\": [\n \"\",\n \"\"\n ],\n \"licenseType\": \"speech-language pathologist\",\n \"updatedValues\": {\n \"homeAddressStreet2\": \"\",\n \"npi\": \"5362356702\",\n \"homeAddressPostalCode\": \"\",\n \"givenName\": \"\",\n \"homeAddressStreet1\": \"\",\n \"compactEligibility\": \"ineligible\",\n \"jurisdictionUploadedCompactEligibility\": \"eligible\",\n \"dateOfBirth\": \"2573-10-02\",\n \"jurisdictionUploadedLicenseStatus\": \"inactive\",\n \"suffix\": \"\",\n \"dateOfIssuance\": \"1792-01-14\",\n \"emailAddress\": \"\",\n \"dateOfExpiration\": \"1556-01-31\",\n \"phoneNumber\": \"+04732654\",\n \"homeAddressState\": \"\",\n \"dateOfRenewal\": \"1887-10-03\",\n \"licenseStatus\": \"active\",\n \"familyName\": \"\",\n \"homeAddressCity\": \"\",\n \"licenseNumber\": \"\",\n \"middleName\": \"\",\n \"licenseStatusName\": \"\"\n }\n },\n {\n \"compact\": \"coun\",\n \"dateOfUpdate\": \"\",\n \"jurisdiction\": \"in\",\n \"previous\": {\n \"dateOfExpiration\": \"2136-12-01\",\n \"dateOfIssuance\": \"1696-01-31\",\n \"dateOfRenewal\": \"1964-10-31\",\n \"familyName\": \"\",\n \"givenName\": \"\",\n \"homeAddressCity\": \"\",\n \"homeAddressPostalCode\": \"\",\n \"homeAddressState\": \"\",\n \"homeAddressStreet1\": \"\",\n \"jurisdictionUploadedCompactEligibility\": \"eligible\",\n \"jurisdictionUploadedLicenseStatus\": \"inactive\",\n \"middleName\": \"\",\n \"homeAddressStreet2\": \"\",\n \"npi\": \"4155100534\",\n \"compactEligibility\": \"eligible\",\n \"dateOfBirth\": \"2824-07-03\",\n \"suffix\": \"\",\n \"emailAddress\": \"\",\n \"phoneNumber\": \"+38146683\",\n \"licenseStatus\": \"active\",\n \"licenseNumber\": \"\",\n \"licenseStatusName\": \"\"\n },\n \"type\": \"licenseUpdate\",\n \"updateType\": \"deactivation\",\n \"removedValues\": [\n \"\",\n \"\"\n ],\n \"licenseType\": \"speech-language pathologist\",\n \"updatedValues\": {\n \"homeAddressStreet2\": \"\",\n \"npi\": \"8589103256\",\n \"homeAddressPostalCode\": \"\",\n \"givenName\": \"\",\n \"homeAddressStreet1\": \"\",\n \"compactEligibility\": \"ineligible\",\n \"jurisdictionUploadedCompactEligibility\": \"eligible\",\n \"dateOfBirth\": \"1671-10-13\",\n \"jurisdictionUploadedLicenseStatus\": \"active\",\n \"suffix\": \"\",\n \"dateOfIssuance\": \"2218-12-11\",\n \"emailAddress\": \"\",\n \"dateOfExpiration\": \"2539-10-14\",\n \"phoneNumber\": \"+86007188095\",\n \"homeAddressState\": \"\",\n \"dateOfRenewal\": \"1897-01-08\",\n \"licenseStatus\": \"inactive\",\n \"familyName\": \"\",\n \"homeAddressCity\": \"\",\n \"licenseNumber\": \"\",\n \"middleName\": \"\",\n \"licenseStatusName\": \"\"\n }\n }\n ],\n \"homeAddressCity\": \"\",\n \"homeAddressPostalCode\": \"\",\n \"homeAddressState\": \"\",\n \"homeAddressStreet1\": \"\",\n \"jurisdiction\": \"mn\",\n \"jurisdictionUploadedCompactEligibility\": \"ineligible\",\n \"jurisdictionUploadedLicenseStatus\": \"active\",\n \"licenseStatus\": \"inactive\",\n \"licenseType\": \"occupational therapy assistant\",\n \"middleName\": \"\",\n \"providerId\": \"cfdbcbbc-6c72-4deb-8651-2fd9915f5573\",\n \"type\": \"license-home\",\n \"homeAddressStreet2\": \"\",\n \"investigations\": [\n {\n \"compact\": \"coun\",\n \"creationDate\": \"\",\n \"dateOfUpdate\": \"\",\n \"investigationId\": \"\",\n \"jurisdiction\": \"me\",\n \"licenseType\": \"\",\n \"providerId\": \"63b94041-8f0b-43c5-971a-478833b5d6e6\",\n \"submittingUser\": \"\",\n \"type\": \"investigation\"\n },\n {\n \"compact\": \"coun\",\n \"creationDate\": \"\",\n \"dateOfUpdate\": \"\",\n \"investigationId\": \"\",\n \"jurisdiction\": \"ny\",\n \"licenseType\": \"\",\n \"providerId\": \"969c9a49-d226-443b-aaa1-dcd93df0300a\",\n \"submittingUser\": \"\",\n \"type\": \"investigation\"\n }\n ],\n \"suffix\": \"\",\n \"emailAddress\": \"\",\n \"licenseNumber\": \"\",\n \"investigationStatus\": \"underInvestigation\",\n \"npi\": \"9667390556\",\n \"dateOfBirth\": \"2014-10-28\",\n \"ssnLastFour\": \"5961\",\n \"phoneNumber\": \"+215555457776743\",\n \"licenseStatusName\": \"\",\n \"adverseActions\": [\n {\n \"actionAgainst\": \"\",\n \"adverseActionId\": \"\",\n \"compact\": \"octp\",\n \"creationDate\": \"2979-12-30\",\n \"dateOfUpdate\": \"\",\n \"effectiveStartDate\": \"2803-07-04\",\n \"encumbranceType\": \"\",\n \"jurisdiction\": \"me\",\n \"licenseType\": \"\",\n \"licenseTypeAbbreviation\": \"\",\n \"providerId\": \"ea82b231-5b5a-41a1-bd61-170eda210d44\",\n \"type\": \"adverseAction\",\n \"clinicalPrivilegeActionCategories\": [\n \"\",\n \"\"\n ],\n \"effectiveLiftDate\": \"1792-11-04\",\n \"clinicalPrivilegeActionCategory\": \"\",\n \"liftingUser\": \"\"\n },\n {\n \"actionAgainst\": \"\",\n \"adverseActionId\": \"\",\n \"compact\": \"aslp\",\n \"creationDate\": \"2045-12-16\",\n \"dateOfUpdate\": \"\",\n \"effectiveStartDate\": \"2301-05-09\",\n \"encumbranceType\": \"\",\n \"jurisdiction\": \"id\",\n \"licenseType\": \"\",\n \"licenseTypeAbbreviation\": \"\",\n \"providerId\": \"8d7a80d6-0190-4b0a-8563-1fb067203716\",\n \"type\": \"adverseAction\",\n \"clinicalPrivilegeActionCategories\": [\n \"\",\n \"\"\n ],\n \"effectiveLiftDate\": \"1125-04-31\",\n \"clinicalPrivilegeActionCategory\": \"\",\n \"liftingUser\": \"\"\n }\n ]\n },\n {\n \"compact\": \"octp\",\n \"compactEligibility\": \"ineligible\",\n \"dateOfExpiration\": \"1160-11-31\",\n \"dateOfIssuance\": \"2576-11-31\",\n \"dateOfRenewal\": \"2046-09-25\",\n \"dateOfUpdate\": \"\",\n \"familyName\": \"\",\n \"givenName\": \"\",\n \"history\": [\n {\n \"compact\": \"octp\",\n \"dateOfUpdate\": \"\",\n \"jurisdiction\": \"md\",\n \"previous\": {\n \"dateOfExpiration\": \"1355-01-03\",\n \"dateOfIssuance\": \"2864-04-16\",\n \"dateOfRenewal\": \"1651-04-11\",\n \"familyName\": \"\",\n \"givenName\": \"\",\n \"homeAddressCity\": \"\",\n \"homeAddressPostalCode\": \"\",\n \"homeAddressState\": \"\",\n \"homeAddressStreet1\": \"\",\n \"jurisdictionUploadedCompactEligibility\": \"eligible\",\n \"jurisdictionUploadedLicenseStatus\": \"inactive\",\n \"middleName\": \"\",\n \"homeAddressStreet2\": \"\",\n \"npi\": \"7235513969\",\n \"compactEligibility\": \"eligible\",\n \"dateOfBirth\": \"2108-12-30\",\n \"suffix\": \"\",\n \"emailAddress\": \"\",\n \"phoneNumber\": \"+9730063408781\",\n \"licenseStatus\": \"active\",\n \"licenseNumber\": \"\",\n \"licenseStatusName\": \"\"\n },\n \"type\": \"licenseUpdate\",\n \"updateType\": \"expiration\",\n \"removedValues\": [\n \"\",\n \"\"\n ],\n \"licenseType\": \"speech-language pathologist\",\n \"updatedValues\": {\n \"homeAddressStreet2\": \"\",\n \"npi\": \"4979399694\",\n \"homeAddressPostalCode\": \"\",\n \"givenName\": \"\",\n \"homeAddressStreet1\": \"\",\n \"compactEligibility\": \"ineligible\",\n \"jurisdictionUploadedCompactEligibility\": \"eligible\",\n \"dateOfBirth\": \"1169-04-20\",\n \"jurisdictionUploadedLicenseStatus\": \"inactive\",\n \"suffix\": \"\",\n \"dateOfIssuance\": \"2610-03-30\",\n \"emailAddress\": \"\",\n \"dateOfExpiration\": \"1235-06-10\",\n \"phoneNumber\": \"+0624416018\",\n \"homeAddressState\": \"\",\n \"dateOfRenewal\": \"2120-08-29\",\n \"licenseStatus\": \"active\",\n \"familyName\": \"\",\n \"homeAddressCity\": \"\",\n \"licenseNumber\": \"\",\n \"middleName\": \"\",\n \"licenseStatusName\": \"\"\n }\n },\n {\n \"compact\": \"coun\",\n \"dateOfUpdate\": \"\",\n \"jurisdiction\": \"mt\",\n \"previous\": {\n \"dateOfExpiration\": \"2247-12-07\",\n \"dateOfIssuance\": \"1732-04-07\",\n \"dateOfRenewal\": \"2490-01-20\",\n \"familyName\": \"\",\n \"givenName\": \"\",\n \"homeAddressCity\": \"\",\n \"homeAddressPostalCode\": \"\",\n \"homeAddressState\": \"\",\n \"homeAddressStreet1\": \"\",\n \"jurisdictionUploadedCompactEligibility\": \"ineligible\",\n \"jurisdictionUploadedLicenseStatus\": \"inactive\",\n \"middleName\": \"\",\n \"homeAddressStreet2\": \"\",\n \"npi\": \"4954399462\",\n \"compactEligibility\": \"eligible\",\n \"dateOfBirth\": \"1542-12-01\",\n \"suffix\": \"\",\n \"emailAddress\": \"\",\n \"phoneNumber\": \"+06725848936\",\n \"licenseStatus\": \"inactive\",\n \"licenseNumber\": \"\",\n \"licenseStatusName\": \"\"\n },\n \"type\": \"licenseUpdate\",\n \"updateType\": \"emailChange\",\n \"removedValues\": [\n \"\",\n \"\"\n ],\n \"licenseType\": \"occupational therapy assistant\",\n \"updatedValues\": {\n \"homeAddressStreet2\": \"\",\n \"npi\": \"1595699795\",\n \"homeAddressPostalCode\": \"\",\n \"givenName\": \"\",\n \"homeAddressStreet1\": \"\",\n \"compactEligibility\": \"ineligible\",\n \"jurisdictionUploadedCompactEligibility\": \"eligible\",\n \"dateOfBirth\": \"2091-12-09\",\n \"jurisdictionUploadedLicenseStatus\": \"inactive\",\n \"suffix\": \"\",\n \"dateOfIssuance\": \"2418-05-05\",\n \"emailAddress\": \"\",\n \"dateOfExpiration\": \"2702-10-02\",\n \"phoneNumber\": \"+37788018735632\",\n \"homeAddressState\": \"\",\n \"dateOfRenewal\": \"1105-10-31\",\n \"licenseStatus\": \"inactive\",\n \"familyName\": \"\",\n \"homeAddressCity\": \"\",\n \"licenseNumber\": \"\",\n \"middleName\": \"\",\n \"licenseStatusName\": \"\"\n }\n }\n ],\n \"homeAddressCity\": \"\",\n \"homeAddressPostalCode\": \"\",\n \"homeAddressState\": \"\",\n \"homeAddressStreet1\": \"\",\n \"jurisdiction\": \"ny\",\n \"jurisdictionUploadedCompactEligibility\": \"eligible\",\n \"jurisdictionUploadedLicenseStatus\": \"active\",\n \"licenseStatus\": \"active\",\n \"licenseType\": \"licensed professional counselor\",\n \"middleName\": \"\",\n \"providerId\": \"313794c2-e6a4-40a2-9e20-1fd7af3d276d\",\n \"type\": \"license-home\",\n \"homeAddressStreet2\": \"\",\n \"investigations\": [\n {\n \"compact\": \"coun\",\n \"creationDate\": \"\",\n \"dateOfUpdate\": \"\",\n \"investigationId\": \"\",\n \"jurisdiction\": \"ms\",\n \"licenseType\": \"\",\n \"providerId\": \"f47123b6-8f51-44f3-a1cb-7b1a8e15611f\",\n \"submittingUser\": \"\",\n \"type\": \"investigation\"\n },\n {\n \"compact\": \"aslp\",\n \"creationDate\": \"\",\n \"dateOfUpdate\": \"\",\n \"investigationId\": \"\",\n \"jurisdiction\": \"nh\",\n \"licenseType\": \"\",\n \"providerId\": \"9594910b-52b8-4b5d-985b-9750832cd1c0\",\n \"submittingUser\": \"\",\n \"type\": \"investigation\"\n }\n ],\n \"suffix\": \"\",\n \"emailAddress\": \"\",\n \"licenseNumber\": \"\",\n \"investigationStatus\": \"underInvestigation\",\n \"npi\": \"2736368753\",\n \"dateOfBirth\": \"2164-07-02\",\n \"ssnLastFour\": \"4355\",\n \"phoneNumber\": \"+70372285\",\n \"licenseStatusName\": \"\",\n \"adverseActions\": [\n {\n \"actionAgainst\": \"\",\n \"adverseActionId\": \"\",\n \"compact\": \"aslp\",\n \"creationDate\": \"2310-07-04\",\n \"dateOfUpdate\": \"\",\n \"effectiveStartDate\": \"2892-12-05\",\n \"encumbranceType\": \"\",\n \"jurisdiction\": \"ia\",\n \"licenseType\": \"\",\n \"licenseTypeAbbreviation\": \"\",\n \"providerId\": \"6cabca4e-7fbe-4e81-9a6c-8e4cfb4bd7df\",\n \"type\": \"adverseAction\",\n \"clinicalPrivilegeActionCategories\": [\n \"\",\n \"\"\n ],\n \"effectiveLiftDate\": \"2524-12-31\",\n \"clinicalPrivilegeActionCategory\": \"\",\n \"liftingUser\": \"\"\n },\n {\n \"actionAgainst\": \"\",\n \"adverseActionId\": \"\",\n \"compact\": \"octp\",\n \"creationDate\": \"1388-04-19\",\n \"dateOfUpdate\": \"\",\n \"effectiveStartDate\": \"2079-09-31\",\n \"encumbranceType\": \"\",\n \"jurisdiction\": \"wa\",\n \"licenseType\": \"\",\n \"licenseTypeAbbreviation\": \"\",\n \"providerId\": \"59d6846b-391f-4b14-80fe-41614be8372d\",\n \"type\": \"adverseAction\",\n \"clinicalPrivilegeActionCategories\": [\n \"\",\n \"\"\n ],\n \"effectiveLiftDate\": \"1284-01-19\",\n \"clinicalPrivilegeActionCategory\": \"\",\n \"liftingUser\": \"\"\n }\n ]\n }\n ],\n \"militaryAffiliations\": [\n {\n \"affiliationType\": \"militaryMemberSpouse\",\n \"compact\": \"coun\",\n \"dateOfUpdate\": \"\",\n \"dateOfUpload\": \"2656-06-30\",\n \"fileNames\": [\n \"\",\n \"\"\n ],\n \"providerId\": \"33ff6892-8ba6-46f7-987e-f1fddba57f4b\",\n \"status\": \"inactive\",\n \"type\": \"militaryAffiliation\",\n \"downloadLinks\": [\n {\n \"fileName\": \"\",\n \"url\": \"\"\n },\n {\n \"fileName\": \"\",\n \"url\": \"\"\n }\n ]\n },\n {\n \"affiliationType\": \"militaryMember\",\n \"compact\": \"coun\",\n \"dateOfUpdate\": \"\",\n \"dateOfUpload\": \"2442-01-07\",\n \"fileNames\": [\n \"\",\n \"\"\n ],\n \"providerId\": \"959e6e13-9d35-4d46-8091-12d3cb827e45\",\n \"status\": \"active\",\n \"type\": \"militaryAffiliation\",\n \"downloadLinks\": [\n {\n \"fileName\": \"\",\n \"url\": \"\"\n },\n {\n \"fileName\": \"\",\n \"url\": \"\"\n }\n ]\n }\n ],\n \"privilegeJurisdictions\": [\n \"md\",\n \"nc\"\n ],\n \"privileges\": [\n {\n \"administratorSetStatus\": \"active\",\n \"attestations\": [\n {\n \"attestationId\": \"\",\n \"version\": \"\"\n },\n {\n \"attestationId\": \"\",\n \"version\": \"\"\n }\n ],\n \"compact\": \"octp\",\n \"compactTransactionId\": \"\",\n \"dateOfExpiration\": \"1102-12-12\",\n \"dateOfIssuance\": \"1313-12-05\",\n \"dateOfRenewal\": \"2701-05-30\",\n \"dateOfUpdate\": \"\",\n \"history\": [\n {\n \"compact\": \"octp\",\n \"dateOfUpdate\": \"\",\n \"jurisdiction\": \"in\",\n \"previous\": {\n \"administratorSetStatus\": \"inactive\",\n \"attestations\": [\n {\n \"attestationId\": \"\",\n \"version\": \"\"\n },\n {\n \"attestationId\": \"\",\n \"version\": \"\"\n }\n ],\n \"compactTransactionId\": \"\",\n \"dateOfExpiration\": \"2836-07-27\",\n \"dateOfIssuance\": \"2788-10-30\",\n \"dateOfRenewal\": \"1280-10-31\",\n \"dateOfUpdate\": \"\",\n \"licenseJurisdiction\": \"id\",\n \"privilegeId\": \"\",\n \"compact\": \"octp\",\n \"jurisdiction\": \"ok\",\n \"type\": \"privilege\",\n \"providerId\": \"a8431870-2a46-424f-9be6-9d9612adc5bd\",\n \"status\": \"inactive\"\n },\n \"type\": \"privilegeUpdate\",\n \"updateType\": \"expiration\",\n \"removedValues\": [\n \"\",\n \"\"\n ],\n \"licenseType\": \"speech-language pathologist\",\n \"updatedValues\": {\n \"licenseJurisdiction\": \"ok\",\n \"compact\": \"octp\",\n \"jurisdiction\": \"mn\",\n \"attestations\": [\n {\n \"attestationId\": \"\",\n \"version\": \"\"\n },\n {\n \"attestationId\": \"\",\n \"version\": \"\"\n }\n ],\n \"type\": \"privilege\",\n \"compactTransactionId\": \"\",\n \"dateOfIssuance\": \"1612-12-31\",\n \"administratorSetStatus\": \"active\",\n \"dateOfExpiration\": \"2400-02-30\",\n \"privilegeId\": \"\",\n \"providerId\": \"040e7e14-5972-4398-95ee-399a7036cfb7\",\n \"dateOfRenewal\": \"2756-10-09\",\n \"dateOfUpdate\": \"\",\n \"status\": \"inactive\"\n }\n },\n {\n \"compact\": \"octp\",\n \"dateOfUpdate\": \"\",\n \"jurisdiction\": \"fl\",\n \"previous\": {\n \"administratorSetStatus\": \"active\",\n \"attestations\": [\n {\n \"attestationId\": \"\",\n \"version\": \"\"\n },\n {\n \"attestationId\": \"\",\n \"version\": \"\"\n }\n ],\n \"compactTransactionId\": \"\",\n \"dateOfExpiration\": \"2891-04-07\",\n \"dateOfIssuance\": \"1522-12-30\",\n \"dateOfRenewal\": \"1235-10-15\",\n \"dateOfUpdate\": \"\",\n \"licenseJurisdiction\": \"oh\",\n \"privilegeId\": \"\",\n \"compact\": \"octp\",\n \"jurisdiction\": \"ny\",\n \"type\": \"privilege\",\n \"providerId\": \"8174175e-978b-46f6-b0d5-75dc883e83ea\",\n \"status\": \"active\"\n },\n \"type\": \"privilegeUpdate\",\n \"updateType\": \"deactivation\",\n \"removedValues\": [\n \"\",\n \"\"\n ],\n \"licenseType\": \"speech-language pathologist\",\n \"updatedValues\": {\n \"licenseJurisdiction\": \"tn\",\n \"compact\": \"octp\",\n \"jurisdiction\": \"ut\",\n \"attestations\": [\n {\n \"attestationId\": \"\",\n \"version\": \"\"\n },\n {\n \"attestationId\": \"\",\n \"version\": \"\"\n }\n ],\n \"type\": \"privilege\",\n \"compactTransactionId\": \"\",\n \"dateOfIssuance\": \"1015-11-16\",\n \"administratorSetStatus\": \"active\",\n \"dateOfExpiration\": \"2808-10-30\",\n \"privilegeId\": \"\",\n \"providerId\": \"402bc3a2-4a76-49df-a95e-83cdc8d4c6c9\",\n \"dateOfRenewal\": \"2912-11-05\",\n \"dateOfUpdate\": \"\",\n \"status\": \"inactive\"\n }\n }\n ],\n \"jurisdiction\": \"il\",\n \"licenseJurisdiction\": \"ok\",\n \"licenseType\": \"licensed professional counselor\",\n \"privilegeId\": \"\",\n \"providerId\": \"fabef33a-bb76-469e-9794-0fb12dabb9d2\",\n \"status\": \"inactive\",\n \"type\": \"privilege\",\n \"investigationStatus\": \"underInvestigation\",\n \"investigations\": [\n {\n \"compact\": \"coun\",\n \"creationDate\": \"\",\n \"dateOfUpdate\": \"\",\n \"investigationId\": \"\",\n \"jurisdiction\": \"nc\",\n \"licenseType\": \"\",\n \"providerId\": \"b8939464-4b19-4a6d-bc77-5bfd9d0d7ea1\",\n \"submittingUser\": \"\",\n \"type\": \"investigation\"\n },\n {\n \"compact\": \"aslp\",\n \"creationDate\": \"\",\n \"dateOfUpdate\": \"\",\n \"investigationId\": \"\",\n \"jurisdiction\": \"az\",\n \"licenseType\": \"\",\n \"providerId\": \"aef09a8b-757f-4a4d-9739-12dfaac27da4\",\n \"submittingUser\": \"\",\n \"type\": \"investigation\"\n }\n ],\n \"adverseActions\": [\n {\n \"actionAgainst\": \"\",\n \"adverseActionId\": \"\",\n \"compact\": \"coun\",\n \"creationDate\": \"1589-10-10\",\n \"dateOfUpdate\": \"\",\n \"effectiveStartDate\": \"1165-04-31\",\n \"encumbranceType\": \"\",\n \"jurisdiction\": \"ca\",\n \"licenseType\": \"\",\n \"licenseTypeAbbreviation\": \"\",\n \"providerId\": \"deacc877-04f8-425a-85af-261178c514ae\",\n \"type\": \"adverseAction\",\n \"clinicalPrivilegeActionCategories\": [\n \"\",\n \"\"\n ],\n \"effectiveLiftDate\": \"1861-12-25\",\n \"clinicalPrivilegeActionCategory\": \"\",\n \"liftingUser\": \"\"\n },\n {\n \"actionAgainst\": \"\",\n \"adverseActionId\": \"\",\n \"compact\": \"coun\",\n \"creationDate\": \"2914-08-03\",\n \"dateOfUpdate\": \"\",\n \"effectiveStartDate\": \"2120-04-30\",\n \"encumbranceType\": \"\",\n \"jurisdiction\": \"id\",\n \"licenseType\": \"\",\n \"licenseTypeAbbreviation\": \"\",\n \"providerId\": \"cf4315a9-ccdb-42a8-854b-7e90def7885a\",\n \"type\": \"adverseAction\",\n \"clinicalPrivilegeActionCategories\": [\n \"\",\n \"\"\n ],\n \"effectiveLiftDate\": \"1914-04-30\",\n \"clinicalPrivilegeActionCategory\": \"\",\n \"liftingUser\": \"\"\n }\n ]\n },\n {\n \"administratorSetStatus\": \"inactive\",\n \"attestations\": [\n {\n \"attestationId\": \"\",\n \"version\": \"\"\n },\n {\n \"attestationId\": \"\",\n \"version\": \"\"\n }\n ],\n \"compact\": \"octp\",\n \"compactTransactionId\": \"\",\n \"dateOfExpiration\": \"1723-06-11\",\n \"dateOfIssuance\": \"1203-12-15\",\n \"dateOfRenewal\": \"2871-11-10\",\n \"dateOfUpdate\": \"\",\n \"history\": [\n {\n \"compact\": \"octp\",\n \"dateOfUpdate\": \"\",\n \"jurisdiction\": \"nj\",\n \"previous\": {\n \"administratorSetStatus\": \"inactive\",\n \"attestations\": [\n {\n \"attestationId\": \"\",\n \"version\": \"\"\n },\n {\n \"attestationId\": \"\",\n \"version\": \"\"\n }\n ],\n \"compactTransactionId\": \"\",\n \"dateOfExpiration\": \"1058-12-11\",\n \"dateOfIssuance\": \"2149-11-30\",\n \"dateOfRenewal\": \"1018-08-01\",\n \"dateOfUpdate\": \"\",\n \"licenseJurisdiction\": \"fl\",\n \"privilegeId\": \"\",\n \"compact\": \"coun\",\n \"jurisdiction\": \"mi\",\n \"type\": \"privilege\",\n \"providerId\": \"5e722037-8d03-47ab-afaa-d915b0cac757\",\n \"status\": \"inactive\"\n },\n \"type\": \"privilegeUpdate\",\n \"updateType\": \"lifting_encumbrance\",\n \"removedValues\": [\n \"\",\n \"\"\n ],\n \"licenseType\": \"occupational therapy assistant\",\n \"updatedValues\": {\n \"licenseJurisdiction\": \"tn\",\n \"compact\": \"coun\",\n \"jurisdiction\": \"wi\",\n \"attestations\": [\n {\n \"attestationId\": \"\",\n \"version\": \"\"\n },\n {\n \"attestationId\": \"\",\n \"version\": \"\"\n }\n ],\n \"type\": \"privilege\",\n \"compactTransactionId\": \"\",\n \"dateOfIssuance\": \"1426-03-06\",\n \"administratorSetStatus\": \"inactive\",\n \"dateOfExpiration\": \"1199-10-06\",\n \"privilegeId\": \"\",\n \"providerId\": \"283c9149-73e8-40a4-9c6a-788e01bbed79\",\n \"dateOfRenewal\": \"1271-04-30\",\n \"dateOfUpdate\": \"\",\n \"status\": \"inactive\"\n }\n },\n {\n \"compact\": \"octp\",\n \"dateOfUpdate\": \"\",\n \"jurisdiction\": \"id\",\n \"previous\": {\n \"administratorSetStatus\": \"inactive\",\n \"attestations\": [\n {\n \"attestationId\": \"\",\n \"version\": \"\"\n },\n {\n \"attestationId\": \"\",\n \"version\": \"\"\n }\n ],\n \"compactTransactionId\": \"\",\n \"dateOfExpiration\": \"2341-12-30\",\n \"dateOfIssuance\": \"1576-03-30\",\n \"dateOfRenewal\": \"1709-03-03\",\n \"dateOfUpdate\": \"\",\n \"licenseJurisdiction\": \"wy\",\n \"privilegeId\": \"\",\n \"compact\": \"octp\",\n \"jurisdiction\": \"ut\",\n \"type\": \"privilege\",\n \"providerId\": \"366e661a-421d-4689-b899-793f7233747c\",\n \"status\": \"inactive\"\n },\n \"type\": \"privilegeUpdate\",\n \"updateType\": \"other\",\n \"removedValues\": [\n \"\",\n \"\"\n ],\n \"licenseType\": \"occupational therapist\",\n \"updatedValues\": {\n \"licenseJurisdiction\": \"hi\",\n \"compact\": \"coun\",\n \"jurisdiction\": \"ky\",\n \"attestations\": [\n {\n \"attestationId\": \"\",\n \"version\": \"\"\n },\n {\n \"attestationId\": \"\",\n \"version\": \"\"\n }\n ],\n \"type\": \"privilege\",\n \"compactTransactionId\": \"\",\n \"dateOfIssuance\": \"2939-01-02\",\n \"administratorSetStatus\": \"inactive\",\n \"dateOfExpiration\": \"1022-10-24\",\n \"privilegeId\": \"\",\n \"providerId\": \"fbbe0975-c404-46f0-9dc7-399ffeae3683\",\n \"dateOfRenewal\": \"1236-01-31\",\n \"dateOfUpdate\": \"\",\n \"status\": \"active\"\n }\n }\n ],\n \"jurisdiction\": \"ms\",\n \"licenseJurisdiction\": \"mn\",\n \"licenseType\": \"licensed professional counselor\",\n \"privilegeId\": \"\",\n \"providerId\": \"8d94428c-bb5b-4140-8aa6-066bffffdfdc\",\n \"status\": \"inactive\",\n \"type\": \"privilege\",\n \"investigationStatus\": \"underInvestigation\",\n \"investigations\": [\n {\n \"compact\": \"coun\",\n \"creationDate\": \"\",\n \"dateOfUpdate\": \"\",\n \"investigationId\": \"\",\n \"jurisdiction\": \"md\",\n \"licenseType\": \"\",\n \"providerId\": \"6b200887-a4e3-4124-acdc-fffce69807f6\",\n \"submittingUser\": \"\",\n \"type\": \"investigation\"\n },\n {\n \"compact\": \"octp\",\n \"creationDate\": \"\",\n \"dateOfUpdate\": \"\",\n \"investigationId\": \"\",\n \"jurisdiction\": \"ak\",\n \"licenseType\": \"\",\n \"providerId\": \"3eb7845c-a341-4b11-8db7-6186a3e0f7e9\",\n \"submittingUser\": \"\",\n \"type\": \"investigation\"\n }\n ],\n \"adverseActions\": [\n {\n \"actionAgainst\": \"\",\n \"adverseActionId\": \"\",\n \"compact\": \"aslp\",\n \"creationDate\": \"2881-04-31\",\n \"dateOfUpdate\": \"\",\n \"effectiveStartDate\": \"1128-04-14\",\n \"encumbranceType\": \"\",\n \"jurisdiction\": \"in\",\n \"licenseType\": \"\",\n \"licenseTypeAbbreviation\": \"\",\n \"providerId\": \"c38f6c41-d865-4c37-ae18-63ce5fd18c55\",\n \"type\": \"adverseAction\",\n \"clinicalPrivilegeActionCategories\": [\n \"\",\n \"\"\n ],\n \"effectiveLiftDate\": \"2871-05-08\",\n \"clinicalPrivilegeActionCategory\": \"\",\n \"liftingUser\": \"\"\n },\n {\n \"actionAgainst\": \"\",\n \"adverseActionId\": \"\",\n \"compact\": \"coun\",\n \"creationDate\": \"2484-04-31\",\n \"dateOfUpdate\": \"\",\n \"effectiveStartDate\": \"2979-11-04\",\n \"encumbranceType\": \"\",\n \"jurisdiction\": \"fl\",\n \"licenseType\": \"\",\n \"licenseTypeAbbreviation\": \"\",\n \"providerId\": \"be457ff9-9a51-404b-9d23-b9f5eaa9d6c6\",\n \"type\": \"adverseAction\",\n \"clinicalPrivilegeActionCategories\": [\n \"\",\n \"\"\n ],\n \"effectiveLiftDate\": \"2146-10-05\",\n \"clinicalPrivilegeActionCategory\": \"\",\n \"liftingUser\": \"\"\n }\n ]\n }\n ],\n \"providerId\": \"098edacb-8ab3-4f0b-a655-c9f26be8827c\",\n \"type\": \"provider\",\n \"npi\": \"5990059533\",\n \"compactEligibility\": \"ineligible\",\n \"jurisdictionUploadedCompactEligibility\": \"eligible\",\n \"dateOfBirth\": \"1157-07-30\",\n \"jurisdictionUploadedLicenseStatus\": \"active\",\n \"suffix\": \"\",\n \"currentHomeJurisdiction\": \"wi\",\n \"ssnLastFour\": \"7344\",\n \"licenseStatus\": \"active\",\n \"middleName\": \"\",\n \"compactConnectRegisteredEmailAddress\": \"\"\n}", + "body": "{\n \"birthMonthDay\": \"02-11\",\n \"compact\": \"octp\",\n \"dateOfExpiration\": \"1631-11-08\",\n \"dateOfUpdate\": \"\",\n \"familyName\": \"\",\n \"givenName\": \"\",\n \"licenseJurisdiction\": \"mi\",\n \"licenses\": [\n {\n \"compact\": \"octp\",\n \"compactEligibility\": \"eligible\",\n \"dateOfExpiration\": \"2477-01-29\",\n \"dateOfIssuance\": \"2289-02-05\",\n \"dateOfRenewal\": \"2467-09-31\",\n \"dateOfUpdate\": \"\",\n \"familyName\": \"\",\n \"givenName\": \"\",\n \"history\": [\n {\n \"compact\": \"aslp\",\n \"dateOfUpdate\": \"\",\n \"jurisdiction\": \"oh\",\n \"previous\": {\n \"dateOfExpiration\": \"1524-10-30\",\n \"dateOfIssuance\": \"1167-12-07\",\n \"dateOfRenewal\": \"1205-01-17\",\n \"familyName\": \"\",\n \"givenName\": \"\",\n \"homeAddressCity\": \"\",\n \"homeAddressPostalCode\": \"\",\n \"homeAddressState\": \"\",\n \"homeAddressStreet1\": \"\",\n \"jurisdictionUploadedCompactEligibility\": \"eligible\",\n \"jurisdictionUploadedLicenseStatus\": \"active\",\n \"middleName\": \"\",\n \"homeAddressStreet2\": \"\",\n \"npi\": \"6002575391\",\n \"compactEligibility\": \"eligible\",\n \"dateOfBirth\": \"1105-06-30\",\n \"suffix\": \"\",\n \"emailAddress\": \"\",\n \"phoneNumber\": \"+007267268\",\n \"licenseStatus\": \"active\",\n \"licenseNumber\": \"\",\n \"licenseStatusName\": \"\"\n },\n \"type\": \"licenseUpdate\",\n \"updateType\": \"other\",\n \"removedValues\": [\n \"\",\n \"\"\n ],\n \"licenseType\": \"occupational therapy assistant\",\n \"updatedValues\": {\n \"homeAddressStreet2\": \"\",\n \"npi\": \"8616398454\",\n \"homeAddressPostalCode\": \"\",\n \"givenName\": \"\",\n \"homeAddressStreet1\": \"\",\n \"compactEligibility\": \"ineligible\",\n \"jurisdictionUploadedCompactEligibility\": \"ineligible\",\n \"dateOfBirth\": \"2499-11-30\",\n \"jurisdictionUploadedLicenseStatus\": \"active\",\n \"suffix\": \"\",\n \"dateOfIssuance\": \"2862-10-28\",\n \"emailAddress\": \"\",\n \"dateOfExpiration\": \"1419-12-09\",\n \"phoneNumber\": \"+47237183623669\",\n \"homeAddressState\": \"\",\n \"dateOfRenewal\": \"1379-02-31\",\n \"licenseStatus\": \"inactive\",\n \"familyName\": \"\",\n \"homeAddressCity\": \"\",\n \"licenseNumber\": \"\",\n \"middleName\": \"\",\n \"licenseStatusName\": \"\"\n }\n },\n {\n \"compact\": \"coun\",\n \"dateOfUpdate\": \"\",\n \"jurisdiction\": \"sc\",\n \"previous\": {\n \"dateOfExpiration\": \"1431-10-21\",\n \"dateOfIssuance\": \"1210-12-31\",\n \"dateOfRenewal\": \"1787-02-31\",\n \"familyName\": \"\",\n \"givenName\": \"\",\n \"homeAddressCity\": \"\",\n \"homeAddressPostalCode\": \"\",\n \"homeAddressState\": \"\",\n \"homeAddressStreet1\": \"\",\n \"jurisdictionUploadedCompactEligibility\": \"eligible\",\n \"jurisdictionUploadedLicenseStatus\": \"inactive\",\n \"middleName\": \"\",\n \"homeAddressStreet2\": \"\",\n \"npi\": \"3083184307\",\n \"compactEligibility\": \"eligible\",\n \"dateOfBirth\": \"1005-10-01\",\n \"suffix\": \"\",\n \"emailAddress\": \"\",\n \"phoneNumber\": \"+52476242194\",\n \"licenseStatus\": \"active\",\n \"licenseNumber\": \"\",\n \"licenseStatusName\": \"\"\n },\n \"type\": \"licenseUpdate\",\n \"updateType\": \"homeJurisdictionChange\",\n \"removedValues\": [\n \"\",\n \"\"\n ],\n \"licenseType\": \"speech-language pathologist\",\n \"updatedValues\": {\n \"homeAddressStreet2\": \"\",\n \"npi\": \"0711842144\",\n \"homeAddressPostalCode\": \"\",\n \"givenName\": \"\",\n \"homeAddressStreet1\": \"\",\n \"compactEligibility\": \"ineligible\",\n \"jurisdictionUploadedCompactEligibility\": \"ineligible\",\n \"dateOfBirth\": \"2578-07-30\",\n \"jurisdictionUploadedLicenseStatus\": \"active\",\n \"suffix\": \"\",\n \"dateOfIssuance\": \"2683-07-31\",\n \"emailAddress\": \"\",\n \"dateOfExpiration\": \"2406-01-27\",\n \"phoneNumber\": \"+57863834913559\",\n \"homeAddressState\": \"\",\n \"dateOfRenewal\": \"1690-11-10\",\n \"licenseStatus\": \"active\",\n \"familyName\": \"\",\n \"homeAddressCity\": \"\",\n \"licenseNumber\": \"\",\n \"middleName\": \"\",\n \"licenseStatusName\": \"\"\n }\n }\n ],\n \"homeAddressCity\": \"\",\n \"homeAddressPostalCode\": \"\",\n \"homeAddressState\": \"\",\n \"homeAddressStreet1\": \"\",\n \"jurisdiction\": \"ga\",\n \"jurisdictionUploadedCompactEligibility\": \"eligible\",\n \"jurisdictionUploadedLicenseStatus\": \"active\",\n \"licenseStatus\": \"active\",\n \"licenseType\": \"audiologist\",\n \"middleName\": \"\",\n \"providerId\": \"c2830970-3ac2-4408-80f3-1c3b7f3f12fd\",\n \"type\": \"license-home\",\n \"homeAddressStreet2\": \"\",\n \"investigations\": [\n {\n \"compact\": \"octp\",\n \"creationDate\": \"\",\n \"dateOfUpdate\": \"\",\n \"investigationId\": \"\",\n \"jurisdiction\": \"ok\",\n \"licenseType\": \"\",\n \"providerId\": \"c4b9a8a2-24f1-41aa-950b-4d1316f8856a\",\n \"submittingUser\": \"\",\n \"type\": \"investigation\"\n },\n {\n \"compact\": \"aslp\",\n \"creationDate\": \"\",\n \"dateOfUpdate\": \"\",\n \"investigationId\": \"\",\n \"jurisdiction\": \"id\",\n \"licenseType\": \"\",\n \"providerId\": \"9fd53799-7da6-4253-897a-e485a6b1d58e\",\n \"submittingUser\": \"\",\n \"type\": \"investigation\"\n }\n ],\n \"suffix\": \"\",\n \"emailAddress\": \"\",\n \"licenseNumber\": \"\",\n \"investigationStatus\": \"underInvestigation\",\n \"npi\": \"6248254005\",\n \"dateOfBirth\": \"2457-06-02\",\n \"ssnLastFour\": \"1587\",\n \"phoneNumber\": \"+88170901\",\n \"licenseStatusName\": \"\",\n \"adverseActions\": [\n {\n \"actionAgainst\": \"\",\n \"adverseActionId\": \"\",\n \"compact\": \"octp\",\n \"creationDate\": \"1079-06-14\",\n \"dateOfUpdate\": \"\",\n \"effectiveStartDate\": \"2198-05-29\",\n \"encumbranceType\": \"\",\n \"jurisdiction\": \"nh\",\n \"licenseType\": \"\",\n \"licenseTypeAbbreviation\": \"\",\n \"providerId\": \"993f7974-2cd5-4f52-b3d7-62ca646321eb\",\n \"type\": \"adverseAction\",\n \"clinicalPrivilegeActionCategories\": [\n \"\",\n \"\"\n ],\n \"effectiveLiftDate\": \"2806-10-31\",\n \"liftingUser\": \"\"\n },\n {\n \"actionAgainst\": \"\",\n \"adverseActionId\": \"\",\n \"compact\": \"octp\",\n \"creationDate\": \"2711-04-26\",\n \"dateOfUpdate\": \"\",\n \"effectiveStartDate\": \"2151-12-31\",\n \"encumbranceType\": \"\",\n \"jurisdiction\": \"ak\",\n \"licenseType\": \"\",\n \"licenseTypeAbbreviation\": \"\",\n \"providerId\": \"f86f7fa0-82b2-496b-bfa2-f81facb1cc9e\",\n \"type\": \"adverseAction\",\n \"clinicalPrivilegeActionCategories\": [\n \"\",\n \"\"\n ],\n \"effectiveLiftDate\": \"2901-09-09\",\n \"liftingUser\": \"\"\n }\n ]\n },\n {\n \"compact\": \"octp\",\n \"compactEligibility\": \"eligible\",\n \"dateOfExpiration\": \"2713-08-14\",\n \"dateOfIssuance\": \"1623-04-15\",\n \"dateOfRenewal\": \"2814-11-21\",\n \"dateOfUpdate\": \"\",\n \"familyName\": \"\",\n \"givenName\": \"\",\n \"history\": [\n {\n \"compact\": \"octp\",\n \"dateOfUpdate\": \"\",\n \"jurisdiction\": \"id\",\n \"previous\": {\n \"dateOfExpiration\": \"2593-10-23\",\n \"dateOfIssuance\": \"1874-09-30\",\n \"dateOfRenewal\": \"1371-04-30\",\n \"familyName\": \"\",\n \"givenName\": \"\",\n \"homeAddressCity\": \"\",\n \"homeAddressPostalCode\": \"\",\n \"homeAddressState\": \"\",\n \"homeAddressStreet1\": \"\",\n \"jurisdictionUploadedCompactEligibility\": \"eligible\",\n \"jurisdictionUploadedLicenseStatus\": \"active\",\n \"middleName\": \"\",\n \"homeAddressStreet2\": \"\",\n \"npi\": \"7160215740\",\n \"compactEligibility\": \"eligible\",\n \"dateOfBirth\": \"2165-02-30\",\n \"suffix\": \"\",\n \"emailAddress\": \"\",\n \"phoneNumber\": \"+8642909671\",\n \"licenseStatus\": \"active\",\n \"licenseNumber\": \"\",\n \"licenseStatusName\": \"\"\n },\n \"type\": \"licenseUpdate\",\n \"updateType\": \"licenseDeactivation\",\n \"removedValues\": [\n \"\",\n \"\"\n ],\n \"licenseType\": \"occupational therapy assistant\",\n \"updatedValues\": {\n \"homeAddressStreet2\": \"\",\n \"npi\": \"6845028097\",\n \"homeAddressPostalCode\": \"\",\n \"givenName\": \"\",\n \"homeAddressStreet1\": \"\",\n \"compactEligibility\": \"ineligible\",\n \"jurisdictionUploadedCompactEligibility\": \"eligible\",\n \"dateOfBirth\": \"2551-06-31\",\n \"jurisdictionUploadedLicenseStatus\": \"active\",\n \"suffix\": \"\",\n \"dateOfIssuance\": \"2263-10-01\",\n \"emailAddress\": \"\",\n \"dateOfExpiration\": \"1706-12-28\",\n \"phoneNumber\": \"+101312267\",\n \"homeAddressState\": \"\",\n \"dateOfRenewal\": \"2012-10-01\",\n \"licenseStatus\": \"inactive\",\n \"familyName\": \"\",\n \"homeAddressCity\": \"\",\n \"licenseNumber\": \"\",\n \"middleName\": \"\",\n \"licenseStatusName\": \"\"\n }\n },\n {\n \"compact\": \"aslp\",\n \"dateOfUpdate\": \"\",\n \"jurisdiction\": \"wi\",\n \"previous\": {\n \"dateOfExpiration\": \"1149-08-11\",\n \"dateOfIssuance\": \"1924-05-10\",\n \"dateOfRenewal\": \"2707-11-16\",\n \"familyName\": \"\",\n \"givenName\": \"\",\n \"homeAddressCity\": \"\",\n \"homeAddressPostalCode\": \"\",\n \"homeAddressState\": \"\",\n \"homeAddressStreet1\": \"\",\n \"jurisdictionUploadedCompactEligibility\": \"ineligible\",\n \"jurisdictionUploadedLicenseStatus\": \"inactive\",\n \"middleName\": \"\",\n \"homeAddressStreet2\": \"\",\n \"npi\": \"6250035227\",\n \"compactEligibility\": \"eligible\",\n \"dateOfBirth\": \"2225-11-01\",\n \"suffix\": \"\",\n \"emailAddress\": \"\",\n \"phoneNumber\": \"+688297087690\",\n \"licenseStatus\": \"inactive\",\n \"licenseNumber\": \"\",\n \"licenseStatusName\": \"\"\n },\n \"type\": \"licenseUpdate\",\n \"updateType\": \"lifting_encumbrance\",\n \"removedValues\": [\n \"\",\n \"\"\n ],\n \"licenseType\": \"occupational therapist\",\n \"updatedValues\": {\n \"homeAddressStreet2\": \"\",\n \"npi\": \"9422845892\",\n \"homeAddressPostalCode\": \"\",\n \"givenName\": \"\",\n \"homeAddressStreet1\": \"\",\n \"compactEligibility\": \"eligible\",\n \"jurisdictionUploadedCompactEligibility\": \"ineligible\",\n \"dateOfBirth\": \"2750-05-07\",\n \"jurisdictionUploadedLicenseStatus\": \"inactive\",\n \"suffix\": \"\",\n \"dateOfIssuance\": \"2095-12-03\",\n \"emailAddress\": \"\",\n \"dateOfExpiration\": \"1505-05-30\",\n \"phoneNumber\": \"+838892464019\",\n \"homeAddressState\": \"\",\n \"dateOfRenewal\": \"1936-12-25\",\n \"licenseStatus\": \"active\",\n \"familyName\": \"\",\n \"homeAddressCity\": \"\",\n \"licenseNumber\": \"\",\n \"middleName\": \"\",\n \"licenseStatusName\": \"\"\n }\n }\n ],\n \"homeAddressCity\": \"\",\n \"homeAddressPostalCode\": \"\",\n \"homeAddressState\": \"\",\n \"homeAddressStreet1\": \"\",\n \"jurisdiction\": \"nj\",\n \"jurisdictionUploadedCompactEligibility\": \"eligible\",\n \"jurisdictionUploadedLicenseStatus\": \"inactive\",\n \"licenseStatus\": \"inactive\",\n \"licenseType\": \"licensed professional counselor\",\n \"middleName\": \"\",\n \"providerId\": \"4aa7243e-4b49-4b76-a465-1ec7965da919\",\n \"type\": \"license-home\",\n \"homeAddressStreet2\": \"\",\n \"investigations\": [\n {\n \"compact\": \"aslp\",\n \"creationDate\": \"\",\n \"dateOfUpdate\": \"\",\n \"investigationId\": \"\",\n \"jurisdiction\": \"oh\",\n \"licenseType\": \"\",\n \"providerId\": \"d2f92662-5881-4273-b95e-ad56981d9b5b\",\n \"submittingUser\": \"\",\n \"type\": \"investigation\"\n },\n {\n \"compact\": \"aslp\",\n \"creationDate\": \"\",\n \"dateOfUpdate\": \"\",\n \"investigationId\": \"\",\n \"jurisdiction\": \"ne\",\n \"licenseType\": \"\",\n \"providerId\": \"273db8ac-b2c3-41fe-9ca1-7240d8f9d0b9\",\n \"submittingUser\": \"\",\n \"type\": \"investigation\"\n }\n ],\n \"suffix\": \"\",\n \"emailAddress\": \"\",\n \"licenseNumber\": \"\",\n \"investigationStatus\": \"underInvestigation\",\n \"npi\": \"3519973963\",\n \"dateOfBirth\": \"1650-03-05\",\n \"ssnLastFour\": \"5195\",\n \"phoneNumber\": \"+478582966712\",\n \"licenseStatusName\": \"\",\n \"adverseActions\": [\n {\n \"actionAgainst\": \"\",\n \"adverseActionId\": \"\",\n \"compact\": \"coun\",\n \"creationDate\": \"1526-12-18\",\n \"dateOfUpdate\": \"\",\n \"effectiveStartDate\": \"2486-07-31\",\n \"encumbranceType\": \"\",\n \"jurisdiction\": \"pr\",\n \"licenseType\": \"\",\n \"licenseTypeAbbreviation\": \"\",\n \"providerId\": \"f1b67668-c893-4417-96b4-97538d1d644d\",\n \"type\": \"adverseAction\",\n \"clinicalPrivilegeActionCategories\": [\n \"\",\n \"\"\n ],\n \"effectiveLiftDate\": \"1572-11-06\",\n \"liftingUser\": \"\"\n },\n {\n \"actionAgainst\": \"\",\n \"adverseActionId\": \"\",\n \"compact\": \"coun\",\n \"creationDate\": \"1253-03-30\",\n \"dateOfUpdate\": \"\",\n \"effectiveStartDate\": \"1025-12-10\",\n \"encumbranceType\": \"\",\n \"jurisdiction\": \"tn\",\n \"licenseType\": \"\",\n \"licenseTypeAbbreviation\": \"\",\n \"providerId\": \"5513e91e-deb1-4775-8536-0b8fdea726ab\",\n \"type\": \"adverseAction\",\n \"clinicalPrivilegeActionCategories\": [\n \"\",\n \"\"\n ],\n \"effectiveLiftDate\": \"1701-07-08\",\n \"liftingUser\": \"\"\n }\n ]\n }\n ],\n \"militaryAffiliations\": [\n {\n \"affiliationType\": \"militaryMember\",\n \"compact\": \"octp\",\n \"dateOfUpdate\": \"\",\n \"dateOfUpload\": \"1555-06-30\",\n \"fileNames\": [\n \"\",\n \"\"\n ],\n \"providerId\": \"b8cc69f7-a33e-42f2-9d71-1601fabab556\",\n \"status\": \"inactive\",\n \"type\": \"militaryAffiliation\",\n \"downloadLinks\": [\n {\n \"fileName\": \"\",\n \"url\": \"\"\n },\n {\n \"fileName\": \"\",\n \"url\": \"\"\n }\n ]\n },\n {\n \"affiliationType\": \"militaryMemberSpouse\",\n \"compact\": \"coun\",\n \"dateOfUpdate\": \"\",\n \"dateOfUpload\": \"2471-05-18\",\n \"fileNames\": [\n \"\",\n \"\"\n ],\n \"providerId\": \"f9e1264d-4cd1-46de-b585-b31a11ddd9d4\",\n \"status\": \"active\",\n \"type\": \"militaryAffiliation\",\n \"downloadLinks\": [\n {\n \"fileName\": \"\",\n \"url\": \"\"\n },\n {\n \"fileName\": \"\",\n \"url\": \"\"\n }\n ]\n }\n ],\n \"privilegeJurisdictions\": [\n \"wv\",\n \"sd\"\n ],\n \"privileges\": [\n {\n \"administratorSetStatus\": \"active\",\n \"attestations\": [\n {\n \"attestationId\": \"\",\n \"version\": \"\"\n },\n {\n \"attestationId\": \"\",\n \"version\": \"\"\n }\n ],\n \"compact\": \"coun\",\n \"compactTransactionId\": \"\",\n \"dateOfExpiration\": \"1185-10-06\",\n \"dateOfIssuance\": \"2132-07-06\",\n \"dateOfRenewal\": \"1638-11-06\",\n \"dateOfUpdate\": \"\",\n \"history\": [\n {\n \"compact\": \"aslp\",\n \"dateOfUpdate\": \"\",\n \"jurisdiction\": \"vi\",\n \"previous\": {\n \"administratorSetStatus\": \"active\",\n \"attestations\": [\n {\n \"attestationId\": \"\",\n \"version\": \"\"\n },\n {\n \"attestationId\": \"\",\n \"version\": \"\"\n }\n ],\n \"compactTransactionId\": \"\",\n \"dateOfExpiration\": \"2351-12-06\",\n \"dateOfIssuance\": \"2834-10-07\",\n \"dateOfRenewal\": \"1257-10-25\",\n \"dateOfUpdate\": \"\",\n \"licenseJurisdiction\": \"ms\",\n \"privilegeId\": \"\",\n \"compact\": \"coun\",\n \"jurisdiction\": \"va\",\n \"type\": \"privilege\",\n \"providerId\": \"714866dc-e30f-4b34-8eda-30c60cfd6082\",\n \"status\": \"active\"\n },\n \"type\": \"privilegeUpdate\",\n \"updateType\": \"homeJurisdictionChange\",\n \"removedValues\": [\n \"\",\n \"\"\n ],\n \"licenseType\": \"occupational therapy assistant\",\n \"updatedValues\": {\n \"licenseJurisdiction\": \"ia\",\n \"compact\": \"octp\",\n \"jurisdiction\": \"ny\",\n \"attestations\": [\n {\n \"attestationId\": \"\",\n \"version\": \"\"\n },\n {\n \"attestationId\": \"\",\n \"version\": \"\"\n }\n ],\n \"type\": \"privilege\",\n \"compactTransactionId\": \"\",\n \"dateOfIssuance\": \"1880-11-31\",\n \"administratorSetStatus\": \"active\",\n \"dateOfExpiration\": \"1482-03-10\",\n \"privilegeId\": \"\",\n \"providerId\": \"d50167b1-d9b2-46ac-96e6-43c98979e1fc\",\n \"dateOfRenewal\": \"1184-02-31\",\n \"dateOfUpdate\": \"\",\n \"status\": \"inactive\"\n }\n },\n {\n \"compact\": \"coun\",\n \"dateOfUpdate\": \"\",\n \"jurisdiction\": \"tx\",\n \"previous\": {\n \"administratorSetStatus\": \"active\",\n \"attestations\": [\n {\n \"attestationId\": \"\",\n \"version\": \"\"\n },\n {\n \"attestationId\": \"\",\n \"version\": \"\"\n }\n ],\n \"compactTransactionId\": \"\",\n \"dateOfExpiration\": \"2459-07-08\",\n \"dateOfIssuance\": \"2383-05-08\",\n \"dateOfRenewal\": \"1311-12-15\",\n \"dateOfUpdate\": \"\",\n \"licenseJurisdiction\": \"nh\",\n \"privilegeId\": \"\",\n \"compact\": \"octp\",\n \"jurisdiction\": \"nc\",\n \"type\": \"privilege\",\n \"providerId\": \"864c3bae-96f5-4a48-8159-9ec036b2e025\",\n \"status\": \"active\"\n },\n \"type\": \"privilegeUpdate\",\n \"updateType\": \"encumbrance\",\n \"removedValues\": [\n \"\",\n \"\"\n ],\n \"licenseType\": \"occupational therapist\",\n \"updatedValues\": {\n \"licenseJurisdiction\": \"ct\",\n \"compact\": \"aslp\",\n \"jurisdiction\": \"vi\",\n \"attestations\": [\n {\n \"attestationId\": \"\",\n \"version\": \"\"\n },\n {\n \"attestationId\": \"\",\n \"version\": \"\"\n }\n ],\n \"type\": \"privilege\",\n \"compactTransactionId\": \"\",\n \"dateOfIssuance\": \"2927-09-08\",\n \"administratorSetStatus\": \"inactive\",\n \"dateOfExpiration\": \"2588-11-04\",\n \"privilegeId\": \"\",\n \"providerId\": \"3006e047-6d77-413d-9e64-4ab2437571fa\",\n \"dateOfRenewal\": \"1981-11-08\",\n \"dateOfUpdate\": \"\",\n \"status\": \"inactive\"\n }\n }\n ],\n \"jurisdiction\": \"ms\",\n \"licenseJurisdiction\": \"ky\",\n \"licenseType\": \"audiologist\",\n \"privilegeId\": \"\",\n \"providerId\": \"f925179c-fdb4-4dac-ba9f-3bf193b985f5\",\n \"status\": \"inactive\",\n \"type\": \"privilege\",\n \"investigationStatus\": \"underInvestigation\",\n \"investigations\": [\n {\n \"compact\": \"aslp\",\n \"creationDate\": \"\",\n \"dateOfUpdate\": \"\",\n \"investigationId\": \"\",\n \"jurisdiction\": \"nv\",\n \"licenseType\": \"\",\n \"providerId\": \"ec7cd51a-0012-4a5c-a895-1f1c63c21e1a\",\n \"submittingUser\": \"\",\n \"type\": \"investigation\"\n },\n {\n \"compact\": \"octp\",\n \"creationDate\": \"\",\n \"dateOfUpdate\": \"\",\n \"investigationId\": \"\",\n \"jurisdiction\": \"ne\",\n \"licenseType\": \"\",\n \"providerId\": \"36b6bfc4-69b4-4bc8-b8a3-8ce1df068152\",\n \"submittingUser\": \"\",\n \"type\": \"investigation\"\n }\n ],\n \"adverseActions\": [\n {\n \"actionAgainst\": \"\",\n \"adverseActionId\": \"\",\n \"compact\": \"aslp\",\n \"creationDate\": \"1843-03-30\",\n \"dateOfUpdate\": \"\",\n \"effectiveStartDate\": \"1150-09-12\",\n \"encumbranceType\": \"\",\n \"jurisdiction\": \"nj\",\n \"licenseType\": \"\",\n \"licenseTypeAbbreviation\": \"\",\n \"providerId\": \"ebc69eb5-08dd-4001-b706-76e811e1af46\",\n \"type\": \"adverseAction\",\n \"clinicalPrivilegeActionCategories\": [\n \"\",\n \"\"\n ],\n \"effectiveLiftDate\": \"2278-11-23\",\n \"liftingUser\": \"\"\n },\n {\n \"actionAgainst\": \"\",\n \"adverseActionId\": \"\",\n \"compact\": \"aslp\",\n \"creationDate\": \"2103-12-13\",\n \"dateOfUpdate\": \"\",\n \"effectiveStartDate\": \"1144-06-30\",\n \"encumbranceType\": \"\",\n \"jurisdiction\": \"nm\",\n \"licenseType\": \"\",\n \"licenseTypeAbbreviation\": \"\",\n \"providerId\": \"3a0d7dc1-8c20-4143-b9a2-636d01cdea78\",\n \"type\": \"adverseAction\",\n \"clinicalPrivilegeActionCategories\": [\n \"\",\n \"\"\n ],\n \"effectiveLiftDate\": \"1310-10-30\",\n \"liftingUser\": \"\"\n }\n ]\n },\n {\n \"administratorSetStatus\": \"active\",\n \"attestations\": [\n {\n \"attestationId\": \"\",\n \"version\": \"\"\n },\n {\n \"attestationId\": \"\",\n \"version\": \"\"\n }\n ],\n \"compact\": \"coun\",\n \"compactTransactionId\": \"\",\n \"dateOfExpiration\": \"2476-06-31\",\n \"dateOfIssuance\": \"2164-05-30\",\n \"dateOfRenewal\": \"2491-07-08\",\n \"dateOfUpdate\": \"\",\n \"history\": [\n {\n \"compact\": \"coun\",\n \"dateOfUpdate\": \"\",\n \"jurisdiction\": \"ky\",\n \"previous\": {\n \"administratorSetStatus\": \"active\",\n \"attestations\": [\n {\n \"attestationId\": \"\",\n \"version\": \"\"\n },\n {\n \"attestationId\": \"\",\n \"version\": \"\"\n }\n ],\n \"compactTransactionId\": \"\",\n \"dateOfExpiration\": \"1884-03-09\",\n \"dateOfIssuance\": \"1109-11-30\",\n \"dateOfRenewal\": \"1434-07-31\",\n \"dateOfUpdate\": \"\",\n \"licenseJurisdiction\": \"ms\",\n \"privilegeId\": \"\",\n \"compact\": \"aslp\",\n \"jurisdiction\": \"id\",\n \"type\": \"privilege\",\n \"providerId\": \"042c0e25-f384-47e0-865f-e0c1881d911c\",\n \"status\": \"active\"\n },\n \"type\": \"privilegeUpdate\",\n \"updateType\": \"issuance\",\n \"removedValues\": [\n \"\",\n \"\"\n ],\n \"licenseType\": \"speech-language pathologist\",\n \"updatedValues\": {\n \"licenseJurisdiction\": \"me\",\n \"compact\": \"octp\",\n \"jurisdiction\": \"il\",\n \"attestations\": [\n {\n \"attestationId\": \"\",\n \"version\": \"\"\n },\n {\n \"attestationId\": \"\",\n \"version\": \"\"\n }\n ],\n \"type\": \"privilege\",\n \"compactTransactionId\": \"\",\n \"dateOfIssuance\": \"1366-12-25\",\n \"administratorSetStatus\": \"active\",\n \"dateOfExpiration\": \"1562-11-11\",\n \"privilegeId\": \"\",\n \"providerId\": \"a8a7380b-fc46-4982-8c1a-40d23ad49d3d\",\n \"dateOfRenewal\": \"2681-05-28\",\n \"dateOfUpdate\": \"\",\n \"status\": \"active\"\n }\n },\n {\n \"compact\": \"octp\",\n \"dateOfUpdate\": \"\",\n \"jurisdiction\": \"ca\",\n \"previous\": {\n \"administratorSetStatus\": \"active\",\n \"attestations\": [\n {\n \"attestationId\": \"\",\n \"version\": \"\"\n },\n {\n \"attestationId\": \"\",\n \"version\": \"\"\n }\n ],\n \"compactTransactionId\": \"\",\n \"dateOfExpiration\": \"2729-03-06\",\n \"dateOfIssuance\": \"1056-05-16\",\n \"dateOfRenewal\": \"1164-11-30\",\n \"dateOfUpdate\": \"\",\n \"licenseJurisdiction\": \"sd\",\n \"privilegeId\": \"\",\n \"compact\": \"octp\",\n \"jurisdiction\": \"ct\",\n \"type\": \"privilege\",\n \"providerId\": \"a7ea8d0c-0f90-4b11-9dba-5d9ac92ead19\",\n \"status\": \"active\"\n },\n \"type\": \"privilegeUpdate\",\n \"updateType\": \"licenseDeactivation\",\n \"removedValues\": [\n \"\",\n \"\"\n ],\n \"licenseType\": \"speech-language pathologist\",\n \"updatedValues\": {\n \"licenseJurisdiction\": \"me\",\n \"compact\": \"octp\",\n \"jurisdiction\": \"pa\",\n \"attestations\": [\n {\n \"attestationId\": \"\",\n \"version\": \"\"\n },\n {\n \"attestationId\": \"\",\n \"version\": \"\"\n }\n ],\n \"type\": \"privilege\",\n \"compactTransactionId\": \"\",\n \"dateOfIssuance\": \"1084-10-31\",\n \"administratorSetStatus\": \"inactive\",\n \"dateOfExpiration\": \"1728-05-31\",\n \"privilegeId\": \"\",\n \"providerId\": \"7a3481b2-8eef-4265-8276-295aaf2c1876\",\n \"dateOfRenewal\": \"2380-06-02\",\n \"dateOfUpdate\": \"\",\n \"status\": \"inactive\"\n }\n }\n ],\n \"jurisdiction\": \"in\",\n \"licenseJurisdiction\": \"la\",\n \"licenseType\": \"audiologist\",\n \"privilegeId\": \"\",\n \"providerId\": \"a9aebe26-d6ee-4469-ad50-e5c6c6a8ab01\",\n \"status\": \"active\",\n \"type\": \"privilege\",\n \"investigationStatus\": \"underInvestigation\",\n \"investigations\": [\n {\n \"compact\": \"octp\",\n \"creationDate\": \"\",\n \"dateOfUpdate\": \"\",\n \"investigationId\": \"\",\n \"jurisdiction\": \"ia\",\n \"licenseType\": \"\",\n \"providerId\": \"756f6b78-d654-42fe-a4fd-268b3a64b935\",\n \"submittingUser\": \"\",\n \"type\": \"investigation\"\n },\n {\n \"compact\": \"coun\",\n \"creationDate\": \"\",\n \"dateOfUpdate\": \"\",\n \"investigationId\": \"\",\n \"jurisdiction\": \"de\",\n \"licenseType\": \"\",\n \"providerId\": \"347af0e5-ab0a-4710-abb1-cda90749e0a7\",\n \"submittingUser\": \"\",\n \"type\": \"investigation\"\n }\n ],\n \"adverseActions\": [\n {\n \"actionAgainst\": \"\",\n \"adverseActionId\": \"\",\n \"compact\": \"aslp\",\n \"creationDate\": \"1177-06-13\",\n \"dateOfUpdate\": \"\",\n \"effectiveStartDate\": \"2473-12-31\",\n \"encumbranceType\": \"\",\n \"jurisdiction\": \"ri\",\n \"licenseType\": \"\",\n \"licenseTypeAbbreviation\": \"\",\n \"providerId\": \"6e0f8dda-0eae-495f-a997-62afae8a758a\",\n \"type\": \"adverseAction\",\n \"clinicalPrivilegeActionCategories\": [\n \"\",\n \"\"\n ],\n \"effectiveLiftDate\": \"2698-07-02\",\n \"liftingUser\": \"\"\n },\n {\n \"actionAgainst\": \"\",\n \"adverseActionId\": \"\",\n \"compact\": \"coun\",\n \"creationDate\": \"1442-04-05\",\n \"dateOfUpdate\": \"\",\n \"effectiveStartDate\": \"2623-11-06\",\n \"encumbranceType\": \"\",\n \"jurisdiction\": \"ms\",\n \"licenseType\": \"\",\n \"licenseTypeAbbreviation\": \"\",\n \"providerId\": \"075c0ce7-03c6-47c0-ab4f-a86d38478e7f\",\n \"type\": \"adverseAction\",\n \"clinicalPrivilegeActionCategories\": [\n \"\",\n \"\"\n ],\n \"effectiveLiftDate\": \"1443-04-15\",\n \"liftingUser\": \"\"\n }\n ]\n }\n ],\n \"providerId\": \"9ef10b59-8448-4323-aa48-8b105bca3f19\",\n \"type\": \"provider\",\n \"suffix\": \"\",\n \"compactConnectRegisteredEmailAddress\": \"\",\n \"npi\": \"8843793071\",\n \"compactEligibility\": \"ineligible\",\n \"jurisdictionUploadedCompactEligibility\": \"ineligible\",\n \"dateOfBirth\": \"2100-12-01\",\n \"jurisdictionUploadedLicenseStatus\": \"active\",\n \"currentHomeJurisdiction\": \"nh\",\n \"militaryStatusNote\": \"\",\n \"ssnLastFour\": \"3500\",\n \"militaryStatus\": \"declined\",\n \"licenseStatus\": \"inactive\",\n \"middleName\": \"\"\n}", "code": 200, "cookie": [], "header": [ @@ -1300,7 +1300,7 @@ "value": "application/json" } ], - "id": "d8acfec2-366f-4502-8a64-a174dfacbbf3", + "id": "32f5e424-e388-494e-b6ce-32c860c40b0a", "name": "200 response", "originalRequest": { "body": {}, @@ -1358,7 +1358,7 @@ "item": [ { "event": [], - "id": "698fbcbc-7384-411d-902b-7be069c3c4cb", + "id": "7655e5b4-9d53-47a7-9819-622ae07229ba", "name": "/v1/compacts/:compact/providers/:providerId/licenses/jurisdiction/:jurisdiction/licenseType/:licenseType/encumbrance", "protocolProfileBehavior": { "disableBodyPruning": true @@ -1372,7 +1372,7 @@ "language": "json" } }, - "raw": "{\n \"encumbranceEffectiveDate\": \"1797-02-10\",\n \"encumbranceType\": \"denial\",\n \"clinicalPrivilegeActionCategories\": [\n \"\",\n \"\"\n ],\n \"clinicalPrivilegeActionCategory\": \"\"\n}" + "raw": "{\n \"clinicalPrivilegeActionCategories\": [\n \"\",\n \"\"\n ],\n \"encumbranceEffectiveDate\": \"2905-09-05\",\n \"encumbranceType\": \"required supervision\"\n}" }, "description": {}, "header": [ @@ -1461,7 +1461,7 @@ "value": "application/json" } ], - "id": "0df0c72a-6727-485e-8e43-227004e11c03", + "id": "04e562f5-2e09-4a31-bf4a-77ac405e4911", "name": "200 response", "originalRequest": { "body": { @@ -1472,7 +1472,7 @@ "language": "json" } }, - "raw": "{\n \"encumbranceEffectiveDate\": \"1797-02-10\",\n \"encumbranceType\": \"denial\",\n \"clinicalPrivilegeActionCategories\": [\n \"\",\n \"\"\n ],\n \"clinicalPrivilegeActionCategory\": \"\"\n}" + "raw": "{\n \"clinicalPrivilegeActionCategories\": [\n \"\",\n \"\"\n ],\n \"encumbranceEffectiveDate\": \"2905-09-05\",\n \"encumbranceType\": \"required supervision\"\n}" }, "header": [ { @@ -1523,7 +1523,7 @@ "item": [ { "event": [], - "id": "f3a68b3b-b1cf-47e4-89b7-fa930d57b7c9", + "id": "3d047556-d854-4fec-a72c-5a96839dea16", "name": "/v1/compacts/:compact/providers/:providerId/licenses/jurisdiction/:jurisdiction/licenseType/:licenseType/encumbrance/:encumbranceId", "protocolProfileBehavior": { "disableBodyPruning": true @@ -1537,7 +1537,7 @@ "language": "json" } }, - "raw": "{\n \"effectiveLiftDate\": \"1474-11-02\"\n}" + "raw": "{\n \"effectiveLiftDate\": \"2092-10-15\"\n}" }, "description": {}, "header": [ @@ -1637,7 +1637,7 @@ "value": "application/json" } ], - "id": "82314686-ff4e-47ac-83cb-70ea094869d1", + "id": "6ac6ef33-78cc-4d57-867a-c90f6e16a85a", "name": "200 response", "originalRequest": { "body": { @@ -1648,7 +1648,7 @@ "language": "json" } }, - "raw": "{\n \"effectiveLiftDate\": \"1474-11-02\"\n}" + "raw": "{\n \"effectiveLiftDate\": \"2092-10-15\"\n}" }, "header": [ { @@ -1706,7 +1706,7 @@ "item": [ { "event": [], - "id": "75028370-fb6b-4cb9-b73f-1d9b55f90a2c", + "id": "8b55bfc7-d740-4d69-b3f1-2fe6166e6a4b", "name": "/v1/compacts/:compact/providers/:providerId/licenses/jurisdiction/:jurisdiction/licenseType/:licenseType/investigation", "protocolProfileBehavior": { "disableBodyPruning": true @@ -1809,7 +1809,7 @@ "value": "application/json" } ], - "id": "dd1b9e80-c2ad-4485-9ae8-40f73e4d478e", + "id": "dc0b5d9d-7f79-45dc-ba00-5fd1e8878c26", "name": "200 response", "originalRequest": { "body": { @@ -1871,7 +1871,7 @@ "item": [ { "event": [], - "id": "5d607508-d8ae-4e97-befb-666602d095c6", + "id": "3b1be437-db45-48c5-ab69-76babb6be518", "name": "/v1/compacts/:compact/providers/:providerId/licenses/jurisdiction/:jurisdiction/licenseType/:licenseType/investigation/:investigationId", "protocolProfileBehavior": { "disableBodyPruning": true @@ -1885,7 +1885,7 @@ "language": "json" } }, - "raw": "{\n \"action\": \"close\",\n \"encumbrance\": {\n \"encumbranceEffectiveDate\": \"1495-02-30\",\n \"encumbranceType\": \"other monitoring\",\n \"clinicalPrivilegeActionCategories\": [\n \"\",\n \"\"\n ],\n \"clinicalPrivilegeActionCategory\": \"\"\n }\n}" + "raw": "{\n \"action\": \"close\",\n \"encumbrance\": {\n \"clinicalPrivilegeActionCategories\": [\n \"\",\n \"\"\n ],\n \"encumbranceEffectiveDate\": \"2151-01-13\",\n \"encumbranceType\": \"other monitoring\"\n }\n}" }, "description": {}, "header": [ @@ -1985,7 +1985,7 @@ "value": "application/json" } ], - "id": "978639ce-f2ef-4531-98ef-c57a4307677e", + "id": "67effa73-625d-4d26-9cc9-ddce3623cbb6", "name": "200 response", "originalRequest": { "body": { @@ -1996,7 +1996,7 @@ "language": "json" } }, - "raw": "{\n \"action\": \"close\",\n \"encumbrance\": {\n \"encumbranceEffectiveDate\": \"1495-02-30\",\n \"encumbranceType\": \"other monitoring\",\n \"clinicalPrivilegeActionCategories\": [\n \"\",\n \"\"\n ],\n \"clinicalPrivilegeActionCategory\": \"\"\n }\n}" + "raw": "{\n \"action\": \"close\",\n \"encumbrance\": {\n \"clinicalPrivilegeActionCategories\": [\n \"\",\n \"\"\n ],\n \"encumbranceEffectiveDate\": \"2151-01-13\",\n \"encumbranceType\": \"other monitoring\"\n }\n}" }, "header": [ { @@ -2069,7 +2069,7 @@ "item": [ { "event": [], - "id": "0ae318b7-2f25-49ba-9b54-d39d5d4b1c46", + "id": "5beb550c-06c4-449f-99ac-3abaa59de4de", "name": "/v1/compacts/:compact/providers/:providerId/militaryAudit", "protocolProfileBehavior": { "disableBodyPruning": true @@ -2083,7 +2083,7 @@ "language": "json" } }, - "raw": "{\n \"militaryStatus\": \"approved\",\n \"militaryStatusNote\": \"\"\n}" + "raw": "{\n \"militaryStatus\": \"declined\",\n \"militaryStatusNote\": \"\"\n}" }, "description": {}, "header": [ @@ -2147,7 +2147,7 @@ "value": "application/json" } ], - "id": "ddc3d92d-b2cf-405a-8d55-61a124a69723", + "id": "dda1b4e8-ab8a-4ced-96e3-93b86e737e78", "name": "200 response", "originalRequest": { "body": { @@ -2158,7 +2158,7 @@ "language": "json" } }, - "raw": "{\n \"militaryStatus\": \"approved\",\n \"militaryStatusNote\": \"\"\n}" + "raw": "{\n \"militaryStatus\": \"declined\",\n \"militaryStatusNote\": \"\"\n}" }, "header": [ { @@ -2222,7 +2222,7 @@ "item": [ { "event": [], - "id": "33549695-08d8-4fa6-9ec5-4eb6bbd19c4a", + "id": "2f3dff67-d4c7-4edb-aaa9-65402f3bf161", "name": "/v1/compacts/:compact/providers/:providerId/privileges/jurisdiction/:jurisdiction/licenseType/:licenseType/deactivate", "protocolProfileBehavior": { "disableBodyPruning": true @@ -2325,7 +2325,7 @@ "value": "application/json" } ], - "id": "92f1ec6f-ed5f-4364-be09-579b5942d71e", + "id": "4e7a2150-602c-4685-a580-7add6aede102", "name": "200 response", "originalRequest": { "body": { @@ -2390,7 +2390,7 @@ "item": [ { "event": [], - "id": "79f1c629-6bfb-4182-981d-c43ceb0e0de5", + "id": "29e06949-aec7-4661-95e1-d1c3364faed8", "name": "/v1/compacts/:compact/providers/:providerId/privileges/jurisdiction/:jurisdiction/licenseType/:licenseType/encumbrance", "protocolProfileBehavior": { "disableBodyPruning": true @@ -2404,7 +2404,7 @@ "language": "json" } }, - "raw": "{\n \"encumbranceEffectiveDate\": \"1797-02-10\",\n \"encumbranceType\": \"denial\",\n \"clinicalPrivilegeActionCategories\": [\n \"\",\n \"\"\n ],\n \"clinicalPrivilegeActionCategory\": \"\"\n}" + "raw": "{\n \"clinicalPrivilegeActionCategories\": [\n \"\",\n \"\"\n ],\n \"encumbranceEffectiveDate\": \"2905-09-05\",\n \"encumbranceType\": \"required supervision\"\n}" }, "description": {}, "header": [ @@ -2493,7 +2493,7 @@ "value": "application/json" } ], - "id": "98c7c85b-a66b-4b3b-aa0f-ed3d0bbb7138", + "id": "439c21b2-892b-406f-bc0a-3ad4d0578c36", "name": "200 response", "originalRequest": { "body": { @@ -2504,7 +2504,7 @@ "language": "json" } }, - "raw": "{\n \"encumbranceEffectiveDate\": \"1797-02-10\",\n \"encumbranceType\": \"denial\",\n \"clinicalPrivilegeActionCategories\": [\n \"\",\n \"\"\n ],\n \"clinicalPrivilegeActionCategory\": \"\"\n}" + "raw": "{\n \"clinicalPrivilegeActionCategories\": [\n \"\",\n \"\"\n ],\n \"encumbranceEffectiveDate\": \"2905-09-05\",\n \"encumbranceType\": \"required supervision\"\n}" }, "header": [ { @@ -2555,7 +2555,7 @@ "item": [ { "event": [], - "id": "470f9235-c954-42fc-a462-e593fda9ffb0", + "id": "18c1d24f-e10a-4161-b92a-8f8f57f76060", "name": "/v1/compacts/:compact/providers/:providerId/privileges/jurisdiction/:jurisdiction/licenseType/:licenseType/encumbrance/:encumbranceId", "protocolProfileBehavior": { "disableBodyPruning": true @@ -2569,7 +2569,7 @@ "language": "json" } }, - "raw": "{\n \"effectiveLiftDate\": \"1474-11-02\"\n}" + "raw": "{\n \"effectiveLiftDate\": \"2092-10-15\"\n}" }, "description": {}, "header": [ @@ -2669,7 +2669,7 @@ "value": "application/json" } ], - "id": "1686a0f6-5e9b-4f86-9a03-2732152c3aad", + "id": "2d39ff84-d12a-4db8-9767-aa16d160054c", "name": "200 response", "originalRequest": { "body": { @@ -2680,7 +2680,7 @@ "language": "json" } }, - "raw": "{\n \"effectiveLiftDate\": \"1474-11-02\"\n}" + "raw": "{\n \"effectiveLiftDate\": \"2092-10-15\"\n}" }, "header": [ { @@ -2738,7 +2738,7 @@ "item": [ { "event": [], - "id": "9b2de6a9-8058-4576-ab95-da18a3003b16", + "id": "39be1d52-8f57-4436-89b0-907976bc2492", "name": "/v1/compacts/:compact/providers/:providerId/privileges/jurisdiction/:jurisdiction/licenseType/:licenseType/history", "protocolProfileBehavior": { "disableBodyPruning": true @@ -2819,7 +2819,7 @@ "response": [ { "_postman_previewlanguage": "json", - "body": "{\n \"compact\": \"octp\",\n \"events\": [\n {\n \"createDate\": \"\",\n \"dateOfUpdate\": \"\",\n \"effectiveDate\": \"1680-11-27\",\n \"type\": \"privilegeUpdate\",\n \"updateType\": \"encumbrance\",\n \"note\": \"\"\n },\n {\n \"createDate\": \"\",\n \"dateOfUpdate\": \"\",\n \"effectiveDate\": \"2994-12-28\",\n \"type\": \"privilegeUpdate\",\n \"updateType\": \"other\",\n \"note\": \"\"\n }\n ],\n \"jurisdiction\": \"id\",\n \"licenseType\": \"audiologist\",\n \"privilegeId\": \"\",\n \"providerId\": \"583662f6-05d7-4dd9-af6c-ce0620242a3d\"\n}", + "body": "{\n \"compact\": \"aslp\",\n \"events\": [\n {\n \"createDate\": \"\",\n \"dateOfUpdate\": \"\",\n \"effectiveDate\": \"2221-06-30\",\n \"type\": \"privilegeUpdate\",\n \"updateType\": \"lifting_encumbrance\",\n \"note\": \"\",\n \"npdbCategories\": [\n \"\",\n \"\"\n ]\n },\n {\n \"createDate\": \"\",\n \"dateOfUpdate\": \"\",\n \"effectiveDate\": \"2677-12-30\",\n \"type\": \"privilegeUpdate\",\n \"updateType\": \"emailChange\",\n \"note\": \"\",\n \"npdbCategories\": [\n \"\",\n \"\"\n ]\n }\n ],\n \"jurisdiction\": \"ne\",\n \"licenseType\": \"occupational therapist\",\n \"privilegeId\": \"\",\n \"providerId\": \"2d4b50f1-d301-4b90-a1f3-1442018658aa\"\n}", "code": 200, "cookie": [], "header": [ @@ -2828,7 +2828,7 @@ "value": "application/json" } ], - "id": "81c3383d-956b-43b4-bde5-000fc794ad59", + "id": "0efa35c9-138d-4e5f-9103-0e9ed97f92a8", "name": "200 response", "originalRequest": { "body": {}, @@ -2880,7 +2880,7 @@ "item": [ { "event": [], - "id": "9077f521-ed60-49ca-8d52-e65d3cc152c1", + "id": "e534f900-eac2-46a8-90d6-8683788ea1dc", "name": "/v1/compacts/:compact/providers/:providerId/privileges/jurisdiction/:jurisdiction/licenseType/:licenseType/investigation", "protocolProfileBehavior": { "disableBodyPruning": true @@ -2983,7 +2983,7 @@ "value": "application/json" } ], - "id": "3758f6ae-2f15-466d-b2ef-c784232e8b72", + "id": "6ac28647-e435-4146-acd9-bee1265cdef1", "name": "200 response", "originalRequest": { "body": { @@ -3045,7 +3045,7 @@ "item": [ { "event": [], - "id": "de970ed9-28b4-4220-8e97-649c8aa27029", + "id": "df8b931e-3ce0-445c-8840-a59c59aa609d", "name": "/v1/compacts/:compact/providers/:providerId/privileges/jurisdiction/:jurisdiction/licenseType/:licenseType/investigation/:investigationId", "protocolProfileBehavior": { "disableBodyPruning": true @@ -3059,7 +3059,7 @@ "language": "json" } }, - "raw": "{\n \"action\": \"close\",\n \"encumbrance\": {\n \"encumbranceEffectiveDate\": \"1495-02-30\",\n \"encumbranceType\": \"other monitoring\",\n \"clinicalPrivilegeActionCategories\": [\n \"\",\n \"\"\n ],\n \"clinicalPrivilegeActionCategory\": \"\"\n }\n}" + "raw": "{\n \"action\": \"close\",\n \"encumbrance\": {\n \"clinicalPrivilegeActionCategories\": [\n \"\",\n \"\"\n ],\n \"encumbranceEffectiveDate\": \"2151-01-13\",\n \"encumbranceType\": \"other monitoring\"\n }\n}" }, "description": {}, "header": [ @@ -3159,7 +3159,7 @@ "value": "application/json" } ], - "id": "a802529f-e8ad-4d96-bfc1-b910057c9002", + "id": "d7224c86-fa15-4c75-8667-adf7f2380e78", "name": "200 response", "originalRequest": { "body": { @@ -3170,7 +3170,7 @@ "language": "json" } }, - "raw": "{\n \"action\": \"close\",\n \"encumbrance\": {\n \"encumbranceEffectiveDate\": \"1495-02-30\",\n \"encumbranceType\": \"other monitoring\",\n \"clinicalPrivilegeActionCategories\": [\n \"\",\n \"\"\n ],\n \"clinicalPrivilegeActionCategory\": \"\"\n }\n}" + "raw": "{\n \"action\": \"close\",\n \"encumbrance\": {\n \"clinicalPrivilegeActionCategories\": [\n \"\",\n \"\"\n ],\n \"encumbranceEffectiveDate\": \"2151-01-13\",\n \"encumbranceType\": \"other monitoring\"\n }\n}" }, "header": [ { @@ -3243,7 +3243,7 @@ "item": [ { "event": [], - "id": "4d6aaf1b-cf4d-4e78-91d0-8aecd3e3552a", + "id": "cfdfa33e-fce7-4305-a8ab-8da8df85ed01", "name": "/v1/compacts/:compact/providers/:providerId/ssn", "protocolProfileBehavior": { "disableBodyPruning": true @@ -3299,7 +3299,7 @@ "response": [ { "_postman_previewlanguage": "json", - "body": "{\n \"ssn\": \"737-45-5308\"\n}", + "body": "{\n \"ssn\": \"479-91-2384\"\n}", "code": 200, "cookie": [], "header": [ @@ -3308,7 +3308,7 @@ "value": "application/json" } ], - "id": "958fa077-b454-4bf4-98cc-5f7241593261", + "id": "15fad8d1-adae-4357-92f2-8cc2b20a408f", "name": "200 response", "originalRequest": { "body": {}, @@ -3361,7 +3361,7 @@ "item": [ { "event": [], - "id": "c264e81b-ce45-4ff3-a934-ac9d9bb0e76f", + "id": "7523bd3c-d24e-459f-a622-8490a3e0133b", "name": "/v1/compacts/:compact/staff-users", "protocolProfileBehavior": { "disableBodyPruning": true @@ -3405,7 +3405,7 @@ "response": [ { "_postman_previewlanguage": "json", - "body": "{\n \"pagination\": {\n \"prevLastKey\": {},\n \"lastKey\": {},\n \"pageSize\": \"\"\n },\n \"users\": [\n {\n \"attributes\": {\n \"email\": \"\",\n \"familyName\": \"\",\n \"givenName\": \"\"\n },\n \"permissions\": {\n \"consectetur8\": {\n \"actions\": {\n \"readPrivate\": \"\",\n \"admin\": \"\",\n \"readSSN\": \"\"\n },\n \"jurisdictions\": {\n \"labore_1\": {\n \"actions\": {\n \"readPrivate\": \"\",\n \"admin\": \"\",\n \"write\": \"\",\n \"readSSN\": \"\"\n }\n },\n \"commodo__\": {\n \"actions\": {\n \"readPrivate\": \"\",\n \"admin\": \"\",\n \"write\": \"\",\n \"readSSN\": \"\"\n }\n },\n \"est_8c7\": {\n \"actions\": {\n \"readPrivate\": \"\",\n \"admin\": \"\",\n \"write\": \"\",\n \"readSSN\": \"\"\n }\n }\n }\n }\n },\n \"status\": \"active\",\n \"userId\": \"\"\n },\n {\n \"attributes\": {\n \"email\": \"\",\n \"familyName\": \"\",\n \"givenName\": \"\"\n },\n \"permissions\": {\n \"irure1c1\": {\n \"actions\": {\n \"readPrivate\": \"\",\n \"admin\": \"\",\n \"readSSN\": \"\"\n },\n \"jurisdictions\": {\n \"elit_491\": {\n \"actions\": {\n \"readPrivate\": \"\",\n \"admin\": \"\",\n \"write\": \"\",\n \"readSSN\": \"\"\n }\n }\n }\n },\n \"veniam_d7\": {\n \"actions\": {\n \"readPrivate\": \"\",\n \"admin\": \"\",\n \"readSSN\": \"\"\n },\n \"jurisdictions\": {\n \"et6d\": {\n \"actions\": {\n \"readPrivate\": \"\",\n \"admin\": \"\",\n \"write\": \"\",\n \"readSSN\": \"\"\n }\n }\n }\n }\n },\n \"status\": \"active\",\n \"userId\": \"\"\n }\n ]\n}", + "body": "{\n \"pagination\": {\n \"prevLastKey\": {},\n \"lastKey\": {},\n \"pageSize\": \"\"\n },\n \"users\": [\n {\n \"attributes\": {\n \"email\": \"\",\n \"familyName\": \"\",\n \"givenName\": \"\"\n },\n \"permissions\": {\n \"esse_0\": {\n \"actions\": {\n \"readPrivate\": \"\",\n \"admin\": \"\",\n \"readSSN\": \"\"\n },\n \"jurisdictions\": {\n \"laborumc7\": {\n \"actions\": {\n \"readPrivate\": \"\",\n \"admin\": \"\",\n \"write\": \"\",\n \"readSSN\": \"\"\n }\n },\n \"officia3\": {\n \"actions\": {\n \"readPrivate\": \"\",\n \"admin\": \"\",\n \"write\": \"\",\n \"readSSN\": \"\"\n }\n }\n }\n }\n },\n \"status\": \"inactive\",\n \"userId\": \"\"\n },\n {\n \"attributes\": {\n \"email\": \"\",\n \"familyName\": \"\",\n \"givenName\": \"\"\n },\n \"permissions\": {\n \"ine\": {\n \"actions\": {\n \"readPrivate\": \"\",\n \"admin\": \"\",\n \"readSSN\": \"\"\n },\n \"jurisdictions\": {\n \"nulla_30\": {\n \"actions\": {\n \"readPrivate\": \"\",\n \"admin\": \"\",\n \"write\": \"\",\n \"readSSN\": \"\"\n }\n }\n }\n },\n \"ut_21\": {\n \"actions\": {\n \"readPrivate\": \"\",\n \"admin\": \"\",\n \"readSSN\": \"\"\n },\n \"jurisdictions\": {\n \"reprehenderit385\": {\n \"actions\": {\n \"readPrivate\": \"\",\n \"admin\": \"\",\n \"write\": \"\",\n \"readSSN\": \"\"\n }\n },\n \"eiusmodc\": {\n \"actions\": {\n \"readPrivate\": \"\",\n \"admin\": \"\",\n \"write\": \"\",\n \"readSSN\": \"\"\n }\n }\n }\n }\n },\n \"status\": \"inactive\",\n \"userId\": \"\"\n }\n ]\n}", "code": 200, "cookie": [], "header": [ @@ -3423,7 +3423,7 @@ "value": "" } ], - "id": "e915276d-04d4-485a-a0fe-081a193b6d9e", + "id": "f8cd207f-8149-4869-b5fc-92ed9a3bde5b", "name": "200 response", "originalRequest": { "body": {}, @@ -3462,7 +3462,7 @@ }, { "event": [], - "id": "ff7f9de4-bbff-4650-88a8-683d49d1fa01", + "id": "efdc67ca-e702-489d-a394-5b99d2d60052", "name": "/v1/compacts/:compact/staff-users", "protocolProfileBehavior": { "disableBodyPruning": true @@ -3476,7 +3476,7 @@ "language": "json" } }, - "raw": "{\n \"attributes\": {\n \"email\": \"\",\n \"familyName\": \"\",\n \"givenName\": \"\"\n },\n \"permissions\": {\n \"eiusmod_f1\": {\n \"actions\": {\n \"readPrivate\": \"\",\n \"admin\": \"\",\n \"readSSN\": \"\"\n },\n \"jurisdictions\": {\n \"irure_7\": {\n \"actions\": {\n \"readPrivate\": \"\",\n \"admin\": \"\",\n \"write\": \"\",\n \"readSSN\": \"\"\n }\n },\n \"animd\": {\n \"actions\": {\n \"readPrivate\": \"\",\n \"admin\": \"\",\n \"write\": \"\",\n \"readSSN\": \"\"\n }\n }\n }\n },\n \"reprehenderit_4\": {\n \"actions\": {\n \"readPrivate\": \"\",\n \"admin\": \"\",\n \"readSSN\": \"\"\n },\n \"jurisdictions\": {\n \"ut69\": {\n \"actions\": {\n \"readPrivate\": \"\",\n \"admin\": \"\",\n \"write\": \"\",\n \"readSSN\": \"\"\n }\n },\n \"deserunt5\": {\n \"actions\": {\n \"readPrivate\": \"\",\n \"admin\": \"\",\n \"write\": \"\",\n \"readSSN\": \"\"\n }\n },\n \"commodo_a_5\": {\n \"actions\": {\n \"readPrivate\": \"\",\n \"admin\": \"\",\n \"write\": \"\",\n \"readSSN\": \"\"\n }\n },\n \"elitcc\": {\n \"actions\": {\n \"readPrivate\": \"\",\n \"admin\": \"\",\n \"write\": \"\",\n \"readSSN\": \"\"\n }\n }\n }\n },\n \"consectetur_f\": {\n \"actions\": {\n \"readPrivate\": \"\",\n \"admin\": \"\",\n \"readSSN\": \"\"\n },\n \"jurisdictions\": {\n \"ut7_1\": {\n \"actions\": {\n \"readPrivate\": \"\",\n \"admin\": \"\",\n \"write\": \"\",\n \"readSSN\": \"\"\n }\n },\n \"consecteturf_9\": {\n \"actions\": {\n \"readPrivate\": \"\",\n \"admin\": \"\",\n \"write\": \"\",\n \"readSSN\": \"\"\n }\n },\n \"consecteturbe\": {\n \"actions\": {\n \"readPrivate\": \"\",\n \"admin\": \"\",\n \"write\": \"\",\n \"readSSN\": \"\"\n }\n }\n }\n },\n \"nulla_3b\": {\n \"actions\": {\n \"readPrivate\": \"\",\n \"admin\": \"\",\n \"readSSN\": \"\"\n },\n \"jurisdictions\": {\n \"veniamfc\": {\n \"actions\": {\n \"readPrivate\": \"\",\n \"admin\": \"\",\n \"write\": \"\",\n \"readSSN\": \"\"\n }\n }\n }\n }\n }\n}" + "raw": "{\n \"attributes\": {\n \"email\": \"\",\n \"familyName\": \"\",\n \"givenName\": \"\"\n },\n \"permissions\": {\n \"incididunt_c\": {\n \"actions\": {\n \"readPrivate\": \"\",\n \"admin\": \"\",\n \"readSSN\": \"\"\n },\n \"jurisdictions\": {\n \"sunt_18\": {\n \"actions\": {\n \"readPrivate\": \"\",\n \"admin\": \"\",\n \"write\": \"\",\n \"readSSN\": \"\"\n }\n },\n \"ea_a8\": {\n \"actions\": {\n \"readPrivate\": \"\",\n \"admin\": \"\",\n \"write\": \"\",\n \"readSSN\": \"\"\n }\n },\n \"nulla9ce\": {\n \"actions\": {\n \"readPrivate\": \"\",\n \"admin\": \"\",\n \"write\": \"\",\n \"readSSN\": \"\"\n }\n }\n }\n }\n }\n}" }, "description": {}, "header": [ @@ -3519,7 +3519,7 @@ "response": [ { "_postman_previewlanguage": "json", - "body": "{\n \"attributes\": {\n \"email\": \"\",\n \"familyName\": \"\",\n \"givenName\": \"\"\n },\n \"permissions\": {\n \"eiusmod_6cf\": {\n \"actions\": {\n \"readPrivate\": \"\",\n \"admin\": \"\",\n \"readSSN\": \"\"\n },\n \"jurisdictions\": {\n \"eu_ec\": {\n \"actions\": {\n \"readPrivate\": \"\",\n \"admin\": \"\",\n \"write\": \"\",\n \"readSSN\": \"\"\n }\n }\n }\n },\n \"adipisicing3\": {\n \"actions\": {\n \"readPrivate\": \"\",\n \"admin\": \"\",\n \"readSSN\": \"\"\n },\n \"jurisdictions\": {\n \"quis_a4b\": {\n \"actions\": {\n \"readPrivate\": \"\",\n \"admin\": \"\",\n \"write\": \"\",\n \"readSSN\": \"\"\n }\n }\n }\n },\n \"Ut_2\": {\n \"actions\": {\n \"readPrivate\": \"\",\n \"admin\": \"\",\n \"readSSN\": \"\"\n },\n \"jurisdictions\": {\n \"commodo_b03\": {\n \"actions\": {\n \"readPrivate\": \"\",\n \"admin\": \"\",\n \"write\": \"\",\n \"readSSN\": \"\"\n }\n },\n \"consequat_5b\": {\n \"actions\": {\n \"readPrivate\": \"\",\n \"admin\": \"\",\n \"write\": \"\",\n \"readSSN\": \"\"\n }\n }\n }\n }\n },\n \"status\": \"inactive\",\n \"userId\": \"\"\n}", + "body": "{\n \"attributes\": {\n \"email\": \"\",\n \"familyName\": \"\",\n \"givenName\": \"\"\n },\n \"permissions\": {\n \"occaecat55\": {\n \"actions\": {\n \"readPrivate\": \"\",\n \"admin\": \"\",\n \"readSSN\": \"\"\n },\n \"jurisdictions\": {\n \"enim_b\": {\n \"actions\": {\n \"readPrivate\": \"\",\n \"admin\": \"\",\n \"write\": \"\",\n \"readSSN\": \"\"\n }\n },\n \"nulla_e\": {\n \"actions\": {\n \"readPrivate\": \"\",\n \"admin\": \"\",\n \"write\": \"\",\n \"readSSN\": \"\"\n }\n },\n \"irure_233\": {\n \"actions\": {\n \"readPrivate\": \"\",\n \"admin\": \"\",\n \"write\": \"\",\n \"readSSN\": \"\"\n }\n }\n }\n }\n },\n \"status\": \"inactive\",\n \"userId\": \"\"\n}", "code": 200, "cookie": [], "header": [ @@ -3537,7 +3537,7 @@ "value": "" } ], - "id": "e9523381-d32a-4378-9b03-a5a12493e4de", + "id": "de7ebad8-7e84-4211-94cc-32521e8bc2af", "name": "200 response", "originalRequest": { "body": { @@ -3548,7 +3548,7 @@ "language": "json" } }, - "raw": "{\n \"attributes\": {\n \"email\": \"\",\n \"familyName\": \"\",\n \"givenName\": \"\"\n },\n \"permissions\": {\n \"eiusmod_f1\": {\n \"actions\": {\n \"readPrivate\": \"\",\n \"admin\": \"\",\n \"readSSN\": \"\"\n },\n \"jurisdictions\": {\n \"irure_7\": {\n \"actions\": {\n \"readPrivate\": \"\",\n \"admin\": \"\",\n \"write\": \"\",\n \"readSSN\": \"\"\n }\n },\n \"animd\": {\n \"actions\": {\n \"readPrivate\": \"\",\n \"admin\": \"\",\n \"write\": \"\",\n \"readSSN\": \"\"\n }\n }\n }\n },\n \"reprehenderit_4\": {\n \"actions\": {\n \"readPrivate\": \"\",\n \"admin\": \"\",\n \"readSSN\": \"\"\n },\n \"jurisdictions\": {\n \"ut69\": {\n \"actions\": {\n \"readPrivate\": \"\",\n \"admin\": \"\",\n \"write\": \"\",\n \"readSSN\": \"\"\n }\n },\n \"deserunt5\": {\n \"actions\": {\n \"readPrivate\": \"\",\n \"admin\": \"\",\n \"write\": \"\",\n \"readSSN\": \"\"\n }\n },\n \"commodo_a_5\": {\n \"actions\": {\n \"readPrivate\": \"\",\n \"admin\": \"\",\n \"write\": \"\",\n \"readSSN\": \"\"\n }\n },\n \"elitcc\": {\n \"actions\": {\n \"readPrivate\": \"\",\n \"admin\": \"\",\n \"write\": \"\",\n \"readSSN\": \"\"\n }\n }\n }\n },\n \"consectetur_f\": {\n \"actions\": {\n \"readPrivate\": \"\",\n \"admin\": \"\",\n \"readSSN\": \"\"\n },\n \"jurisdictions\": {\n \"ut7_1\": {\n \"actions\": {\n \"readPrivate\": \"\",\n \"admin\": \"\",\n \"write\": \"\",\n \"readSSN\": \"\"\n }\n },\n \"consecteturf_9\": {\n \"actions\": {\n \"readPrivate\": \"\",\n \"admin\": \"\",\n \"write\": \"\",\n \"readSSN\": \"\"\n }\n },\n \"consecteturbe\": {\n \"actions\": {\n \"readPrivate\": \"\",\n \"admin\": \"\",\n \"write\": \"\",\n \"readSSN\": \"\"\n }\n }\n }\n },\n \"nulla_3b\": {\n \"actions\": {\n \"readPrivate\": \"\",\n \"admin\": \"\",\n \"readSSN\": \"\"\n },\n \"jurisdictions\": {\n \"veniamfc\": {\n \"actions\": {\n \"readPrivate\": \"\",\n \"admin\": \"\",\n \"write\": \"\",\n \"readSSN\": \"\"\n }\n }\n }\n }\n }\n}" + "raw": "{\n \"attributes\": {\n \"email\": \"\",\n \"familyName\": \"\",\n \"givenName\": \"\"\n },\n \"permissions\": {\n \"incididunt_c\": {\n \"actions\": {\n \"readPrivate\": \"\",\n \"admin\": \"\",\n \"readSSN\": \"\"\n },\n \"jurisdictions\": {\n \"sunt_18\": {\n \"actions\": {\n \"readPrivate\": \"\",\n \"admin\": \"\",\n \"write\": \"\",\n \"readSSN\": \"\"\n }\n },\n \"ea_a8\": {\n \"actions\": {\n \"readPrivate\": \"\",\n \"admin\": \"\",\n \"write\": \"\",\n \"readSSN\": \"\"\n }\n },\n \"nulla9ce\": {\n \"actions\": {\n \"readPrivate\": \"\",\n \"admin\": \"\",\n \"write\": \"\",\n \"readSSN\": \"\"\n }\n }\n }\n }\n }\n}" }, "header": [ { @@ -3592,7 +3592,7 @@ "item": [ { "event": [], - "id": "4354e238-aad8-4067-a977-5f6ad155cf8c", + "id": "f32ca342-35e1-491a-82b3-62ce3312d4bd", "name": "/v1/compacts/:compact/staff-users/:userId", "protocolProfileBehavior": { "disableBodyPruning": true @@ -3647,7 +3647,7 @@ "response": [ { "_postman_previewlanguage": "json", - "body": "{\n \"attributes\": {\n \"email\": \"\",\n \"familyName\": \"\",\n \"givenName\": \"\"\n },\n \"permissions\": {\n \"eiusmod_6cf\": {\n \"actions\": {\n \"readPrivate\": \"\",\n \"admin\": \"\",\n \"readSSN\": \"\"\n },\n \"jurisdictions\": {\n \"eu_ec\": {\n \"actions\": {\n \"readPrivate\": \"\",\n \"admin\": \"\",\n \"write\": \"\",\n \"readSSN\": \"\"\n }\n }\n }\n },\n \"adipisicing3\": {\n \"actions\": {\n \"readPrivate\": \"\",\n \"admin\": \"\",\n \"readSSN\": \"\"\n },\n \"jurisdictions\": {\n \"quis_a4b\": {\n \"actions\": {\n \"readPrivate\": \"\",\n \"admin\": \"\",\n \"write\": \"\",\n \"readSSN\": \"\"\n }\n }\n }\n },\n \"Ut_2\": {\n \"actions\": {\n \"readPrivate\": \"\",\n \"admin\": \"\",\n \"readSSN\": \"\"\n },\n \"jurisdictions\": {\n \"commodo_b03\": {\n \"actions\": {\n \"readPrivate\": \"\",\n \"admin\": \"\",\n \"write\": \"\",\n \"readSSN\": \"\"\n }\n },\n \"consequat_5b\": {\n \"actions\": {\n \"readPrivate\": \"\",\n \"admin\": \"\",\n \"write\": \"\",\n \"readSSN\": \"\"\n }\n }\n }\n }\n },\n \"status\": \"inactive\",\n \"userId\": \"\"\n}", + "body": "{\n \"attributes\": {\n \"email\": \"\",\n \"familyName\": \"\",\n \"givenName\": \"\"\n },\n \"permissions\": {\n \"occaecat55\": {\n \"actions\": {\n \"readPrivate\": \"\",\n \"admin\": \"\",\n \"readSSN\": \"\"\n },\n \"jurisdictions\": {\n \"enim_b\": {\n \"actions\": {\n \"readPrivate\": \"\",\n \"admin\": \"\",\n \"write\": \"\",\n \"readSSN\": \"\"\n }\n },\n \"nulla_e\": {\n \"actions\": {\n \"readPrivate\": \"\",\n \"admin\": \"\",\n \"write\": \"\",\n \"readSSN\": \"\"\n }\n },\n \"irure_233\": {\n \"actions\": {\n \"readPrivate\": \"\",\n \"admin\": \"\",\n \"write\": \"\",\n \"readSSN\": \"\"\n }\n }\n }\n }\n },\n \"status\": \"inactive\",\n \"userId\": \"\"\n}", "code": 200, "cookie": [], "header": [ @@ -3665,7 +3665,7 @@ "value": "" } ], - "id": "3ae9c8f4-06c7-4eae-a7a7-ddddeecdaaa6", + "id": "103f06ab-de81-4f8b-a8af-dd65eacb2e83", "name": "200 response", "originalRequest": { "body": {}, @@ -3712,7 +3712,7 @@ "value": "application/json" } ], - "id": "5684861e-0d48-4833-8a81-f008d5f31316", + "id": "6cd305ae-5bc9-485c-87df-3e831c70f2bd", "name": "404 response", "originalRequest": { "body": {}, @@ -3752,7 +3752,7 @@ }, { "event": [], - "id": "c1ab59ca-f3e7-4f31-b2ef-02e8c914a77d", + "id": "f85b4438-0095-4dd6-a882-5d770f0f03a9", "name": "/v1/compacts/:compact/staff-users/:userId", "protocolProfileBehavior": { "disableBodyPruning": true @@ -3816,7 +3816,7 @@ "value": "application/json" } ], - "id": "88f96571-7f15-4875-a43b-563a923cbd5f", + "id": "464cd545-8fd0-4390-b3a2-707dd48449c6", "name": "200 response", "originalRequest": { "body": {}, @@ -3863,7 +3863,7 @@ "value": "application/json" } ], - "id": "75415609-04e6-49a9-bb3d-42fec15c254e", + "id": "8dfea1ba-f457-4481-8c19-9dc550128efe", "name": "404 response", "originalRequest": { "body": {}, @@ -3903,7 +3903,7 @@ }, { "event": [], - "id": "5ec44c02-5907-4fa7-a6c9-2b475784ae55", + "id": "c92d865a-0d71-47e2-8491-33890009189b", "name": "/v1/compacts/:compact/staff-users/:userId", "protocolProfileBehavior": { "disableBodyPruning": true @@ -3917,7 +3917,7 @@ "language": "json" } }, - "raw": "{\n \"permissions\": {\n \"in__\": {\n \"actions\": {\n \"readPrivate\": \"\",\n \"admin\": \"\",\n \"readSSN\": \"\"\n },\n \"jurisdictions\": {\n \"mollita_\": {\n \"actions\": {\n \"readPrivate\": \"\",\n \"admin\": \"\",\n \"write\": \"\",\n \"readSSN\": \"\"\n }\n }\n }\n }\n }\n}" + "raw": "{\n \"permissions\": {\n \"Duis_c4b\": {\n \"actions\": {\n \"readPrivate\": \"\",\n \"admin\": \"\",\n \"readSSN\": \"\"\n },\n \"jurisdictions\": {\n \"ametc\": {\n \"actions\": {\n \"readPrivate\": \"\",\n \"admin\": \"\",\n \"write\": \"\",\n \"readSSN\": \"\"\n }\n }\n }\n },\n \"aute_0\": {\n \"actions\": {\n \"readPrivate\": \"\",\n \"admin\": \"\",\n \"readSSN\": \"\"\n },\n \"jurisdictions\": {\n \"magna_5\": {\n \"actions\": {\n \"readPrivate\": \"\",\n \"admin\": \"\",\n \"write\": \"\",\n \"readSSN\": \"\"\n }\n }\n }\n },\n \"dof5c\": {\n \"actions\": {\n \"readPrivate\": \"\",\n \"admin\": \"\",\n \"readSSN\": \"\"\n },\n \"jurisdictions\": {\n \"utb3\": {\n \"actions\": {\n \"readPrivate\": \"\",\n \"admin\": \"\",\n \"write\": \"\",\n \"readSSN\": \"\"\n }\n },\n \"id9e3\": {\n \"actions\": {\n \"readPrivate\": \"\",\n \"admin\": \"\",\n \"write\": \"\",\n \"readSSN\": \"\"\n }\n }\n }\n }\n }\n}" }, "description": {}, "header": [ @@ -3971,7 +3971,7 @@ "response": [ { "_postman_previewlanguage": "json", - "body": "{\n \"attributes\": {\n \"email\": \"\",\n \"familyName\": \"\",\n \"givenName\": \"\"\n },\n \"permissions\": {\n \"eiusmod_6cf\": {\n \"actions\": {\n \"readPrivate\": \"\",\n \"admin\": \"\",\n \"readSSN\": \"\"\n },\n \"jurisdictions\": {\n \"eu_ec\": {\n \"actions\": {\n \"readPrivate\": \"\",\n \"admin\": \"\",\n \"write\": \"\",\n \"readSSN\": \"\"\n }\n }\n }\n },\n \"adipisicing3\": {\n \"actions\": {\n \"readPrivate\": \"\",\n \"admin\": \"\",\n \"readSSN\": \"\"\n },\n \"jurisdictions\": {\n \"quis_a4b\": {\n \"actions\": {\n \"readPrivate\": \"\",\n \"admin\": \"\",\n \"write\": \"\",\n \"readSSN\": \"\"\n }\n }\n }\n },\n \"Ut_2\": {\n \"actions\": {\n \"readPrivate\": \"\",\n \"admin\": \"\",\n \"readSSN\": \"\"\n },\n \"jurisdictions\": {\n \"commodo_b03\": {\n \"actions\": {\n \"readPrivate\": \"\",\n \"admin\": \"\",\n \"write\": \"\",\n \"readSSN\": \"\"\n }\n },\n \"consequat_5b\": {\n \"actions\": {\n \"readPrivate\": \"\",\n \"admin\": \"\",\n \"write\": \"\",\n \"readSSN\": \"\"\n }\n }\n }\n }\n },\n \"status\": \"inactive\",\n \"userId\": \"\"\n}", + "body": "{\n \"attributes\": {\n \"email\": \"\",\n \"familyName\": \"\",\n \"givenName\": \"\"\n },\n \"permissions\": {\n \"occaecat55\": {\n \"actions\": {\n \"readPrivate\": \"\",\n \"admin\": \"\",\n \"readSSN\": \"\"\n },\n \"jurisdictions\": {\n \"enim_b\": {\n \"actions\": {\n \"readPrivate\": \"\",\n \"admin\": \"\",\n \"write\": \"\",\n \"readSSN\": \"\"\n }\n },\n \"nulla_e\": {\n \"actions\": {\n \"readPrivate\": \"\",\n \"admin\": \"\",\n \"write\": \"\",\n \"readSSN\": \"\"\n }\n },\n \"irure_233\": {\n \"actions\": {\n \"readPrivate\": \"\",\n \"admin\": \"\",\n \"write\": \"\",\n \"readSSN\": \"\"\n }\n }\n }\n }\n },\n \"status\": \"inactive\",\n \"userId\": \"\"\n}", "code": 200, "cookie": [], "header": [ @@ -3989,7 +3989,7 @@ "value": "" } ], - "id": "91da90aa-d7e6-4aca-8274-e3b8010564a0", + "id": "baf33227-87c8-466f-9d74-ec362950b932", "name": "200 response", "originalRequest": { "body": { @@ -4000,7 +4000,7 @@ "language": "json" } }, - "raw": "{\n \"permissions\": {\n \"in__\": {\n \"actions\": {\n \"readPrivate\": \"\",\n \"admin\": \"\",\n \"readSSN\": \"\"\n },\n \"jurisdictions\": {\n \"mollita_\": {\n \"actions\": {\n \"readPrivate\": \"\",\n \"admin\": \"\",\n \"write\": \"\",\n \"readSSN\": \"\"\n }\n }\n }\n }\n }\n}" + "raw": "{\n \"permissions\": {\n \"Duis_c4b\": {\n \"actions\": {\n \"readPrivate\": \"\",\n \"admin\": \"\",\n \"readSSN\": \"\"\n },\n \"jurisdictions\": {\n \"ametc\": {\n \"actions\": {\n \"readPrivate\": \"\",\n \"admin\": \"\",\n \"write\": \"\",\n \"readSSN\": \"\"\n }\n }\n }\n },\n \"aute_0\": {\n \"actions\": {\n \"readPrivate\": \"\",\n \"admin\": \"\",\n \"readSSN\": \"\"\n },\n \"jurisdictions\": {\n \"magna_5\": {\n \"actions\": {\n \"readPrivate\": \"\",\n \"admin\": \"\",\n \"write\": \"\",\n \"readSSN\": \"\"\n }\n }\n }\n },\n \"dof5c\": {\n \"actions\": {\n \"readPrivate\": \"\",\n \"admin\": \"\",\n \"readSSN\": \"\"\n },\n \"jurisdictions\": {\n \"utb3\": {\n \"actions\": {\n \"readPrivate\": \"\",\n \"admin\": \"\",\n \"write\": \"\",\n \"readSSN\": \"\"\n }\n },\n \"id9e3\": {\n \"actions\": {\n \"readPrivate\": \"\",\n \"admin\": \"\",\n \"write\": \"\",\n \"readSSN\": \"\"\n }\n }\n }\n }\n }\n}" }, "header": [ { @@ -4049,7 +4049,7 @@ "value": "application/json" } ], - "id": "d4e2e5cc-7ada-4e4c-8126-f6f4a27a5e9a", + "id": "1fdac09b-baae-40ad-83ce-96f682d4cd42", "name": "404 response", "originalRequest": { "body": { @@ -4060,7 +4060,7 @@ "language": "json" } }, - "raw": "{\n \"permissions\": {\n \"in__\": {\n \"actions\": {\n \"readPrivate\": \"\",\n \"admin\": \"\",\n \"readSSN\": \"\"\n },\n \"jurisdictions\": {\n \"mollita_\": {\n \"actions\": {\n \"readPrivate\": \"\",\n \"admin\": \"\",\n \"write\": \"\",\n \"readSSN\": \"\"\n }\n }\n }\n }\n }\n}" + "raw": "{\n \"permissions\": {\n \"Duis_c4b\": {\n \"actions\": {\n \"readPrivate\": \"\",\n \"admin\": \"\",\n \"readSSN\": \"\"\n },\n \"jurisdictions\": {\n \"ametc\": {\n \"actions\": {\n \"readPrivate\": \"\",\n \"admin\": \"\",\n \"write\": \"\",\n \"readSSN\": \"\"\n }\n }\n }\n },\n \"aute_0\": {\n \"actions\": {\n \"readPrivate\": \"\",\n \"admin\": \"\",\n \"readSSN\": \"\"\n },\n \"jurisdictions\": {\n \"magna_5\": {\n \"actions\": {\n \"readPrivate\": \"\",\n \"admin\": \"\",\n \"write\": \"\",\n \"readSSN\": \"\"\n }\n }\n }\n },\n \"dof5c\": {\n \"actions\": {\n \"readPrivate\": \"\",\n \"admin\": \"\",\n \"readSSN\": \"\"\n },\n \"jurisdictions\": {\n \"utb3\": {\n \"actions\": {\n \"readPrivate\": \"\",\n \"admin\": \"\",\n \"write\": \"\",\n \"readSSN\": \"\"\n }\n },\n \"id9e3\": {\n \"actions\": {\n \"readPrivate\": \"\",\n \"admin\": \"\",\n \"write\": \"\",\n \"readSSN\": \"\"\n }\n }\n }\n }\n }\n}" }, "header": [ { @@ -4105,7 +4105,7 @@ "item": [ { "event": [], - "id": "2bc4edca-4c04-4a49-a1ef-cb32ea0ff815", + "id": "e9a668e1-3c7e-4968-8f72-42084cd47780", "name": "/v1/compacts/:compact/staff-users/:userId/reinvite", "protocolProfileBehavior": { "disableBodyPruning": true @@ -4170,7 +4170,7 @@ "value": "application/json" } ], - "id": "37dcf7e1-99a3-4ed6-8561-a6109227e31f", + "id": "7ff4c00d-c9f5-4557-bf78-666d2e2fe2c0", "name": "200 response", "originalRequest": { "body": {}, @@ -4218,7 +4218,7 @@ "value": "application/json" } ], - "id": "6f17a2fb-b055-4943-9004-4be81e5769a7", + "id": "953d0a31-0287-46f3-b100-7b5e7e9c7794", "name": "404 response", "originalRequest": { "body": {}, @@ -4283,7 +4283,7 @@ "item": [ { "event": [], - "id": "5dac1ee7-d35c-4511-ae08-3592dcee9ea5", + "id": "0c80458c-7b75-498f-ad17-4edee62986b3", "name": "/v1/flags/:flagId/check", "protocolProfileBehavior": { "disableBodyPruning": true @@ -4300,7 +4300,7 @@ "language": "json" } }, - "raw": "{\n \"context\": {\n \"userId\": \"\",\n \"customAttributes\": {\n \"exercitation_\": \"\"\n }\n }\n}" + "raw": "{\n \"context\": {\n \"userId\": \"\",\n \"customAttributes\": {\n \"fugiat_a\": \"\"\n }\n }\n}" }, "description": {}, "header": [ @@ -4352,7 +4352,7 @@ "value": "application/json" } ], - "id": "7aa3319c-3567-492c-a462-c70e36ec6858", + "id": "1fb52ed8-738d-4fcd-8832-c2a2187412cf", "name": "200 response", "originalRequest": { "body": { @@ -4363,7 +4363,7 @@ "language": "json" } }, - "raw": "{\n \"context\": {\n \"userId\": \"\",\n \"customAttributes\": {\n \"exercitation_\": \"\"\n }\n }\n}" + "raw": "{\n \"context\": {\n \"userId\": \"\",\n \"customAttributes\": {\n \"fugiat_a\": \"\"\n }\n }\n}" }, "header": [ { @@ -4421,7 +4421,7 @@ "item": [ { "event": [], - "id": "0af77e59-8737-4510-8d25-578e77c5cdd2", + "id": "31f54d3e-12fd-4974-b457-f18244dee628", "name": "/v1/provider-users/initiateRecovery", "protocolProfileBehavior": { "disableBodyPruning": true @@ -4438,7 +4438,7 @@ "language": "json" } }, - "raw": "{\n \"compact\": \"aslp\",\n \"dob\": \"1907-08-04\",\n \"familyName\": \"\",\n \"givenName\": \"\",\n \"jurisdiction\": \"ak\",\n \"licenseType\": \"audiologist\",\n \"partialSocial\": \"2423\",\n \"password\": \"\",\n \"recaptchaToken\": \"\",\n \"username\": \"\"\n}" + "raw": "{\n \"compact\": \"octp\",\n \"dob\": \"2067-12-02\",\n \"familyName\": \"\",\n \"givenName\": \"\",\n \"jurisdiction\": \"in\",\n \"licenseType\": \"audiologist\",\n \"partialSocial\": \"9228\",\n \"password\": \"\",\n \"recaptchaToken\": \"\",\n \"username\": \"\"\n}" }, "description": {}, "header": [ @@ -4478,7 +4478,7 @@ "value": "application/json" } ], - "id": "c024bdc9-a963-4a9b-9f78-15ab05d8ae84", + "id": "8d5f7bf5-9403-4366-a280-45259718043c", "name": "200 response", "originalRequest": { "body": { @@ -4489,7 +4489,7 @@ "language": "json" } }, - "raw": "{\n \"compact\": \"aslp\",\n \"dob\": \"1907-08-04\",\n \"familyName\": \"\",\n \"givenName\": \"\",\n \"jurisdiction\": \"ak\",\n \"licenseType\": \"audiologist\",\n \"partialSocial\": \"2423\",\n \"password\": \"\",\n \"recaptchaToken\": \"\",\n \"username\": \"\"\n}" + "raw": "{\n \"compact\": \"octp\",\n \"dob\": \"2067-12-02\",\n \"familyName\": \"\",\n \"givenName\": \"\",\n \"jurisdiction\": \"in\",\n \"licenseType\": \"audiologist\",\n \"partialSocial\": \"9228\",\n \"password\": \"\",\n \"recaptchaToken\": \"\",\n \"username\": \"\"\n}" }, "header": [ { @@ -4527,7 +4527,7 @@ "item": [ { "event": [], - "id": "5e0a9bd9-8096-4faa-b907-a70aeccb6071", + "id": "ff78afcc-5146-4c97-a3a0-f2468b6d6a64", "name": "/v1/provider-users/me", "protocolProfileBehavior": { "disableBodyPruning": true @@ -4559,7 +4559,7 @@ "response": [ { "_postman_previewlanguage": "json", - "body": "{\n \"birthMonthDay\": \"11-17\",\n \"compact\": \"aslp\",\n \"dateOfExpiration\": \"1016-04-15\",\n \"dateOfUpdate\": \"\",\n \"familyName\": \"\",\n \"givenName\": \"\",\n \"licenseJurisdiction\": \"mt\",\n \"licenses\": [\n {\n \"compact\": \"octp\",\n \"compactEligibility\": \"ineligible\",\n \"dateOfExpiration\": \"2174-01-13\",\n \"dateOfIssuance\": \"2125-08-07\",\n \"dateOfRenewal\": \"2884-03-30\",\n \"dateOfUpdate\": \"\",\n \"familyName\": \"\",\n \"givenName\": \"\",\n \"history\": [\n {\n \"compact\": \"coun\",\n \"dateOfUpdate\": \"\",\n \"jurisdiction\": \"ok\",\n \"previous\": {\n \"dateOfExpiration\": \"2765-01-27\",\n \"dateOfIssuance\": \"2200-04-07\",\n \"dateOfRenewal\": \"1960-12-31\",\n \"familyName\": \"\",\n \"givenName\": \"\",\n \"homeAddressCity\": \"\",\n \"homeAddressPostalCode\": \"\",\n \"homeAddressState\": \"\",\n \"homeAddressStreet1\": \"\",\n \"jurisdictionUploadedCompactEligibility\": \"eligible\",\n \"jurisdictionUploadedLicenseStatus\": \"active\",\n \"middleName\": \"\",\n \"homeAddressStreet2\": \"\",\n \"npi\": \"4323861062\",\n \"compactEligibility\": \"eligible\",\n \"dateOfBirth\": \"2605-04-10\",\n \"suffix\": \"\",\n \"emailAddress\": \"\",\n \"phoneNumber\": \"+876228646884801\",\n \"licenseStatus\": \"active\",\n \"licenseNumber\": \"\",\n \"licenseStatusName\": \"\"\n },\n \"type\": \"licenseUpdate\",\n \"updateType\": \"licenseDeactivation\",\n \"removedValues\": [\n \"\",\n \"\"\n ],\n \"licenseType\": \"speech-language pathologist\",\n \"updatedValues\": {\n \"homeAddressStreet2\": \"\",\n \"npi\": \"5362356702\",\n \"homeAddressPostalCode\": \"\",\n \"givenName\": \"\",\n \"homeAddressStreet1\": \"\",\n \"compactEligibility\": \"ineligible\",\n \"jurisdictionUploadedCompactEligibility\": \"eligible\",\n \"dateOfBirth\": \"2573-10-02\",\n \"jurisdictionUploadedLicenseStatus\": \"inactive\",\n \"suffix\": \"\",\n \"dateOfIssuance\": \"1792-01-14\",\n \"emailAddress\": \"\",\n \"dateOfExpiration\": \"1556-01-31\",\n \"phoneNumber\": \"+04732654\",\n \"homeAddressState\": \"\",\n \"dateOfRenewal\": \"1887-10-03\",\n \"licenseStatus\": \"active\",\n \"familyName\": \"\",\n \"homeAddressCity\": \"\",\n \"licenseNumber\": \"\",\n \"middleName\": \"\",\n \"licenseStatusName\": \"\"\n }\n },\n {\n \"compact\": \"coun\",\n \"dateOfUpdate\": \"\",\n \"jurisdiction\": \"in\",\n \"previous\": {\n \"dateOfExpiration\": \"2136-12-01\",\n \"dateOfIssuance\": \"1696-01-31\",\n \"dateOfRenewal\": \"1964-10-31\",\n \"familyName\": \"\",\n \"givenName\": \"\",\n \"homeAddressCity\": \"\",\n \"homeAddressPostalCode\": \"\",\n \"homeAddressState\": \"\",\n \"homeAddressStreet1\": \"\",\n \"jurisdictionUploadedCompactEligibility\": \"eligible\",\n \"jurisdictionUploadedLicenseStatus\": \"inactive\",\n \"middleName\": \"\",\n \"homeAddressStreet2\": \"\",\n \"npi\": \"4155100534\",\n \"compactEligibility\": \"eligible\",\n \"dateOfBirth\": \"2824-07-03\",\n \"suffix\": \"\",\n \"emailAddress\": \"\",\n \"phoneNumber\": \"+38146683\",\n \"licenseStatus\": \"active\",\n \"licenseNumber\": \"\",\n \"licenseStatusName\": \"\"\n },\n \"type\": \"licenseUpdate\",\n \"updateType\": \"deactivation\",\n \"removedValues\": [\n \"\",\n \"\"\n ],\n \"licenseType\": \"speech-language pathologist\",\n \"updatedValues\": {\n \"homeAddressStreet2\": \"\",\n \"npi\": \"8589103256\",\n \"homeAddressPostalCode\": \"\",\n \"givenName\": \"\",\n \"homeAddressStreet1\": \"\",\n \"compactEligibility\": \"ineligible\",\n \"jurisdictionUploadedCompactEligibility\": \"eligible\",\n \"dateOfBirth\": \"1671-10-13\",\n \"jurisdictionUploadedLicenseStatus\": \"active\",\n \"suffix\": \"\",\n \"dateOfIssuance\": \"2218-12-11\",\n \"emailAddress\": \"\",\n \"dateOfExpiration\": \"2539-10-14\",\n \"phoneNumber\": \"+86007188095\",\n \"homeAddressState\": \"\",\n \"dateOfRenewal\": \"1897-01-08\",\n \"licenseStatus\": \"inactive\",\n \"familyName\": \"\",\n \"homeAddressCity\": \"\",\n \"licenseNumber\": \"\",\n \"middleName\": \"\",\n \"licenseStatusName\": \"\"\n }\n }\n ],\n \"homeAddressCity\": \"\",\n \"homeAddressPostalCode\": \"\",\n \"homeAddressState\": \"\",\n \"homeAddressStreet1\": \"\",\n \"jurisdiction\": \"mn\",\n \"jurisdictionUploadedCompactEligibility\": \"ineligible\",\n \"jurisdictionUploadedLicenseStatus\": \"active\",\n \"licenseStatus\": \"inactive\",\n \"licenseType\": \"occupational therapy assistant\",\n \"middleName\": \"\",\n \"providerId\": \"cfdbcbbc-6c72-4deb-8651-2fd9915f5573\",\n \"type\": \"license-home\",\n \"homeAddressStreet2\": \"\",\n \"investigations\": [\n {\n \"compact\": \"coun\",\n \"creationDate\": \"\",\n \"dateOfUpdate\": \"\",\n \"investigationId\": \"\",\n \"jurisdiction\": \"me\",\n \"licenseType\": \"\",\n \"providerId\": \"63b94041-8f0b-43c5-971a-478833b5d6e6\",\n \"submittingUser\": \"\",\n \"type\": \"investigation\"\n },\n {\n \"compact\": \"coun\",\n \"creationDate\": \"\",\n \"dateOfUpdate\": \"\",\n \"investigationId\": \"\",\n \"jurisdiction\": \"ny\",\n \"licenseType\": \"\",\n \"providerId\": \"969c9a49-d226-443b-aaa1-dcd93df0300a\",\n \"submittingUser\": \"\",\n \"type\": \"investigation\"\n }\n ],\n \"suffix\": \"\",\n \"emailAddress\": \"\",\n \"licenseNumber\": \"\",\n \"investigationStatus\": \"underInvestigation\",\n \"npi\": \"9667390556\",\n \"dateOfBirth\": \"2014-10-28\",\n \"ssnLastFour\": \"5961\",\n \"phoneNumber\": \"+215555457776743\",\n \"licenseStatusName\": \"\",\n \"adverseActions\": [\n {\n \"actionAgainst\": \"\",\n \"adverseActionId\": \"\",\n \"compact\": \"octp\",\n \"creationDate\": \"2979-12-30\",\n \"dateOfUpdate\": \"\",\n \"effectiveStartDate\": \"2803-07-04\",\n \"encumbranceType\": \"\",\n \"jurisdiction\": \"me\",\n \"licenseType\": \"\",\n \"licenseTypeAbbreviation\": \"\",\n \"providerId\": \"ea82b231-5b5a-41a1-bd61-170eda210d44\",\n \"type\": \"adverseAction\",\n \"clinicalPrivilegeActionCategories\": [\n \"\",\n \"\"\n ],\n \"effectiveLiftDate\": \"1792-11-04\",\n \"clinicalPrivilegeActionCategory\": \"\",\n \"liftingUser\": \"\"\n },\n {\n \"actionAgainst\": \"\",\n \"adverseActionId\": \"\",\n \"compact\": \"aslp\",\n \"creationDate\": \"2045-12-16\",\n \"dateOfUpdate\": \"\",\n \"effectiveStartDate\": \"2301-05-09\",\n \"encumbranceType\": \"\",\n \"jurisdiction\": \"id\",\n \"licenseType\": \"\",\n \"licenseTypeAbbreviation\": \"\",\n \"providerId\": \"8d7a80d6-0190-4b0a-8563-1fb067203716\",\n \"type\": \"adverseAction\",\n \"clinicalPrivilegeActionCategories\": [\n \"\",\n \"\"\n ],\n \"effectiveLiftDate\": \"1125-04-31\",\n \"clinicalPrivilegeActionCategory\": \"\",\n \"liftingUser\": \"\"\n }\n ]\n },\n {\n \"compact\": \"octp\",\n \"compactEligibility\": \"ineligible\",\n \"dateOfExpiration\": \"1160-11-31\",\n \"dateOfIssuance\": \"2576-11-31\",\n \"dateOfRenewal\": \"2046-09-25\",\n \"dateOfUpdate\": \"\",\n \"familyName\": \"\",\n \"givenName\": \"\",\n \"history\": [\n {\n \"compact\": \"octp\",\n \"dateOfUpdate\": \"\",\n \"jurisdiction\": \"md\",\n \"previous\": {\n \"dateOfExpiration\": \"1355-01-03\",\n \"dateOfIssuance\": \"2864-04-16\",\n \"dateOfRenewal\": \"1651-04-11\",\n \"familyName\": \"\",\n \"givenName\": \"\",\n \"homeAddressCity\": \"\",\n \"homeAddressPostalCode\": \"\",\n \"homeAddressState\": \"\",\n \"homeAddressStreet1\": \"\",\n \"jurisdictionUploadedCompactEligibility\": \"eligible\",\n \"jurisdictionUploadedLicenseStatus\": \"inactive\",\n \"middleName\": \"\",\n \"homeAddressStreet2\": \"\",\n \"npi\": \"7235513969\",\n \"compactEligibility\": \"eligible\",\n \"dateOfBirth\": \"2108-12-30\",\n \"suffix\": \"\",\n \"emailAddress\": \"\",\n \"phoneNumber\": \"+9730063408781\",\n \"licenseStatus\": \"active\",\n \"licenseNumber\": \"\",\n \"licenseStatusName\": \"\"\n },\n \"type\": \"licenseUpdate\",\n \"updateType\": \"expiration\",\n \"removedValues\": [\n \"\",\n \"\"\n ],\n \"licenseType\": \"speech-language pathologist\",\n \"updatedValues\": {\n \"homeAddressStreet2\": \"\",\n \"npi\": \"4979399694\",\n \"homeAddressPostalCode\": \"\",\n \"givenName\": \"\",\n \"homeAddressStreet1\": \"\",\n \"compactEligibility\": \"ineligible\",\n \"jurisdictionUploadedCompactEligibility\": \"eligible\",\n \"dateOfBirth\": \"1169-04-20\",\n \"jurisdictionUploadedLicenseStatus\": \"inactive\",\n \"suffix\": \"\",\n \"dateOfIssuance\": \"2610-03-30\",\n \"emailAddress\": \"\",\n \"dateOfExpiration\": \"1235-06-10\",\n \"phoneNumber\": \"+0624416018\",\n \"homeAddressState\": \"\",\n \"dateOfRenewal\": \"2120-08-29\",\n \"licenseStatus\": \"active\",\n \"familyName\": \"\",\n \"homeAddressCity\": \"\",\n \"licenseNumber\": \"\",\n \"middleName\": \"\",\n \"licenseStatusName\": \"\"\n }\n },\n {\n \"compact\": \"coun\",\n \"dateOfUpdate\": \"\",\n \"jurisdiction\": \"mt\",\n \"previous\": {\n \"dateOfExpiration\": \"2247-12-07\",\n \"dateOfIssuance\": \"1732-04-07\",\n \"dateOfRenewal\": \"2490-01-20\",\n \"familyName\": \"\",\n \"givenName\": \"\",\n \"homeAddressCity\": \"\",\n \"homeAddressPostalCode\": \"\",\n \"homeAddressState\": \"\",\n \"homeAddressStreet1\": \"\",\n \"jurisdictionUploadedCompactEligibility\": \"ineligible\",\n \"jurisdictionUploadedLicenseStatus\": \"inactive\",\n \"middleName\": \"\",\n \"homeAddressStreet2\": \"\",\n \"npi\": \"4954399462\",\n \"compactEligibility\": \"eligible\",\n \"dateOfBirth\": \"1542-12-01\",\n \"suffix\": \"\",\n \"emailAddress\": \"\",\n \"phoneNumber\": \"+06725848936\",\n \"licenseStatus\": \"inactive\",\n \"licenseNumber\": \"\",\n \"licenseStatusName\": \"\"\n },\n \"type\": \"licenseUpdate\",\n \"updateType\": \"emailChange\",\n \"removedValues\": [\n \"\",\n \"\"\n ],\n \"licenseType\": \"occupational therapy assistant\",\n \"updatedValues\": {\n \"homeAddressStreet2\": \"\",\n \"npi\": \"1595699795\",\n \"homeAddressPostalCode\": \"\",\n \"givenName\": \"\",\n \"homeAddressStreet1\": \"\",\n \"compactEligibility\": \"ineligible\",\n \"jurisdictionUploadedCompactEligibility\": \"eligible\",\n \"dateOfBirth\": \"2091-12-09\",\n \"jurisdictionUploadedLicenseStatus\": \"inactive\",\n \"suffix\": \"\",\n \"dateOfIssuance\": \"2418-05-05\",\n \"emailAddress\": \"\",\n \"dateOfExpiration\": \"2702-10-02\",\n \"phoneNumber\": \"+37788018735632\",\n \"homeAddressState\": \"\",\n \"dateOfRenewal\": \"1105-10-31\",\n \"licenseStatus\": \"inactive\",\n \"familyName\": \"\",\n \"homeAddressCity\": \"\",\n \"licenseNumber\": \"\",\n \"middleName\": \"\",\n \"licenseStatusName\": \"\"\n }\n }\n ],\n \"homeAddressCity\": \"\",\n \"homeAddressPostalCode\": \"\",\n \"homeAddressState\": \"\",\n \"homeAddressStreet1\": \"\",\n \"jurisdiction\": \"ny\",\n \"jurisdictionUploadedCompactEligibility\": \"eligible\",\n \"jurisdictionUploadedLicenseStatus\": \"active\",\n \"licenseStatus\": \"active\",\n \"licenseType\": \"licensed professional counselor\",\n \"middleName\": \"\",\n \"providerId\": \"313794c2-e6a4-40a2-9e20-1fd7af3d276d\",\n \"type\": \"license-home\",\n \"homeAddressStreet2\": \"\",\n \"investigations\": [\n {\n \"compact\": \"coun\",\n \"creationDate\": \"\",\n \"dateOfUpdate\": \"\",\n \"investigationId\": \"\",\n \"jurisdiction\": \"ms\",\n \"licenseType\": \"\",\n \"providerId\": \"f47123b6-8f51-44f3-a1cb-7b1a8e15611f\",\n \"submittingUser\": \"\",\n \"type\": \"investigation\"\n },\n {\n \"compact\": \"aslp\",\n \"creationDate\": \"\",\n \"dateOfUpdate\": \"\",\n \"investigationId\": \"\",\n \"jurisdiction\": \"nh\",\n \"licenseType\": \"\",\n \"providerId\": \"9594910b-52b8-4b5d-985b-9750832cd1c0\",\n \"submittingUser\": \"\",\n \"type\": \"investigation\"\n }\n ],\n \"suffix\": \"\",\n \"emailAddress\": \"\",\n \"licenseNumber\": \"\",\n \"investigationStatus\": \"underInvestigation\",\n \"npi\": \"2736368753\",\n \"dateOfBirth\": \"2164-07-02\",\n \"ssnLastFour\": \"4355\",\n \"phoneNumber\": \"+70372285\",\n \"licenseStatusName\": \"\",\n \"adverseActions\": [\n {\n \"actionAgainst\": \"\",\n \"adverseActionId\": \"\",\n \"compact\": \"aslp\",\n \"creationDate\": \"2310-07-04\",\n \"dateOfUpdate\": \"\",\n \"effectiveStartDate\": \"2892-12-05\",\n \"encumbranceType\": \"\",\n \"jurisdiction\": \"ia\",\n \"licenseType\": \"\",\n \"licenseTypeAbbreviation\": \"\",\n \"providerId\": \"6cabca4e-7fbe-4e81-9a6c-8e4cfb4bd7df\",\n \"type\": \"adverseAction\",\n \"clinicalPrivilegeActionCategories\": [\n \"\",\n \"\"\n ],\n \"effectiveLiftDate\": \"2524-12-31\",\n \"clinicalPrivilegeActionCategory\": \"\",\n \"liftingUser\": \"\"\n },\n {\n \"actionAgainst\": \"\",\n \"adverseActionId\": \"\",\n \"compact\": \"octp\",\n \"creationDate\": \"1388-04-19\",\n \"dateOfUpdate\": \"\",\n \"effectiveStartDate\": \"2079-09-31\",\n \"encumbranceType\": \"\",\n \"jurisdiction\": \"wa\",\n \"licenseType\": \"\",\n \"licenseTypeAbbreviation\": \"\",\n \"providerId\": \"59d6846b-391f-4b14-80fe-41614be8372d\",\n \"type\": \"adverseAction\",\n \"clinicalPrivilegeActionCategories\": [\n \"\",\n \"\"\n ],\n \"effectiveLiftDate\": \"1284-01-19\",\n \"clinicalPrivilegeActionCategory\": \"\",\n \"liftingUser\": \"\"\n }\n ]\n }\n ],\n \"militaryAffiliations\": [\n {\n \"affiliationType\": \"militaryMemberSpouse\",\n \"compact\": \"coun\",\n \"dateOfUpdate\": \"\",\n \"dateOfUpload\": \"2656-06-30\",\n \"fileNames\": [\n \"\",\n \"\"\n ],\n \"providerId\": \"33ff6892-8ba6-46f7-987e-f1fddba57f4b\",\n \"status\": \"inactive\",\n \"type\": \"militaryAffiliation\",\n \"downloadLinks\": [\n {\n \"fileName\": \"\",\n \"url\": \"\"\n },\n {\n \"fileName\": \"\",\n \"url\": \"\"\n }\n ]\n },\n {\n \"affiliationType\": \"militaryMember\",\n \"compact\": \"coun\",\n \"dateOfUpdate\": \"\",\n \"dateOfUpload\": \"2442-01-07\",\n \"fileNames\": [\n \"\",\n \"\"\n ],\n \"providerId\": \"959e6e13-9d35-4d46-8091-12d3cb827e45\",\n \"status\": \"active\",\n \"type\": \"militaryAffiliation\",\n \"downloadLinks\": [\n {\n \"fileName\": \"\",\n \"url\": \"\"\n },\n {\n \"fileName\": \"\",\n \"url\": \"\"\n }\n ]\n }\n ],\n \"privilegeJurisdictions\": [\n \"md\",\n \"nc\"\n ],\n \"privileges\": [\n {\n \"administratorSetStatus\": \"active\",\n \"attestations\": [\n {\n \"attestationId\": \"\",\n \"version\": \"\"\n },\n {\n \"attestationId\": \"\",\n \"version\": \"\"\n }\n ],\n \"compact\": \"octp\",\n \"compactTransactionId\": \"\",\n \"dateOfExpiration\": \"1102-12-12\",\n \"dateOfIssuance\": \"1313-12-05\",\n \"dateOfRenewal\": \"2701-05-30\",\n \"dateOfUpdate\": \"\",\n \"history\": [\n {\n \"compact\": \"octp\",\n \"dateOfUpdate\": \"\",\n \"jurisdiction\": \"in\",\n \"previous\": {\n \"administratorSetStatus\": \"inactive\",\n \"attestations\": [\n {\n \"attestationId\": \"\",\n \"version\": \"\"\n },\n {\n \"attestationId\": \"\",\n \"version\": \"\"\n }\n ],\n \"compactTransactionId\": \"\",\n \"dateOfExpiration\": \"2836-07-27\",\n \"dateOfIssuance\": \"2788-10-30\",\n \"dateOfRenewal\": \"1280-10-31\",\n \"dateOfUpdate\": \"\",\n \"licenseJurisdiction\": \"id\",\n \"privilegeId\": \"\",\n \"compact\": \"octp\",\n \"jurisdiction\": \"ok\",\n \"type\": \"privilege\",\n \"providerId\": \"a8431870-2a46-424f-9be6-9d9612adc5bd\",\n \"status\": \"inactive\"\n },\n \"type\": \"privilegeUpdate\",\n \"updateType\": \"expiration\",\n \"removedValues\": [\n \"\",\n \"\"\n ],\n \"licenseType\": \"speech-language pathologist\",\n \"updatedValues\": {\n \"licenseJurisdiction\": \"ok\",\n \"compact\": \"octp\",\n \"jurisdiction\": \"mn\",\n \"attestations\": [\n {\n \"attestationId\": \"\",\n \"version\": \"\"\n },\n {\n \"attestationId\": \"\",\n \"version\": \"\"\n }\n ],\n \"type\": \"privilege\",\n \"compactTransactionId\": \"\",\n \"dateOfIssuance\": \"1612-12-31\",\n \"administratorSetStatus\": \"active\",\n \"dateOfExpiration\": \"2400-02-30\",\n \"privilegeId\": \"\",\n \"providerId\": \"040e7e14-5972-4398-95ee-399a7036cfb7\",\n \"dateOfRenewal\": \"2756-10-09\",\n \"dateOfUpdate\": \"\",\n \"status\": \"inactive\"\n }\n },\n {\n \"compact\": \"octp\",\n \"dateOfUpdate\": \"\",\n \"jurisdiction\": \"fl\",\n \"previous\": {\n \"administratorSetStatus\": \"active\",\n \"attestations\": [\n {\n \"attestationId\": \"\",\n \"version\": \"\"\n },\n {\n \"attestationId\": \"\",\n \"version\": \"\"\n }\n ],\n \"compactTransactionId\": \"\",\n \"dateOfExpiration\": \"2891-04-07\",\n \"dateOfIssuance\": \"1522-12-30\",\n \"dateOfRenewal\": \"1235-10-15\",\n \"dateOfUpdate\": \"\",\n \"licenseJurisdiction\": \"oh\",\n \"privilegeId\": \"\",\n \"compact\": \"octp\",\n \"jurisdiction\": \"ny\",\n \"type\": \"privilege\",\n \"providerId\": \"8174175e-978b-46f6-b0d5-75dc883e83ea\",\n \"status\": \"active\"\n },\n \"type\": \"privilegeUpdate\",\n \"updateType\": \"deactivation\",\n \"removedValues\": [\n \"\",\n \"\"\n ],\n \"licenseType\": \"speech-language pathologist\",\n \"updatedValues\": {\n \"licenseJurisdiction\": \"tn\",\n \"compact\": \"octp\",\n \"jurisdiction\": \"ut\",\n \"attestations\": [\n {\n \"attestationId\": \"\",\n \"version\": \"\"\n },\n {\n \"attestationId\": \"\",\n \"version\": \"\"\n }\n ],\n \"type\": \"privilege\",\n \"compactTransactionId\": \"\",\n \"dateOfIssuance\": \"1015-11-16\",\n \"administratorSetStatus\": \"active\",\n \"dateOfExpiration\": \"2808-10-30\",\n \"privilegeId\": \"\",\n \"providerId\": \"402bc3a2-4a76-49df-a95e-83cdc8d4c6c9\",\n \"dateOfRenewal\": \"2912-11-05\",\n \"dateOfUpdate\": \"\",\n \"status\": \"inactive\"\n }\n }\n ],\n \"jurisdiction\": \"il\",\n \"licenseJurisdiction\": \"ok\",\n \"licenseType\": \"licensed professional counselor\",\n \"privilegeId\": \"\",\n \"providerId\": \"fabef33a-bb76-469e-9794-0fb12dabb9d2\",\n \"status\": \"inactive\",\n \"type\": \"privilege\",\n \"investigationStatus\": \"underInvestigation\",\n \"investigations\": [\n {\n \"compact\": \"coun\",\n \"creationDate\": \"\",\n \"dateOfUpdate\": \"\",\n \"investigationId\": \"\",\n \"jurisdiction\": \"nc\",\n \"licenseType\": \"\",\n \"providerId\": \"b8939464-4b19-4a6d-bc77-5bfd9d0d7ea1\",\n \"submittingUser\": \"\",\n \"type\": \"investigation\"\n },\n {\n \"compact\": \"aslp\",\n \"creationDate\": \"\",\n \"dateOfUpdate\": \"\",\n \"investigationId\": \"\",\n \"jurisdiction\": \"az\",\n \"licenseType\": \"\",\n \"providerId\": \"aef09a8b-757f-4a4d-9739-12dfaac27da4\",\n \"submittingUser\": \"\",\n \"type\": \"investigation\"\n }\n ],\n \"adverseActions\": [\n {\n \"actionAgainst\": \"\",\n \"adverseActionId\": \"\",\n \"compact\": \"coun\",\n \"creationDate\": \"1589-10-10\",\n \"dateOfUpdate\": \"\",\n \"effectiveStartDate\": \"1165-04-31\",\n \"encumbranceType\": \"\",\n \"jurisdiction\": \"ca\",\n \"licenseType\": \"\",\n \"licenseTypeAbbreviation\": \"\",\n \"providerId\": \"deacc877-04f8-425a-85af-261178c514ae\",\n \"type\": \"adverseAction\",\n \"clinicalPrivilegeActionCategories\": [\n \"\",\n \"\"\n ],\n \"effectiveLiftDate\": \"1861-12-25\",\n \"clinicalPrivilegeActionCategory\": \"\",\n \"liftingUser\": \"\"\n },\n {\n \"actionAgainst\": \"\",\n \"adverseActionId\": \"\",\n \"compact\": \"coun\",\n \"creationDate\": \"2914-08-03\",\n \"dateOfUpdate\": \"\",\n \"effectiveStartDate\": \"2120-04-30\",\n \"encumbranceType\": \"\",\n \"jurisdiction\": \"id\",\n \"licenseType\": \"\",\n \"licenseTypeAbbreviation\": \"\",\n \"providerId\": \"cf4315a9-ccdb-42a8-854b-7e90def7885a\",\n \"type\": \"adverseAction\",\n \"clinicalPrivilegeActionCategories\": [\n \"\",\n \"\"\n ],\n \"effectiveLiftDate\": \"1914-04-30\",\n \"clinicalPrivilegeActionCategory\": \"\",\n \"liftingUser\": \"\"\n }\n ]\n },\n {\n \"administratorSetStatus\": \"inactive\",\n \"attestations\": [\n {\n \"attestationId\": \"\",\n \"version\": \"\"\n },\n {\n \"attestationId\": \"\",\n \"version\": \"\"\n }\n ],\n \"compact\": \"octp\",\n \"compactTransactionId\": \"\",\n \"dateOfExpiration\": \"1723-06-11\",\n \"dateOfIssuance\": \"1203-12-15\",\n \"dateOfRenewal\": \"2871-11-10\",\n \"dateOfUpdate\": \"\",\n \"history\": [\n {\n \"compact\": \"octp\",\n \"dateOfUpdate\": \"\",\n \"jurisdiction\": \"nj\",\n \"previous\": {\n \"administratorSetStatus\": \"inactive\",\n \"attestations\": [\n {\n \"attestationId\": \"\",\n \"version\": \"\"\n },\n {\n \"attestationId\": \"\",\n \"version\": \"\"\n }\n ],\n \"compactTransactionId\": \"\",\n \"dateOfExpiration\": \"1058-12-11\",\n \"dateOfIssuance\": \"2149-11-30\",\n \"dateOfRenewal\": \"1018-08-01\",\n \"dateOfUpdate\": \"\",\n \"licenseJurisdiction\": \"fl\",\n \"privilegeId\": \"\",\n \"compact\": \"coun\",\n \"jurisdiction\": \"mi\",\n \"type\": \"privilege\",\n \"providerId\": \"5e722037-8d03-47ab-afaa-d915b0cac757\",\n \"status\": \"inactive\"\n },\n \"type\": \"privilegeUpdate\",\n \"updateType\": \"lifting_encumbrance\",\n \"removedValues\": [\n \"\",\n \"\"\n ],\n \"licenseType\": \"occupational therapy assistant\",\n \"updatedValues\": {\n \"licenseJurisdiction\": \"tn\",\n \"compact\": \"coun\",\n \"jurisdiction\": \"wi\",\n \"attestations\": [\n {\n \"attestationId\": \"\",\n \"version\": \"\"\n },\n {\n \"attestationId\": \"\",\n \"version\": \"\"\n }\n ],\n \"type\": \"privilege\",\n \"compactTransactionId\": \"\",\n \"dateOfIssuance\": \"1426-03-06\",\n \"administratorSetStatus\": \"inactive\",\n \"dateOfExpiration\": \"1199-10-06\",\n \"privilegeId\": \"\",\n \"providerId\": \"283c9149-73e8-40a4-9c6a-788e01bbed79\",\n \"dateOfRenewal\": \"1271-04-30\",\n \"dateOfUpdate\": \"\",\n \"status\": \"inactive\"\n }\n },\n {\n \"compact\": \"octp\",\n \"dateOfUpdate\": \"\",\n \"jurisdiction\": \"id\",\n \"previous\": {\n \"administratorSetStatus\": \"inactive\",\n \"attestations\": [\n {\n \"attestationId\": \"\",\n \"version\": \"\"\n },\n {\n \"attestationId\": \"\",\n \"version\": \"\"\n }\n ],\n \"compactTransactionId\": \"\",\n \"dateOfExpiration\": \"2341-12-30\",\n \"dateOfIssuance\": \"1576-03-30\",\n \"dateOfRenewal\": \"1709-03-03\",\n \"dateOfUpdate\": \"\",\n \"licenseJurisdiction\": \"wy\",\n \"privilegeId\": \"\",\n \"compact\": \"octp\",\n \"jurisdiction\": \"ut\",\n \"type\": \"privilege\",\n \"providerId\": \"366e661a-421d-4689-b899-793f7233747c\",\n \"status\": \"inactive\"\n },\n \"type\": \"privilegeUpdate\",\n \"updateType\": \"other\",\n \"removedValues\": [\n \"\",\n \"\"\n ],\n \"licenseType\": \"occupational therapist\",\n \"updatedValues\": {\n \"licenseJurisdiction\": \"hi\",\n \"compact\": \"coun\",\n \"jurisdiction\": \"ky\",\n \"attestations\": [\n {\n \"attestationId\": \"\",\n \"version\": \"\"\n },\n {\n \"attestationId\": \"\",\n \"version\": \"\"\n }\n ],\n \"type\": \"privilege\",\n \"compactTransactionId\": \"\",\n \"dateOfIssuance\": \"2939-01-02\",\n \"administratorSetStatus\": \"inactive\",\n \"dateOfExpiration\": \"1022-10-24\",\n \"privilegeId\": \"\",\n \"providerId\": \"fbbe0975-c404-46f0-9dc7-399ffeae3683\",\n \"dateOfRenewal\": \"1236-01-31\",\n \"dateOfUpdate\": \"\",\n \"status\": \"active\"\n }\n }\n ],\n \"jurisdiction\": \"ms\",\n \"licenseJurisdiction\": \"mn\",\n \"licenseType\": \"licensed professional counselor\",\n \"privilegeId\": \"\",\n \"providerId\": \"8d94428c-bb5b-4140-8aa6-066bffffdfdc\",\n \"status\": \"inactive\",\n \"type\": \"privilege\",\n \"investigationStatus\": \"underInvestigation\",\n \"investigations\": [\n {\n \"compact\": \"coun\",\n \"creationDate\": \"\",\n \"dateOfUpdate\": \"\",\n \"investigationId\": \"\",\n \"jurisdiction\": \"md\",\n \"licenseType\": \"\",\n \"providerId\": \"6b200887-a4e3-4124-acdc-fffce69807f6\",\n \"submittingUser\": \"\",\n \"type\": \"investigation\"\n },\n {\n \"compact\": \"octp\",\n \"creationDate\": \"\",\n \"dateOfUpdate\": \"\",\n \"investigationId\": \"\",\n \"jurisdiction\": \"ak\",\n \"licenseType\": \"\",\n \"providerId\": \"3eb7845c-a341-4b11-8db7-6186a3e0f7e9\",\n \"submittingUser\": \"\",\n \"type\": \"investigation\"\n }\n ],\n \"adverseActions\": [\n {\n \"actionAgainst\": \"\",\n \"adverseActionId\": \"\",\n \"compact\": \"aslp\",\n \"creationDate\": \"2881-04-31\",\n \"dateOfUpdate\": \"\",\n \"effectiveStartDate\": \"1128-04-14\",\n \"encumbranceType\": \"\",\n \"jurisdiction\": \"in\",\n \"licenseType\": \"\",\n \"licenseTypeAbbreviation\": \"\",\n \"providerId\": \"c38f6c41-d865-4c37-ae18-63ce5fd18c55\",\n \"type\": \"adverseAction\",\n \"clinicalPrivilegeActionCategories\": [\n \"\",\n \"\"\n ],\n \"effectiveLiftDate\": \"2871-05-08\",\n \"clinicalPrivilegeActionCategory\": \"\",\n \"liftingUser\": \"\"\n },\n {\n \"actionAgainst\": \"\",\n \"adverseActionId\": \"\",\n \"compact\": \"coun\",\n \"creationDate\": \"2484-04-31\",\n \"dateOfUpdate\": \"\",\n \"effectiveStartDate\": \"2979-11-04\",\n \"encumbranceType\": \"\",\n \"jurisdiction\": \"fl\",\n \"licenseType\": \"\",\n \"licenseTypeAbbreviation\": \"\",\n \"providerId\": \"be457ff9-9a51-404b-9d23-b9f5eaa9d6c6\",\n \"type\": \"adverseAction\",\n \"clinicalPrivilegeActionCategories\": [\n \"\",\n \"\"\n ],\n \"effectiveLiftDate\": \"2146-10-05\",\n \"clinicalPrivilegeActionCategory\": \"\",\n \"liftingUser\": \"\"\n }\n ]\n }\n ],\n \"providerId\": \"098edacb-8ab3-4f0b-a655-c9f26be8827c\",\n \"type\": \"provider\",\n \"npi\": \"5990059533\",\n \"compactEligibility\": \"ineligible\",\n \"jurisdictionUploadedCompactEligibility\": \"eligible\",\n \"dateOfBirth\": \"1157-07-30\",\n \"jurisdictionUploadedLicenseStatus\": \"active\",\n \"suffix\": \"\",\n \"currentHomeJurisdiction\": \"wi\",\n \"ssnLastFour\": \"7344\",\n \"licenseStatus\": \"active\",\n \"middleName\": \"\",\n \"compactConnectRegisteredEmailAddress\": \"\"\n}", + "body": "{\n \"birthMonthDay\": \"02-11\",\n \"compact\": \"octp\",\n \"dateOfExpiration\": \"1631-11-08\",\n \"dateOfUpdate\": \"\",\n \"familyName\": \"\",\n \"givenName\": \"\",\n \"licenseJurisdiction\": \"mi\",\n \"licenses\": [\n {\n \"compact\": \"octp\",\n \"compactEligibility\": \"eligible\",\n \"dateOfExpiration\": \"2477-01-29\",\n \"dateOfIssuance\": \"2289-02-05\",\n \"dateOfRenewal\": \"2467-09-31\",\n \"dateOfUpdate\": \"\",\n \"familyName\": \"\",\n \"givenName\": \"\",\n \"history\": [\n {\n \"compact\": \"aslp\",\n \"dateOfUpdate\": \"\",\n \"jurisdiction\": \"oh\",\n \"previous\": {\n \"dateOfExpiration\": \"1524-10-30\",\n \"dateOfIssuance\": \"1167-12-07\",\n \"dateOfRenewal\": \"1205-01-17\",\n \"familyName\": \"\",\n \"givenName\": \"\",\n \"homeAddressCity\": \"\",\n \"homeAddressPostalCode\": \"\",\n \"homeAddressState\": \"\",\n \"homeAddressStreet1\": \"\",\n \"jurisdictionUploadedCompactEligibility\": \"eligible\",\n \"jurisdictionUploadedLicenseStatus\": \"active\",\n \"middleName\": \"\",\n \"homeAddressStreet2\": \"\",\n \"npi\": \"6002575391\",\n \"compactEligibility\": \"eligible\",\n \"dateOfBirth\": \"1105-06-30\",\n \"suffix\": \"\",\n \"emailAddress\": \"\",\n \"phoneNumber\": \"+007267268\",\n \"licenseStatus\": \"active\",\n \"licenseNumber\": \"\",\n \"licenseStatusName\": \"\"\n },\n \"type\": \"licenseUpdate\",\n \"updateType\": \"other\",\n \"removedValues\": [\n \"\",\n \"\"\n ],\n \"licenseType\": \"occupational therapy assistant\",\n \"updatedValues\": {\n \"homeAddressStreet2\": \"\",\n \"npi\": \"8616398454\",\n \"homeAddressPostalCode\": \"\",\n \"givenName\": \"\",\n \"homeAddressStreet1\": \"\",\n \"compactEligibility\": \"ineligible\",\n \"jurisdictionUploadedCompactEligibility\": \"ineligible\",\n \"dateOfBirth\": \"2499-11-30\",\n \"jurisdictionUploadedLicenseStatus\": \"active\",\n \"suffix\": \"\",\n \"dateOfIssuance\": \"2862-10-28\",\n \"emailAddress\": \"\",\n \"dateOfExpiration\": \"1419-12-09\",\n \"phoneNumber\": \"+47237183623669\",\n \"homeAddressState\": \"\",\n \"dateOfRenewal\": \"1379-02-31\",\n \"licenseStatus\": \"inactive\",\n \"familyName\": \"\",\n \"homeAddressCity\": \"\",\n \"licenseNumber\": \"\",\n \"middleName\": \"\",\n \"licenseStatusName\": \"\"\n }\n },\n {\n \"compact\": \"coun\",\n \"dateOfUpdate\": \"\",\n \"jurisdiction\": \"sc\",\n \"previous\": {\n \"dateOfExpiration\": \"1431-10-21\",\n \"dateOfIssuance\": \"1210-12-31\",\n \"dateOfRenewal\": \"1787-02-31\",\n \"familyName\": \"\",\n \"givenName\": \"\",\n \"homeAddressCity\": \"\",\n \"homeAddressPostalCode\": \"\",\n \"homeAddressState\": \"\",\n \"homeAddressStreet1\": \"\",\n \"jurisdictionUploadedCompactEligibility\": \"eligible\",\n \"jurisdictionUploadedLicenseStatus\": \"inactive\",\n \"middleName\": \"\",\n \"homeAddressStreet2\": \"\",\n \"npi\": \"3083184307\",\n \"compactEligibility\": \"eligible\",\n \"dateOfBirth\": \"1005-10-01\",\n \"suffix\": \"\",\n \"emailAddress\": \"\",\n \"phoneNumber\": \"+52476242194\",\n \"licenseStatus\": \"active\",\n \"licenseNumber\": \"\",\n \"licenseStatusName\": \"\"\n },\n \"type\": \"licenseUpdate\",\n \"updateType\": \"homeJurisdictionChange\",\n \"removedValues\": [\n \"\",\n \"\"\n ],\n \"licenseType\": \"speech-language pathologist\",\n \"updatedValues\": {\n \"homeAddressStreet2\": \"\",\n \"npi\": \"0711842144\",\n \"homeAddressPostalCode\": \"\",\n \"givenName\": \"\",\n \"homeAddressStreet1\": \"\",\n \"compactEligibility\": \"ineligible\",\n \"jurisdictionUploadedCompactEligibility\": \"ineligible\",\n \"dateOfBirth\": \"2578-07-30\",\n \"jurisdictionUploadedLicenseStatus\": \"active\",\n \"suffix\": \"\",\n \"dateOfIssuance\": \"2683-07-31\",\n \"emailAddress\": \"\",\n \"dateOfExpiration\": \"2406-01-27\",\n \"phoneNumber\": \"+57863834913559\",\n \"homeAddressState\": \"\",\n \"dateOfRenewal\": \"1690-11-10\",\n \"licenseStatus\": \"active\",\n \"familyName\": \"\",\n \"homeAddressCity\": \"\",\n \"licenseNumber\": \"\",\n \"middleName\": \"\",\n \"licenseStatusName\": \"\"\n }\n }\n ],\n \"homeAddressCity\": \"\",\n \"homeAddressPostalCode\": \"\",\n \"homeAddressState\": \"\",\n \"homeAddressStreet1\": \"\",\n \"jurisdiction\": \"ga\",\n \"jurisdictionUploadedCompactEligibility\": \"eligible\",\n \"jurisdictionUploadedLicenseStatus\": \"active\",\n \"licenseStatus\": \"active\",\n \"licenseType\": \"audiologist\",\n \"middleName\": \"\",\n \"providerId\": \"c2830970-3ac2-4408-80f3-1c3b7f3f12fd\",\n \"type\": \"license-home\",\n \"homeAddressStreet2\": \"\",\n \"investigations\": [\n {\n \"compact\": \"octp\",\n \"creationDate\": \"\",\n \"dateOfUpdate\": \"\",\n \"investigationId\": \"\",\n \"jurisdiction\": \"ok\",\n \"licenseType\": \"\",\n \"providerId\": \"c4b9a8a2-24f1-41aa-950b-4d1316f8856a\",\n \"submittingUser\": \"\",\n \"type\": \"investigation\"\n },\n {\n \"compact\": \"aslp\",\n \"creationDate\": \"\",\n \"dateOfUpdate\": \"\",\n \"investigationId\": \"\",\n \"jurisdiction\": \"id\",\n \"licenseType\": \"\",\n \"providerId\": \"9fd53799-7da6-4253-897a-e485a6b1d58e\",\n \"submittingUser\": \"\",\n \"type\": \"investigation\"\n }\n ],\n \"suffix\": \"\",\n \"emailAddress\": \"\",\n \"licenseNumber\": \"\",\n \"investigationStatus\": \"underInvestigation\",\n \"npi\": \"6248254005\",\n \"dateOfBirth\": \"2457-06-02\",\n \"ssnLastFour\": \"1587\",\n \"phoneNumber\": \"+88170901\",\n \"licenseStatusName\": \"\",\n \"adverseActions\": [\n {\n \"actionAgainst\": \"\",\n \"adverseActionId\": \"\",\n \"compact\": \"octp\",\n \"creationDate\": \"1079-06-14\",\n \"dateOfUpdate\": \"\",\n \"effectiveStartDate\": \"2198-05-29\",\n \"encumbranceType\": \"\",\n \"jurisdiction\": \"nh\",\n \"licenseType\": \"\",\n \"licenseTypeAbbreviation\": \"\",\n \"providerId\": \"993f7974-2cd5-4f52-b3d7-62ca646321eb\",\n \"type\": \"adverseAction\",\n \"clinicalPrivilegeActionCategories\": [\n \"\",\n \"\"\n ],\n \"effectiveLiftDate\": \"2806-10-31\",\n \"liftingUser\": \"\"\n },\n {\n \"actionAgainst\": \"\",\n \"adverseActionId\": \"\",\n \"compact\": \"octp\",\n \"creationDate\": \"2711-04-26\",\n \"dateOfUpdate\": \"\",\n \"effectiveStartDate\": \"2151-12-31\",\n \"encumbranceType\": \"\",\n \"jurisdiction\": \"ak\",\n \"licenseType\": \"\",\n \"licenseTypeAbbreviation\": \"\",\n \"providerId\": \"f86f7fa0-82b2-496b-bfa2-f81facb1cc9e\",\n \"type\": \"adverseAction\",\n \"clinicalPrivilegeActionCategories\": [\n \"\",\n \"\"\n ],\n \"effectiveLiftDate\": \"2901-09-09\",\n \"liftingUser\": \"\"\n }\n ]\n },\n {\n \"compact\": \"octp\",\n \"compactEligibility\": \"eligible\",\n \"dateOfExpiration\": \"2713-08-14\",\n \"dateOfIssuance\": \"1623-04-15\",\n \"dateOfRenewal\": \"2814-11-21\",\n \"dateOfUpdate\": \"\",\n \"familyName\": \"\",\n \"givenName\": \"\",\n \"history\": [\n {\n \"compact\": \"octp\",\n \"dateOfUpdate\": \"\",\n \"jurisdiction\": \"id\",\n \"previous\": {\n \"dateOfExpiration\": \"2593-10-23\",\n \"dateOfIssuance\": \"1874-09-30\",\n \"dateOfRenewal\": \"1371-04-30\",\n \"familyName\": \"\",\n \"givenName\": \"\",\n \"homeAddressCity\": \"\",\n \"homeAddressPostalCode\": \"\",\n \"homeAddressState\": \"\",\n \"homeAddressStreet1\": \"\",\n \"jurisdictionUploadedCompactEligibility\": \"eligible\",\n \"jurisdictionUploadedLicenseStatus\": \"active\",\n \"middleName\": \"\",\n \"homeAddressStreet2\": \"\",\n \"npi\": \"7160215740\",\n \"compactEligibility\": \"eligible\",\n \"dateOfBirth\": \"2165-02-30\",\n \"suffix\": \"\",\n \"emailAddress\": \"\",\n \"phoneNumber\": \"+8642909671\",\n \"licenseStatus\": \"active\",\n \"licenseNumber\": \"\",\n \"licenseStatusName\": \"\"\n },\n \"type\": \"licenseUpdate\",\n \"updateType\": \"licenseDeactivation\",\n \"removedValues\": [\n \"\",\n \"\"\n ],\n \"licenseType\": \"occupational therapy assistant\",\n \"updatedValues\": {\n \"homeAddressStreet2\": \"\",\n \"npi\": \"6845028097\",\n \"homeAddressPostalCode\": \"\",\n \"givenName\": \"\",\n \"homeAddressStreet1\": \"\",\n \"compactEligibility\": \"ineligible\",\n \"jurisdictionUploadedCompactEligibility\": \"eligible\",\n \"dateOfBirth\": \"2551-06-31\",\n \"jurisdictionUploadedLicenseStatus\": \"active\",\n \"suffix\": \"\",\n \"dateOfIssuance\": \"2263-10-01\",\n \"emailAddress\": \"\",\n \"dateOfExpiration\": \"1706-12-28\",\n \"phoneNumber\": \"+101312267\",\n \"homeAddressState\": \"\",\n \"dateOfRenewal\": \"2012-10-01\",\n \"licenseStatus\": \"inactive\",\n \"familyName\": \"\",\n \"homeAddressCity\": \"\",\n \"licenseNumber\": \"\",\n \"middleName\": \"\",\n \"licenseStatusName\": \"\"\n }\n },\n {\n \"compact\": \"aslp\",\n \"dateOfUpdate\": \"\",\n \"jurisdiction\": \"wi\",\n \"previous\": {\n \"dateOfExpiration\": \"1149-08-11\",\n \"dateOfIssuance\": \"1924-05-10\",\n \"dateOfRenewal\": \"2707-11-16\",\n \"familyName\": \"\",\n \"givenName\": \"\",\n \"homeAddressCity\": \"\",\n \"homeAddressPostalCode\": \"\",\n \"homeAddressState\": \"\",\n \"homeAddressStreet1\": \"\",\n \"jurisdictionUploadedCompactEligibility\": \"ineligible\",\n \"jurisdictionUploadedLicenseStatus\": \"inactive\",\n \"middleName\": \"\",\n \"homeAddressStreet2\": \"\",\n \"npi\": \"6250035227\",\n \"compactEligibility\": \"eligible\",\n \"dateOfBirth\": \"2225-11-01\",\n \"suffix\": \"\",\n \"emailAddress\": \"\",\n \"phoneNumber\": \"+688297087690\",\n \"licenseStatus\": \"inactive\",\n \"licenseNumber\": \"\",\n \"licenseStatusName\": \"\"\n },\n \"type\": \"licenseUpdate\",\n \"updateType\": \"lifting_encumbrance\",\n \"removedValues\": [\n \"\",\n \"\"\n ],\n \"licenseType\": \"occupational therapist\",\n \"updatedValues\": {\n \"homeAddressStreet2\": \"\",\n \"npi\": \"9422845892\",\n \"homeAddressPostalCode\": \"\",\n \"givenName\": \"\",\n \"homeAddressStreet1\": \"\",\n \"compactEligibility\": \"eligible\",\n \"jurisdictionUploadedCompactEligibility\": \"ineligible\",\n \"dateOfBirth\": \"2750-05-07\",\n \"jurisdictionUploadedLicenseStatus\": \"inactive\",\n \"suffix\": \"\",\n \"dateOfIssuance\": \"2095-12-03\",\n \"emailAddress\": \"\",\n \"dateOfExpiration\": \"1505-05-30\",\n \"phoneNumber\": \"+838892464019\",\n \"homeAddressState\": \"\",\n \"dateOfRenewal\": \"1936-12-25\",\n \"licenseStatus\": \"active\",\n \"familyName\": \"\",\n \"homeAddressCity\": \"\",\n \"licenseNumber\": \"\",\n \"middleName\": \"\",\n \"licenseStatusName\": \"\"\n }\n }\n ],\n \"homeAddressCity\": \"\",\n \"homeAddressPostalCode\": \"\",\n \"homeAddressState\": \"\",\n \"homeAddressStreet1\": \"\",\n \"jurisdiction\": \"nj\",\n \"jurisdictionUploadedCompactEligibility\": \"eligible\",\n \"jurisdictionUploadedLicenseStatus\": \"inactive\",\n \"licenseStatus\": \"inactive\",\n \"licenseType\": \"licensed professional counselor\",\n \"middleName\": \"\",\n \"providerId\": \"4aa7243e-4b49-4b76-a465-1ec7965da919\",\n \"type\": \"license-home\",\n \"homeAddressStreet2\": \"\",\n \"investigations\": [\n {\n \"compact\": \"aslp\",\n \"creationDate\": \"\",\n \"dateOfUpdate\": \"\",\n \"investigationId\": \"\",\n \"jurisdiction\": \"oh\",\n \"licenseType\": \"\",\n \"providerId\": \"d2f92662-5881-4273-b95e-ad56981d9b5b\",\n \"submittingUser\": \"\",\n \"type\": \"investigation\"\n },\n {\n \"compact\": \"aslp\",\n \"creationDate\": \"\",\n \"dateOfUpdate\": \"\",\n \"investigationId\": \"\",\n \"jurisdiction\": \"ne\",\n \"licenseType\": \"\",\n \"providerId\": \"273db8ac-b2c3-41fe-9ca1-7240d8f9d0b9\",\n \"submittingUser\": \"\",\n \"type\": \"investigation\"\n }\n ],\n \"suffix\": \"\",\n \"emailAddress\": \"\",\n \"licenseNumber\": \"\",\n \"investigationStatus\": \"underInvestigation\",\n \"npi\": \"3519973963\",\n \"dateOfBirth\": \"1650-03-05\",\n \"ssnLastFour\": \"5195\",\n \"phoneNumber\": \"+478582966712\",\n \"licenseStatusName\": \"\",\n \"adverseActions\": [\n {\n \"actionAgainst\": \"\",\n \"adverseActionId\": \"\",\n \"compact\": \"coun\",\n \"creationDate\": \"1526-12-18\",\n \"dateOfUpdate\": \"\",\n \"effectiveStartDate\": \"2486-07-31\",\n \"encumbranceType\": \"\",\n \"jurisdiction\": \"pr\",\n \"licenseType\": \"\",\n \"licenseTypeAbbreviation\": \"\",\n \"providerId\": \"f1b67668-c893-4417-96b4-97538d1d644d\",\n \"type\": \"adverseAction\",\n \"clinicalPrivilegeActionCategories\": [\n \"\",\n \"\"\n ],\n \"effectiveLiftDate\": \"1572-11-06\",\n \"liftingUser\": \"\"\n },\n {\n \"actionAgainst\": \"\",\n \"adverseActionId\": \"\",\n \"compact\": \"coun\",\n \"creationDate\": \"1253-03-30\",\n \"dateOfUpdate\": \"\",\n \"effectiveStartDate\": \"1025-12-10\",\n \"encumbranceType\": \"\",\n \"jurisdiction\": \"tn\",\n \"licenseType\": \"\",\n \"licenseTypeAbbreviation\": \"\",\n \"providerId\": \"5513e91e-deb1-4775-8536-0b8fdea726ab\",\n \"type\": \"adverseAction\",\n \"clinicalPrivilegeActionCategories\": [\n \"\",\n \"\"\n ],\n \"effectiveLiftDate\": \"1701-07-08\",\n \"liftingUser\": \"\"\n }\n ]\n }\n ],\n \"militaryAffiliations\": [\n {\n \"affiliationType\": \"militaryMember\",\n \"compact\": \"octp\",\n \"dateOfUpdate\": \"\",\n \"dateOfUpload\": \"1555-06-30\",\n \"fileNames\": [\n \"\",\n \"\"\n ],\n \"providerId\": \"b8cc69f7-a33e-42f2-9d71-1601fabab556\",\n \"status\": \"inactive\",\n \"type\": \"militaryAffiliation\",\n \"downloadLinks\": [\n {\n \"fileName\": \"\",\n \"url\": \"\"\n },\n {\n \"fileName\": \"\",\n \"url\": \"\"\n }\n ]\n },\n {\n \"affiliationType\": \"militaryMemberSpouse\",\n \"compact\": \"coun\",\n \"dateOfUpdate\": \"\",\n \"dateOfUpload\": \"2471-05-18\",\n \"fileNames\": [\n \"\",\n \"\"\n ],\n \"providerId\": \"f9e1264d-4cd1-46de-b585-b31a11ddd9d4\",\n \"status\": \"active\",\n \"type\": \"militaryAffiliation\",\n \"downloadLinks\": [\n {\n \"fileName\": \"\",\n \"url\": \"\"\n },\n {\n \"fileName\": \"\",\n \"url\": \"\"\n }\n ]\n }\n ],\n \"privilegeJurisdictions\": [\n \"wv\",\n \"sd\"\n ],\n \"privileges\": [\n {\n \"administratorSetStatus\": \"active\",\n \"attestations\": [\n {\n \"attestationId\": \"\",\n \"version\": \"\"\n },\n {\n \"attestationId\": \"\",\n \"version\": \"\"\n }\n ],\n \"compact\": \"coun\",\n \"compactTransactionId\": \"\",\n \"dateOfExpiration\": \"1185-10-06\",\n \"dateOfIssuance\": \"2132-07-06\",\n \"dateOfRenewal\": \"1638-11-06\",\n \"dateOfUpdate\": \"\",\n \"history\": [\n {\n \"compact\": \"aslp\",\n \"dateOfUpdate\": \"\",\n \"jurisdiction\": \"vi\",\n \"previous\": {\n \"administratorSetStatus\": \"active\",\n \"attestations\": [\n {\n \"attestationId\": \"\",\n \"version\": \"\"\n },\n {\n \"attestationId\": \"\",\n \"version\": \"\"\n }\n ],\n \"compactTransactionId\": \"\",\n \"dateOfExpiration\": \"2351-12-06\",\n \"dateOfIssuance\": \"2834-10-07\",\n \"dateOfRenewal\": \"1257-10-25\",\n \"dateOfUpdate\": \"\",\n \"licenseJurisdiction\": \"ms\",\n \"privilegeId\": \"\",\n \"compact\": \"coun\",\n \"jurisdiction\": \"va\",\n \"type\": \"privilege\",\n \"providerId\": \"714866dc-e30f-4b34-8eda-30c60cfd6082\",\n \"status\": \"active\"\n },\n \"type\": \"privilegeUpdate\",\n \"updateType\": \"homeJurisdictionChange\",\n \"removedValues\": [\n \"\",\n \"\"\n ],\n \"licenseType\": \"occupational therapy assistant\",\n \"updatedValues\": {\n \"licenseJurisdiction\": \"ia\",\n \"compact\": \"octp\",\n \"jurisdiction\": \"ny\",\n \"attestations\": [\n {\n \"attestationId\": \"\",\n \"version\": \"\"\n },\n {\n \"attestationId\": \"\",\n \"version\": \"\"\n }\n ],\n \"type\": \"privilege\",\n \"compactTransactionId\": \"\",\n \"dateOfIssuance\": \"1880-11-31\",\n \"administratorSetStatus\": \"active\",\n \"dateOfExpiration\": \"1482-03-10\",\n \"privilegeId\": \"\",\n \"providerId\": \"d50167b1-d9b2-46ac-96e6-43c98979e1fc\",\n \"dateOfRenewal\": \"1184-02-31\",\n \"dateOfUpdate\": \"\",\n \"status\": \"inactive\"\n }\n },\n {\n \"compact\": \"coun\",\n \"dateOfUpdate\": \"\",\n \"jurisdiction\": \"tx\",\n \"previous\": {\n \"administratorSetStatus\": \"active\",\n \"attestations\": [\n {\n \"attestationId\": \"\",\n \"version\": \"\"\n },\n {\n \"attestationId\": \"\",\n \"version\": \"\"\n }\n ],\n \"compactTransactionId\": \"\",\n \"dateOfExpiration\": \"2459-07-08\",\n \"dateOfIssuance\": \"2383-05-08\",\n \"dateOfRenewal\": \"1311-12-15\",\n \"dateOfUpdate\": \"\",\n \"licenseJurisdiction\": \"nh\",\n \"privilegeId\": \"\",\n \"compact\": \"octp\",\n \"jurisdiction\": \"nc\",\n \"type\": \"privilege\",\n \"providerId\": \"864c3bae-96f5-4a48-8159-9ec036b2e025\",\n \"status\": \"active\"\n },\n \"type\": \"privilegeUpdate\",\n \"updateType\": \"encumbrance\",\n \"removedValues\": [\n \"\",\n \"\"\n ],\n \"licenseType\": \"occupational therapist\",\n \"updatedValues\": {\n \"licenseJurisdiction\": \"ct\",\n \"compact\": \"aslp\",\n \"jurisdiction\": \"vi\",\n \"attestations\": [\n {\n \"attestationId\": \"\",\n \"version\": \"\"\n },\n {\n \"attestationId\": \"\",\n \"version\": \"\"\n }\n ],\n \"type\": \"privilege\",\n \"compactTransactionId\": \"\",\n \"dateOfIssuance\": \"2927-09-08\",\n \"administratorSetStatus\": \"inactive\",\n \"dateOfExpiration\": \"2588-11-04\",\n \"privilegeId\": \"\",\n \"providerId\": \"3006e047-6d77-413d-9e64-4ab2437571fa\",\n \"dateOfRenewal\": \"1981-11-08\",\n \"dateOfUpdate\": \"\",\n \"status\": \"inactive\"\n }\n }\n ],\n \"jurisdiction\": \"ms\",\n \"licenseJurisdiction\": \"ky\",\n \"licenseType\": \"audiologist\",\n \"privilegeId\": \"\",\n \"providerId\": \"f925179c-fdb4-4dac-ba9f-3bf193b985f5\",\n \"status\": \"inactive\",\n \"type\": \"privilege\",\n \"investigationStatus\": \"underInvestigation\",\n \"investigations\": [\n {\n \"compact\": \"aslp\",\n \"creationDate\": \"\",\n \"dateOfUpdate\": \"\",\n \"investigationId\": \"\",\n \"jurisdiction\": \"nv\",\n \"licenseType\": \"\",\n \"providerId\": \"ec7cd51a-0012-4a5c-a895-1f1c63c21e1a\",\n \"submittingUser\": \"\",\n \"type\": \"investigation\"\n },\n {\n \"compact\": \"octp\",\n \"creationDate\": \"\",\n \"dateOfUpdate\": \"\",\n \"investigationId\": \"\",\n \"jurisdiction\": \"ne\",\n \"licenseType\": \"\",\n \"providerId\": \"36b6bfc4-69b4-4bc8-b8a3-8ce1df068152\",\n \"submittingUser\": \"\",\n \"type\": \"investigation\"\n }\n ],\n \"adverseActions\": [\n {\n \"actionAgainst\": \"\",\n \"adverseActionId\": \"\",\n \"compact\": \"aslp\",\n \"creationDate\": \"1843-03-30\",\n \"dateOfUpdate\": \"\",\n \"effectiveStartDate\": \"1150-09-12\",\n \"encumbranceType\": \"\",\n \"jurisdiction\": \"nj\",\n \"licenseType\": \"\",\n \"licenseTypeAbbreviation\": \"\",\n \"providerId\": \"ebc69eb5-08dd-4001-b706-76e811e1af46\",\n \"type\": \"adverseAction\",\n \"clinicalPrivilegeActionCategories\": [\n \"\",\n \"\"\n ],\n \"effectiveLiftDate\": \"2278-11-23\",\n \"liftingUser\": \"\"\n },\n {\n \"actionAgainst\": \"\",\n \"adverseActionId\": \"\",\n \"compact\": \"aslp\",\n \"creationDate\": \"2103-12-13\",\n \"dateOfUpdate\": \"\",\n \"effectiveStartDate\": \"1144-06-30\",\n \"encumbranceType\": \"\",\n \"jurisdiction\": \"nm\",\n \"licenseType\": \"\",\n \"licenseTypeAbbreviation\": \"\",\n \"providerId\": \"3a0d7dc1-8c20-4143-b9a2-636d01cdea78\",\n \"type\": \"adverseAction\",\n \"clinicalPrivilegeActionCategories\": [\n \"\",\n \"\"\n ],\n \"effectiveLiftDate\": \"1310-10-30\",\n \"liftingUser\": \"\"\n }\n ]\n },\n {\n \"administratorSetStatus\": \"active\",\n \"attestations\": [\n {\n \"attestationId\": \"\",\n \"version\": \"\"\n },\n {\n \"attestationId\": \"\",\n \"version\": \"\"\n }\n ],\n \"compact\": \"coun\",\n \"compactTransactionId\": \"\",\n \"dateOfExpiration\": \"2476-06-31\",\n \"dateOfIssuance\": \"2164-05-30\",\n \"dateOfRenewal\": \"2491-07-08\",\n \"dateOfUpdate\": \"\",\n \"history\": [\n {\n \"compact\": \"coun\",\n \"dateOfUpdate\": \"\",\n \"jurisdiction\": \"ky\",\n \"previous\": {\n \"administratorSetStatus\": \"active\",\n \"attestations\": [\n {\n \"attestationId\": \"\",\n \"version\": \"\"\n },\n {\n \"attestationId\": \"\",\n \"version\": \"\"\n }\n ],\n \"compactTransactionId\": \"\",\n \"dateOfExpiration\": \"1884-03-09\",\n \"dateOfIssuance\": \"1109-11-30\",\n \"dateOfRenewal\": \"1434-07-31\",\n \"dateOfUpdate\": \"\",\n \"licenseJurisdiction\": \"ms\",\n \"privilegeId\": \"\",\n \"compact\": \"aslp\",\n \"jurisdiction\": \"id\",\n \"type\": \"privilege\",\n \"providerId\": \"042c0e25-f384-47e0-865f-e0c1881d911c\",\n \"status\": \"active\"\n },\n \"type\": \"privilegeUpdate\",\n \"updateType\": \"issuance\",\n \"removedValues\": [\n \"\",\n \"\"\n ],\n \"licenseType\": \"speech-language pathologist\",\n \"updatedValues\": {\n \"licenseJurisdiction\": \"me\",\n \"compact\": \"octp\",\n \"jurisdiction\": \"il\",\n \"attestations\": [\n {\n \"attestationId\": \"\",\n \"version\": \"\"\n },\n {\n \"attestationId\": \"\",\n \"version\": \"\"\n }\n ],\n \"type\": \"privilege\",\n \"compactTransactionId\": \"\",\n \"dateOfIssuance\": \"1366-12-25\",\n \"administratorSetStatus\": \"active\",\n \"dateOfExpiration\": \"1562-11-11\",\n \"privilegeId\": \"\",\n \"providerId\": \"a8a7380b-fc46-4982-8c1a-40d23ad49d3d\",\n \"dateOfRenewal\": \"2681-05-28\",\n \"dateOfUpdate\": \"\",\n \"status\": \"active\"\n }\n },\n {\n \"compact\": \"octp\",\n \"dateOfUpdate\": \"\",\n \"jurisdiction\": \"ca\",\n \"previous\": {\n \"administratorSetStatus\": \"active\",\n \"attestations\": [\n {\n \"attestationId\": \"\",\n \"version\": \"\"\n },\n {\n \"attestationId\": \"\",\n \"version\": \"\"\n }\n ],\n \"compactTransactionId\": \"\",\n \"dateOfExpiration\": \"2729-03-06\",\n \"dateOfIssuance\": \"1056-05-16\",\n \"dateOfRenewal\": \"1164-11-30\",\n \"dateOfUpdate\": \"\",\n \"licenseJurisdiction\": \"sd\",\n \"privilegeId\": \"\",\n \"compact\": \"octp\",\n \"jurisdiction\": \"ct\",\n \"type\": \"privilege\",\n \"providerId\": \"a7ea8d0c-0f90-4b11-9dba-5d9ac92ead19\",\n \"status\": \"active\"\n },\n \"type\": \"privilegeUpdate\",\n \"updateType\": \"licenseDeactivation\",\n \"removedValues\": [\n \"\",\n \"\"\n ],\n \"licenseType\": \"speech-language pathologist\",\n \"updatedValues\": {\n \"licenseJurisdiction\": \"me\",\n \"compact\": \"octp\",\n \"jurisdiction\": \"pa\",\n \"attestations\": [\n {\n \"attestationId\": \"\",\n \"version\": \"\"\n },\n {\n \"attestationId\": \"\",\n \"version\": \"\"\n }\n ],\n \"type\": \"privilege\",\n \"compactTransactionId\": \"\",\n \"dateOfIssuance\": \"1084-10-31\",\n \"administratorSetStatus\": \"inactive\",\n \"dateOfExpiration\": \"1728-05-31\",\n \"privilegeId\": \"\",\n \"providerId\": \"7a3481b2-8eef-4265-8276-295aaf2c1876\",\n \"dateOfRenewal\": \"2380-06-02\",\n \"dateOfUpdate\": \"\",\n \"status\": \"inactive\"\n }\n }\n ],\n \"jurisdiction\": \"in\",\n \"licenseJurisdiction\": \"la\",\n \"licenseType\": \"audiologist\",\n \"privilegeId\": \"\",\n \"providerId\": \"a9aebe26-d6ee-4469-ad50-e5c6c6a8ab01\",\n \"status\": \"active\",\n \"type\": \"privilege\",\n \"investigationStatus\": \"underInvestigation\",\n \"investigations\": [\n {\n \"compact\": \"octp\",\n \"creationDate\": \"\",\n \"dateOfUpdate\": \"\",\n \"investigationId\": \"\",\n \"jurisdiction\": \"ia\",\n \"licenseType\": \"\",\n \"providerId\": \"756f6b78-d654-42fe-a4fd-268b3a64b935\",\n \"submittingUser\": \"\",\n \"type\": \"investigation\"\n },\n {\n \"compact\": \"coun\",\n \"creationDate\": \"\",\n \"dateOfUpdate\": \"\",\n \"investigationId\": \"\",\n \"jurisdiction\": \"de\",\n \"licenseType\": \"\",\n \"providerId\": \"347af0e5-ab0a-4710-abb1-cda90749e0a7\",\n \"submittingUser\": \"\",\n \"type\": \"investigation\"\n }\n ],\n \"adverseActions\": [\n {\n \"actionAgainst\": \"\",\n \"adverseActionId\": \"\",\n \"compact\": \"aslp\",\n \"creationDate\": \"1177-06-13\",\n \"dateOfUpdate\": \"\",\n \"effectiveStartDate\": \"2473-12-31\",\n \"encumbranceType\": \"\",\n \"jurisdiction\": \"ri\",\n \"licenseType\": \"\",\n \"licenseTypeAbbreviation\": \"\",\n \"providerId\": \"6e0f8dda-0eae-495f-a997-62afae8a758a\",\n \"type\": \"adverseAction\",\n \"clinicalPrivilegeActionCategories\": [\n \"\",\n \"\"\n ],\n \"effectiveLiftDate\": \"2698-07-02\",\n \"liftingUser\": \"\"\n },\n {\n \"actionAgainst\": \"\",\n \"adverseActionId\": \"\",\n \"compact\": \"coun\",\n \"creationDate\": \"1442-04-05\",\n \"dateOfUpdate\": \"\",\n \"effectiveStartDate\": \"2623-11-06\",\n \"encumbranceType\": \"\",\n \"jurisdiction\": \"ms\",\n \"licenseType\": \"\",\n \"licenseTypeAbbreviation\": \"\",\n \"providerId\": \"075c0ce7-03c6-47c0-ab4f-a86d38478e7f\",\n \"type\": \"adverseAction\",\n \"clinicalPrivilegeActionCategories\": [\n \"\",\n \"\"\n ],\n \"effectiveLiftDate\": \"1443-04-15\",\n \"liftingUser\": \"\"\n }\n ]\n }\n ],\n \"providerId\": \"9ef10b59-8448-4323-aa48-8b105bca3f19\",\n \"type\": \"provider\",\n \"suffix\": \"\",\n \"compactConnectRegisteredEmailAddress\": \"\",\n \"npi\": \"8843793071\",\n \"compactEligibility\": \"ineligible\",\n \"jurisdictionUploadedCompactEligibility\": \"ineligible\",\n \"dateOfBirth\": \"2100-12-01\",\n \"jurisdictionUploadedLicenseStatus\": \"active\",\n \"currentHomeJurisdiction\": \"nh\",\n \"militaryStatusNote\": \"\",\n \"ssnLastFour\": \"3500\",\n \"militaryStatus\": \"declined\",\n \"licenseStatus\": \"inactive\",\n \"middleName\": \"\"\n}", "code": 200, "cookie": [], "header": [ @@ -4568,7 +4568,7 @@ "value": "application/json" } ], - "id": "c5a41dd5-e566-490c-8587-7f55365c2cee", + "id": "945c6c9c-01d4-47ce-9cd7-2670938ca522", "name": "200 response", "originalRequest": { "body": {}, @@ -4609,7 +4609,7 @@ "item": [ { "event": [], - "id": "7054f10c-2607-45ea-8edb-9ad3aff21dbf", + "id": "c6af9341-f547-45b9-b425-42be3a648465", "name": "/v1/provider-users/me/email", "protocolProfileBehavior": { "disableBodyPruning": true @@ -4664,7 +4664,7 @@ "value": "application/json" } ], - "id": "e4d9943a-510c-4211-a9a0-6a6c5dde32c3", + "id": "a5cc1e0c-6b49-47a7-98e4-248478651583", "name": "200 response", "originalRequest": { "body": { @@ -4719,7 +4719,7 @@ "item": [ { "event": [], - "id": "3f398de5-c110-45ba-a446-893b7ffb5213", + "id": "8af9e313-7b9e-4f31-9f7e-14bf464e8958", "name": "/v1/provider-users/me/email/verify", "protocolProfileBehavior": { "disableBodyPruning": true @@ -4733,7 +4733,7 @@ "language": "json" } }, - "raw": "{\n \"verificationCode\": \"3402\"\n}" + "raw": "{\n \"verificationCode\": \"0866\"\n}" }, "description": {}, "header": [ @@ -4775,7 +4775,7 @@ "value": "application/json" } ], - "id": "cae72537-da94-4aab-b907-31cedea0288d", + "id": "97ef3733-9aa7-4317-9a25-5db802416619", "name": "200 response", "originalRequest": { "body": { @@ -4786,7 +4786,7 @@ "language": "json" } }, - "raw": "{\n \"verificationCode\": \"3402\"\n}" + "raw": "{\n \"verificationCode\": \"0866\"\n}" }, "header": [ { @@ -4837,7 +4837,7 @@ "item": [ { "event": [], - "id": "e9d1a9bb-2372-4c55-8a61-cae3b4745fdf", + "id": "332a1915-0bd1-40e1-85af-aa2391228a6e", "name": "/v1/provider-users/me/home-jurisdiction", "protocolProfileBehavior": { "disableBodyPruning": true @@ -4851,7 +4851,7 @@ "language": "json" } }, - "raw": "{\n \"jurisdiction\": \"de\"\n}" + "raw": "{\n \"jurisdiction\": \"ok\"\n}" }, "description": {}, "header": [ @@ -4892,7 +4892,7 @@ "value": "application/json" } ], - "id": "476299f3-96e3-4920-8e1a-3012b12f8eeb", + "id": "5a435fb5-e1f5-41f1-93a9-fc5cbe10d05d", "name": "200 response", "originalRequest": { "body": { @@ -4903,7 +4903,7 @@ "language": "json" } }, - "raw": "{\n \"jurisdiction\": \"de\"\n}" + "raw": "{\n \"jurisdiction\": \"ok\"\n}" }, "header": [ { @@ -4962,7 +4962,7 @@ "item": [ { "event": [], - "id": "b30f32c2-4076-4abc-a419-ddd5048c132e", + "id": "ea2f75ba-0869-47c7-98be-095aa7a01658", "name": "/v1/provider-users/me/jurisdiction/:jurisdiction/licenseType/:licenseType/history", "protocolProfileBehavior": { "disableBodyPruning": true @@ -5020,7 +5020,7 @@ "response": [ { "_postman_previewlanguage": "json", - "body": "{\n \"compact\": \"octp\",\n \"events\": [\n {\n \"createDate\": \"\",\n \"dateOfUpdate\": \"\",\n \"effectiveDate\": \"1680-11-27\",\n \"type\": \"privilegeUpdate\",\n \"updateType\": \"encumbrance\",\n \"note\": \"\"\n },\n {\n \"createDate\": \"\",\n \"dateOfUpdate\": \"\",\n \"effectiveDate\": \"2994-12-28\",\n \"type\": \"privilegeUpdate\",\n \"updateType\": \"other\",\n \"note\": \"\"\n }\n ],\n \"jurisdiction\": \"id\",\n \"licenseType\": \"audiologist\",\n \"privilegeId\": \"\",\n \"providerId\": \"583662f6-05d7-4dd9-af6c-ce0620242a3d\"\n}", + "body": "{\n \"compact\": \"aslp\",\n \"events\": [\n {\n \"createDate\": \"\",\n \"dateOfUpdate\": \"\",\n \"effectiveDate\": \"2221-06-30\",\n \"type\": \"privilegeUpdate\",\n \"updateType\": \"lifting_encumbrance\",\n \"note\": \"\",\n \"npdbCategories\": [\n \"\",\n \"\"\n ]\n },\n {\n \"createDate\": \"\",\n \"dateOfUpdate\": \"\",\n \"effectiveDate\": \"2677-12-30\",\n \"type\": \"privilegeUpdate\",\n \"updateType\": \"emailChange\",\n \"note\": \"\",\n \"npdbCategories\": [\n \"\",\n \"\"\n ]\n }\n ],\n \"jurisdiction\": \"ne\",\n \"licenseType\": \"occupational therapist\",\n \"privilegeId\": \"\",\n \"providerId\": \"2d4b50f1-d301-4b90-a1f3-1442018658aa\"\n}", "code": 200, "cookie": [], "header": [ @@ -5029,7 +5029,7 @@ "value": "application/json" } ], - "id": "4af8fdd8-38da-45ca-a6db-fbd25111a3a6", + "id": "6e3df0d8-140e-41c2-9d7f-2109a9061d40", "name": "200 response", "originalRequest": { "body": {}, @@ -5090,7 +5090,7 @@ "item": [ { "event": [], - "id": "435a6200-6b3d-4a7f-81a9-8871f8a25a62", + "id": "f0780ae5-d2f7-49cb-9546-127c3aac5bc7", "name": "/v1/provider-users/me/military-affiliation", "protocolProfileBehavior": { "disableBodyPruning": true @@ -5104,7 +5104,7 @@ "language": "json" } }, - "raw": "{\n \"affiliationType\": \"militaryMember\",\n \"fileNames\": [\n \"\",\n \"\"\n ]\n}" + "raw": "{\n \"affiliationType\": \"militaryMemberSpouse\",\n \"fileNames\": [\n \"\",\n \"\"\n ]\n}" }, "description": {}, "header": [ @@ -5136,7 +5136,7 @@ "response": [ { "_postman_previewlanguage": "json", - "body": "{\n \"affiliationType\": \"militaryMemberSpouse\",\n \"dateOfUpdate\": \"\",\n \"dateOfUpload\": \"2016-11-12\",\n \"documentUploadFields\": [\n {\n \"fields\": {\n \"mollit8fd\": \"\",\n \"dolore_050\": \"\",\n \"velite9\": \"\"\n },\n \"url\": \"\"\n },\n {\n \"fields\": {\n \"minim7f\": \"\"\n },\n \"url\": \"\"\n }\n ],\n \"status\": \"\",\n \"fileNames\": [\n \"\",\n \"\"\n ]\n}", + "body": "{\n \"affiliationType\": \"militaryMember\",\n \"dateOfUpdate\": \"\",\n \"dateOfUpload\": \"2935-10-01\",\n \"documentUploadFields\": [\n {\n \"fields\": {\n \"sit_62\": \"\",\n \"exercitation_3\": \"\",\n \"tempor_c_9\": \"\"\n },\n \"url\": \"\"\n },\n {\n \"fields\": {\n \"proident5\": \"\",\n \"est_c\": \"\",\n \"in1\": \"\"\n },\n \"url\": \"\"\n }\n ],\n \"status\": \"\",\n \"fileNames\": [\n \"\",\n \"\"\n ]\n}", "code": 200, "cookie": [], "header": [ @@ -5145,7 +5145,7 @@ "value": "application/json" } ], - "id": "5ff97bfb-9590-4ab4-b3f2-805b9ec618af", + "id": "91c53014-a8da-4f8f-97d8-56d342866634", "name": "200 response", "originalRequest": { "body": { @@ -5156,7 +5156,7 @@ "language": "json" } }, - "raw": "{\n \"affiliationType\": \"militaryMember\",\n \"fileNames\": [\n \"\",\n \"\"\n ]\n}" + "raw": "{\n \"affiliationType\": \"militaryMemberSpouse\",\n \"fileNames\": [\n \"\",\n \"\"\n ]\n}" }, "header": [ { @@ -5197,7 +5197,7 @@ }, { "event": [], - "id": "2fcef1ba-b0c7-4199-8438-acebde5430dc", + "id": "7dc62a86-0697-4621-b926-b8b05182356f", "name": "/v1/provider-users/me/military-affiliation", "protocolProfileBehavior": { "disableBodyPruning": true @@ -5252,7 +5252,7 @@ "value": "application/json" } ], - "id": "7dd31df9-e908-4ccc-8b97-52a6a7f59218", + "id": "10e103d7-05c4-4640-b28f-481f272cdef8", "name": "200 response", "originalRequest": { "body": { @@ -5313,7 +5313,7 @@ "item": [ { "event": [], - "id": "0339e3c8-8d3d-41e5-b212-2d277f2b1b5b", + "id": "ceb9d94f-0b66-46ac-abd4-5b389fa27c8d", "name": "/v1/provider-users/registration", "protocolProfileBehavior": { "disableBodyPruning": true @@ -5330,7 +5330,7 @@ "language": "json" } }, - "raw": "{\n \"compact\": \"\",\n \"dob\": \"2324-09-04\",\n \"email\": \"\",\n \"familyName\": \"\",\n \"givenName\": \"\",\n \"jurisdiction\": \"sc\",\n \"licenseType\": \"speech-language pathologist\",\n \"partialSocial\": \"\",\n \"token\": \"\"\n}" + "raw": "{\n \"compact\": \"\",\n \"dob\": \"1163-10-31\",\n \"email\": \"\",\n \"familyName\": \"\",\n \"givenName\": \"\",\n \"jurisdiction\": \"sc\",\n \"licenseType\": \"speech-language pathologist\",\n \"partialSocial\": \"\",\n \"token\": \"\"\n}" }, "description": {}, "header": [ @@ -5370,7 +5370,7 @@ "value": "application/json" } ], - "id": "c540498f-d862-4271-aedc-51dae5bb5c3e", + "id": "87da58e5-324f-46c3-afd0-a92a878ea5b3", "name": "200 response", "originalRequest": { "body": { @@ -5381,7 +5381,7 @@ "language": "json" } }, - "raw": "{\n \"compact\": \"\",\n \"dob\": \"2324-09-04\",\n \"email\": \"\",\n \"familyName\": \"\",\n \"givenName\": \"\",\n \"jurisdiction\": \"sc\",\n \"licenseType\": \"speech-language pathologist\",\n \"partialSocial\": \"\",\n \"token\": \"\"\n}" + "raw": "{\n \"compact\": \"\",\n \"dob\": \"1163-10-31\",\n \"email\": \"\",\n \"familyName\": \"\",\n \"givenName\": \"\",\n \"jurisdiction\": \"sc\",\n \"licenseType\": \"speech-language pathologist\",\n \"partialSocial\": \"\",\n \"token\": \"\"\n}" }, "header": [ { @@ -5419,7 +5419,7 @@ "item": [ { "event": [], - "id": "55cd4d84-a628-4add-be1c-ae554d87ecfc", + "id": "2549647d-ba31-4da2-bfa5-f6055f57dc54", "name": "/v1/provider-users/verifyRecovery", "protocolProfileBehavior": { "disableBodyPruning": true @@ -5436,7 +5436,7 @@ "language": "json" } }, - "raw": "{\n \"compact\": \"octp\",\n \"providerId\": \"54c57e85-92d7-4c87-984c-5c4614cdcef2\",\n \"recaptchaToken\": \"\",\n \"recoveryToken\": \"\"\n}" + "raw": "{\n \"compact\": \"octp\",\n \"providerId\": \"f7badcbe-8663-40f1-91e3-666ca9c12329\",\n \"recaptchaToken\": \"\",\n \"recoveryToken\": \"\"\n}" }, "description": {}, "header": [ @@ -5476,7 +5476,7 @@ "value": "application/json" } ], - "id": "e9e3c5de-c55e-452f-9e9b-9f720a70d871", + "id": "37814e7f-0803-41a9-a927-67e9eb6a3d15", "name": "200 response", "originalRequest": { "body": { @@ -5487,7 +5487,7 @@ "language": "json" } }, - "raw": "{\n \"compact\": \"octp\",\n \"providerId\": \"54c57e85-92d7-4c87-984c-5c4614cdcef2\",\n \"recaptchaToken\": \"\",\n \"recoveryToken\": \"\"\n}" + "raw": "{\n \"compact\": \"octp\",\n \"providerId\": \"f7badcbe-8663-40f1-91e3-666ca9c12329\",\n \"recaptchaToken\": \"\",\n \"recoveryToken\": \"\"\n}" }, "header": [ { @@ -5537,7 +5537,7 @@ "item": [ { "event": [], - "id": "3755cff7-19a3-456d-97df-afaec1090895", + "id": "ce7e9d8c-bd11-415c-96ce-42524c50b377", "name": "/v1/public/compacts/:compact/jurisdictions", "protocolProfileBehavior": { "disableBodyPruning": true @@ -5594,7 +5594,7 @@ "value": "application/json" } ], - "id": "34f9f419-3299-4095-a56b-c01e82ecf332", + "id": "46c95289-16bc-4411-9fc6-cda8902595d7", "name": "200 response", "originalRequest": { "body": {}, @@ -5635,7 +5635,7 @@ "item": [ { "event": [], - "id": "40c8362f-e3cf-4e28-b026-a4a9a9b5bfd9", + "id": "2d09c684-bb71-4ebe-99cc-45a61caccdc8", "name": "/v1/public/compacts/:compact/providers/query", "protocolProfileBehavior": { "disableBodyPruning": true @@ -5652,7 +5652,7 @@ "language": "json" } }, - "raw": "{\n \"query\": {\n \"providerId\": \"8ab484ca-2bcd-4109-8bd1-732bba4947b7\",\n \"jurisdiction\": \"in\",\n \"givenName\": \"\",\n \"familyName\": \"\"\n },\n \"pagination\": {\n \"lastKey\": \"\",\n \"pageSize\": \"\"\n },\n \"sorting\": {\n \"key\": \"dateOfUpdate\",\n \"direction\": \"ascending\"\n }\n}" + "raw": "{\n \"query\": {\n \"providerId\": \"4c7413e0-fa19-4e6c-b44f-b4ac49ffd716\",\n \"jurisdiction\": \"ia\",\n \"givenName\": \"\",\n \"familyName\": \"\"\n },\n \"pagination\": {\n \"lastKey\": \"\",\n \"pageSize\": \"\"\n },\n \"sorting\": {\n \"key\": \"familyName\",\n \"direction\": \"ascending\"\n }\n}" }, "description": {}, "header": [ @@ -5697,7 +5697,7 @@ "response": [ { "_postman_previewlanguage": "json", - "body": "{\n \"pagination\": {\n \"prevLastKey\": {},\n \"lastKey\": {},\n \"pageSize\": \"\"\n },\n \"providers\": [\n {\n \"compact\": \"coun\",\n \"familyName\": \"\",\n \"givenName\": \"\",\n \"licenseJurisdiction\": \"fl\",\n \"privilegeJurisdictions\": [\n \"nv\",\n \"sd\"\n ],\n \"providerId\": \"375337bb-657c-4d68-a3a4-0a17dbe4da57\",\n \"type\": \"provider\",\n \"npi\": \"0813673874\",\n \"middleName\": \"\",\n \"suffix\": \"\",\n \"currentHomeJurisdiction\": \"nc\",\n \"dateOfUpdate\": \"\"\n },\n {\n \"compact\": \"coun\",\n \"familyName\": \"\",\n \"givenName\": \"\",\n \"licenseJurisdiction\": \"nh\",\n \"privilegeJurisdictions\": [\n \"hi\",\n \"az\"\n ],\n \"providerId\": \"f1a40ad2-7d55-476f-a8c9-7e5ea649bee2\",\n \"type\": \"provider\",\n \"npi\": \"9067266875\",\n \"middleName\": \"\",\n \"suffix\": \"\",\n \"currentHomeJurisdiction\": \"mi\",\n \"dateOfUpdate\": \"\"\n }\n ],\n \"query\": {\n \"providerId\": \"d7151651-7040-4ec1-b5ab-b50c7e9e3d21\",\n \"jurisdiction\": \"de\",\n \"givenName\": \"\",\n \"familyName\": \"\"\n },\n \"sorting\": {\n \"key\": \"dateOfUpdate\",\n \"direction\": \"ascending\"\n }\n}", + "body": "{\n \"pagination\": {\n \"prevLastKey\": {},\n \"lastKey\": {},\n \"pageSize\": \"\"\n },\n \"providers\": [\n {\n \"compact\": \"octp\",\n \"familyName\": \"\",\n \"givenName\": \"\",\n \"licenseJurisdiction\": \"wy\",\n \"privilegeJurisdictions\": [\n \"vt\",\n \"mn\"\n ],\n \"providerId\": \"e5b53933-e7ee-4c73-84ca-2d72ba8a2148\",\n \"type\": \"provider\",\n \"npi\": \"9301134303\",\n \"middleName\": \"\",\n \"suffix\": \"\",\n \"currentHomeJurisdiction\": \"ia\",\n \"dateOfUpdate\": \"\"\n },\n {\n \"compact\": \"coun\",\n \"familyName\": \"\",\n \"givenName\": \"\",\n \"licenseJurisdiction\": \"tx\",\n \"privilegeJurisdictions\": [\n \"nh\",\n \"in\"\n ],\n \"providerId\": \"87eb8024-b3c0-48ca-840d-9621ead456a5\",\n \"type\": \"provider\",\n \"npi\": \"6359122001\",\n \"middleName\": \"\",\n \"suffix\": \"\",\n \"currentHomeJurisdiction\": \"ok\",\n \"dateOfUpdate\": \"\"\n }\n ],\n \"query\": {\n \"providerId\": \"3ffe1026-2208-4ade-ad68-14698161dd10\",\n \"jurisdiction\": \"ia\",\n \"givenName\": \"\",\n \"familyName\": \"\"\n },\n \"sorting\": {\n \"key\": \"familyName\",\n \"direction\": \"descending\"\n }\n}", "code": 200, "cookie": [], "header": [ @@ -5706,7 +5706,7 @@ "value": "application/json" } ], - "id": "1d724c4c-eb68-4cb2-afaf-03694180f3b8", + "id": "e79a3568-c0f9-44ee-b09a-f789ad55ec17", "name": "200 response", "originalRequest": { "body": { @@ -5717,7 +5717,7 @@ "language": "json" } }, - "raw": "{\n \"query\": {\n \"providerId\": \"8ab484ca-2bcd-4109-8bd1-732bba4947b7\",\n \"jurisdiction\": \"in\",\n \"givenName\": \"\",\n \"familyName\": \"\"\n },\n \"pagination\": {\n \"lastKey\": \"\",\n \"pageSize\": \"\"\n },\n \"sorting\": {\n \"key\": \"dateOfUpdate\",\n \"direction\": \"ascending\"\n }\n}" + "raw": "{\n \"query\": {\n \"providerId\": \"4c7413e0-fa19-4e6c-b44f-b4ac49ffd716\",\n \"jurisdiction\": \"ia\",\n \"givenName\": \"\",\n \"familyName\": \"\"\n },\n \"pagination\": {\n \"lastKey\": \"\",\n \"pageSize\": \"\"\n },\n \"sorting\": {\n \"key\": \"familyName\",\n \"direction\": \"ascending\"\n }\n}" }, "header": [ { @@ -5758,7 +5758,7 @@ "item": [ { "event": [], - "id": "cbc6fd87-3d5b-4f23-839c-fc7ca7e468ea", + "id": "bc508dd4-0e97-45f2-840f-a548d8fe1e7b", "name": "/v1/public/compacts/:compact/providers/:providerId", "protocolProfileBehavior": { "disableBodyPruning": true @@ -5817,7 +5817,7 @@ "response": [ { "_postman_previewlanguage": "json", - "body": "{\n \"compact\": \"coun\",\n \"dateOfUpdate\": \"\",\n \"familyName\": \"\",\n \"givenName\": \"\",\n \"licenseJurisdiction\": \"ar\",\n \"privilegeJurisdictions\": [\n \"ks\",\n \"tn\"\n ],\n \"providerId\": \"3bf898e8-3033-47b5-a5ac-9a281fc29a1b\",\n \"type\": \"provider\",\n \"privileges\": [\n {\n \"administratorSetStatus\": \"active\",\n \"compact\": \"octp\",\n \"dateOfExpiration\": \"2042-12-01\",\n \"dateOfIssuance\": \"2348-03-16\",\n \"dateOfRenewal\": \"1722-03-01\",\n \"dateOfUpdate\": \"\",\n \"jurisdiction\": \"md\",\n \"licenseJurisdiction\": \"ne\",\n \"licenseType\": \"audiologist\",\n \"privilegeId\": \"\",\n \"providerId\": \"f72c3377-a212-4e09-b315-81c4a44eb586\",\n \"status\": \"inactive\",\n \"type\": \"privilege\",\n \"history\": [\n {\n \"compact\": \"aslp\",\n \"dateOfUpdate\": \"\",\n \"jurisdiction\": \"al\",\n \"licenseType\": \"occupational therapist\",\n \"previous\": {\n \"administratorSetStatus\": \"inactive\",\n \"dateOfExpiration\": \"2831-12-05\",\n \"dateOfIssuance\": \"2694-06-29\",\n \"dateOfRenewal\": \"2944-06-01\",\n \"dateOfUpdate\": \"\",\n \"licenseJurisdiction\": \"or\",\n \"privilegeId\": \"\"\n },\n \"providerId\": \"1a8d0b13-f6a8-4136-a24b-9660790964e8\",\n \"type\": \"privilegeUpdate\",\n \"updateType\": \"licenseDeactivation\",\n \"updatedValues\": {\n \"administratorSetStatus\": \"active\",\n \"dateOfExpiration\": \"1300-01-05\",\n \"licenseJurisdiction\": \"co\",\n \"privilegeId\": \"\",\n \"dateOfRenewal\": \"1490-03-24\",\n \"dateOfIssuance\": \"1881-12-18\",\n \"dateOfUpdate\": \"\"\n }\n },\n {\n \"compact\": \"aslp\",\n \"dateOfUpdate\": \"\",\n \"jurisdiction\": \"wv\",\n \"licenseType\": \"licensed professional counselor\",\n \"previous\": {\n \"administratorSetStatus\": \"active\",\n \"dateOfExpiration\": \"2520-06-30\",\n \"dateOfIssuance\": \"1400-03-31\",\n \"dateOfRenewal\": \"1865-03-24\",\n \"dateOfUpdate\": \"\",\n \"licenseJurisdiction\": \"ny\",\n \"privilegeId\": \"\"\n },\n \"providerId\": \"37723210-3255-4a1d-a918-85450c0f525a\",\n \"type\": \"privilegeUpdate\",\n \"updateType\": \"homeJurisdictionChange\",\n \"updatedValues\": {\n \"administratorSetStatus\": \"active\",\n \"dateOfExpiration\": \"2338-11-08\",\n \"licenseJurisdiction\": \"dc\",\n \"privilegeId\": \"\",\n \"dateOfRenewal\": \"2388-11-30\",\n \"dateOfIssuance\": \"1039-09-07\",\n \"dateOfUpdate\": \"\"\n }\n }\n ],\n \"adverseActions\": [\n {\n \"actionAgainst\": \"\",\n \"adverseActionId\": \"\",\n \"compact\": \"aslp\",\n \"creationDate\": \"2872-11-07\",\n \"dateOfUpdate\": \"\",\n \"effectiveStartDate\": \"1671-06-08\",\n \"jurisdiction\": \"dc\",\n \"licenseType\": \"\",\n \"licenseTypeAbbreviation\": \"\",\n \"providerId\": \"4f2fe7b9-5930-4d10-a6e9-1a10a13a101b\",\n \"type\": \"adverseAction\",\n \"effectiveLiftDate\": \"1930-03-28\"\n },\n {\n \"actionAgainst\": \"\",\n \"adverseActionId\": \"\",\n \"compact\": \"aslp\",\n \"creationDate\": \"2667-10-30\",\n \"dateOfUpdate\": \"\",\n \"effectiveStartDate\": \"1936-05-26\",\n \"jurisdiction\": \"al\",\n \"licenseType\": \"\",\n \"licenseTypeAbbreviation\": \"\",\n \"providerId\": \"6cd7dfff-a2f5-4976-a393-b74289c629fb\",\n \"type\": \"adverseAction\",\n \"effectiveLiftDate\": \"1074-04-30\"\n }\n ]\n },\n {\n \"administratorSetStatus\": \"active\",\n \"compact\": \"aslp\",\n \"dateOfExpiration\": \"2746-11-31\",\n \"dateOfIssuance\": \"1256-05-31\",\n \"dateOfRenewal\": \"2932-03-30\",\n \"dateOfUpdate\": \"\",\n \"jurisdiction\": \"wy\",\n \"licenseJurisdiction\": \"vt\",\n \"licenseType\": \"licensed professional counselor\",\n \"privilegeId\": \"\",\n \"providerId\": \"298f4cbf-8a94-4611-9577-73d80553f030\",\n \"status\": \"inactive\",\n \"type\": \"privilege\",\n \"history\": [\n {\n \"compact\": \"coun\",\n \"dateOfUpdate\": \"\",\n \"jurisdiction\": \"co\",\n \"licenseType\": \"licensed professional counselor\",\n \"previous\": {\n \"administratorSetStatus\": \"active\",\n \"dateOfExpiration\": \"1383-10-18\",\n \"dateOfIssuance\": \"1065-11-30\",\n \"dateOfRenewal\": \"2957-11-17\",\n \"dateOfUpdate\": \"\",\n \"licenseJurisdiction\": \"vt\",\n \"privilegeId\": \"\"\n },\n \"providerId\": \"20fb9e08-95e4-4362-8c92-5a18923429de\",\n \"type\": \"privilegeUpdate\",\n \"updateType\": \"registration\",\n \"updatedValues\": {\n \"administratorSetStatus\": \"active\",\n \"dateOfExpiration\": \"2259-10-09\",\n \"licenseJurisdiction\": \"vt\",\n \"privilegeId\": \"\",\n \"dateOfRenewal\": \"1144-10-30\",\n \"dateOfIssuance\": \"2121-10-03\",\n \"dateOfUpdate\": \"\"\n }\n },\n {\n \"compact\": \"octp\",\n \"dateOfUpdate\": \"\",\n \"jurisdiction\": \"pr\",\n \"licenseType\": \"audiologist\",\n \"previous\": {\n \"administratorSetStatus\": \"active\",\n \"dateOfExpiration\": \"2458-12-23\",\n \"dateOfIssuance\": \"1261-11-14\",\n \"dateOfRenewal\": \"2780-02-08\",\n \"dateOfUpdate\": \"\",\n \"licenseJurisdiction\": \"ri\",\n \"privilegeId\": \"\"\n },\n \"providerId\": \"502c873b-a49c-4b13-aad0-a3ffca8499e5\",\n \"type\": \"privilegeUpdate\",\n \"updateType\": \"renewal\",\n \"updatedValues\": {\n \"administratorSetStatus\": \"active\",\n \"dateOfExpiration\": \"1230-01-30\",\n \"licenseJurisdiction\": \"nh\",\n \"privilegeId\": \"\",\n \"dateOfRenewal\": \"2726-08-08\",\n \"dateOfIssuance\": \"1947-02-28\",\n \"dateOfUpdate\": \"\"\n }\n }\n ],\n \"adverseActions\": [\n {\n \"actionAgainst\": \"\",\n \"adverseActionId\": \"\",\n \"compact\": \"coun\",\n \"creationDate\": \"1147-12-24\",\n \"dateOfUpdate\": \"\",\n \"effectiveStartDate\": \"2118-06-03\",\n \"jurisdiction\": \"la\",\n \"licenseType\": \"\",\n \"licenseTypeAbbreviation\": \"\",\n \"providerId\": \"4e47be52-dd03-4cf3-a285-6f5a86ba8a6b\",\n \"type\": \"adverseAction\",\n \"effectiveLiftDate\": \"1395-02-24\"\n },\n {\n \"actionAgainst\": \"\",\n \"adverseActionId\": \"\",\n \"compact\": \"coun\",\n \"creationDate\": \"1033-10-08\",\n \"dateOfUpdate\": \"\",\n \"effectiveStartDate\": \"2644-08-01\",\n \"jurisdiction\": \"ks\",\n \"licenseType\": \"\",\n \"licenseTypeAbbreviation\": \"\",\n \"providerId\": \"d0a3dff7-089a-40b5-a395-18e297112b9c\",\n \"type\": \"adverseAction\",\n \"effectiveLiftDate\": \"1543-04-20\"\n }\n ]\n }\n ],\n \"npi\": \"7554928476\",\n \"suffix\": \"\",\n \"currentHomeJurisdiction\": \"pa\",\n \"middleName\": \"\"\n}", + "body": "{\n \"compact\": \"aslp\",\n \"dateOfUpdate\": \"\",\n \"familyName\": \"\",\n \"givenName\": \"\",\n \"licenseJurisdiction\": \"ma\",\n \"privilegeJurisdictions\": [\n \"nd\",\n \"ny\"\n ],\n \"providerId\": \"42ebd66f-caea-4f91-957d-5e32f41bed5f\",\n \"type\": \"provider\",\n \"privileges\": [\n {\n \"administratorSetStatus\": \"active\",\n \"compact\": \"aslp\",\n \"dateOfExpiration\": \"1353-04-31\",\n \"dateOfIssuance\": \"1862-12-25\",\n \"dateOfRenewal\": \"2275-02-30\",\n \"dateOfUpdate\": \"\",\n \"jurisdiction\": \"or\",\n \"licenseJurisdiction\": \"in\",\n \"licenseType\": \"occupational therapist\",\n \"privilegeId\": \"\",\n \"providerId\": \"a67c57d3-ff47-416c-ad37-6361ac86dae3\",\n \"status\": \"active\",\n \"type\": \"privilege\",\n \"history\": [\n {\n \"compact\": \"octp\",\n \"dateOfUpdate\": \"\",\n \"jurisdiction\": \"md\",\n \"licenseType\": \"audiologist\",\n \"previous\": {\n \"administratorSetStatus\": \"inactive\",\n \"dateOfExpiration\": \"2680-09-18\",\n \"dateOfIssuance\": \"2523-12-07\",\n \"dateOfRenewal\": \"1608-11-24\",\n \"dateOfUpdate\": \"\",\n \"licenseJurisdiction\": \"nd\",\n \"privilegeId\": \"\"\n },\n \"providerId\": \"db0f5520-d261-4158-aa81-f0e17355e078\",\n \"type\": \"privilegeUpdate\",\n \"updateType\": \"homeJurisdictionChange\",\n \"updatedValues\": {\n \"administratorSetStatus\": \"active\",\n \"dateOfExpiration\": \"2620-11-31\",\n \"licenseJurisdiction\": \"me\",\n \"privilegeId\": \"\",\n \"dateOfRenewal\": \"2508-02-30\",\n \"dateOfIssuance\": \"2792-10-12\",\n \"dateOfUpdate\": \"\"\n }\n },\n {\n \"compact\": \"aslp\",\n \"dateOfUpdate\": \"\",\n \"jurisdiction\": \"wi\",\n \"licenseType\": \"occupational therapy assistant\",\n \"previous\": {\n \"administratorSetStatus\": \"inactive\",\n \"dateOfExpiration\": \"2603-10-07\",\n \"dateOfIssuance\": \"2239-10-08\",\n \"dateOfRenewal\": \"1678-12-31\",\n \"dateOfUpdate\": \"\",\n \"licenseJurisdiction\": \"ak\",\n \"privilegeId\": \"\"\n },\n \"providerId\": \"45911944-9e6c-436e-b19d-ae56fc3c65ca\",\n \"type\": \"privilegeUpdate\",\n \"updateType\": \"lifting_encumbrance\",\n \"updatedValues\": {\n \"administratorSetStatus\": \"inactive\",\n \"dateOfExpiration\": \"2882-12-12\",\n \"licenseJurisdiction\": \"va\",\n \"privilegeId\": \"\",\n \"dateOfRenewal\": \"2856-10-30\",\n \"dateOfIssuance\": \"1121-11-15\",\n \"dateOfUpdate\": \"\"\n }\n }\n ],\n \"adverseActions\": [\n {\n \"actionAgainst\": \"\",\n \"adverseActionId\": \"\",\n \"compact\": \"octp\",\n \"creationDate\": \"2509-08-14\",\n \"dateOfUpdate\": \"\",\n \"effectiveStartDate\": \"2193-12-31\",\n \"jurisdiction\": \"wy\",\n \"licenseType\": \"\",\n \"licenseTypeAbbreviation\": \"\",\n \"providerId\": \"291ec5d7-e8d3-4515-9482-18292b9c5883\",\n \"type\": \"adverseAction\",\n \"effectiveLiftDate\": \"1125-06-19\"\n },\n {\n \"actionAgainst\": \"\",\n \"adverseActionId\": \"\",\n \"compact\": \"coun\",\n \"creationDate\": \"1513-03-04\",\n \"dateOfUpdate\": \"\",\n \"effectiveStartDate\": \"2537-12-03\",\n \"jurisdiction\": \"mo\",\n \"licenseType\": \"\",\n \"licenseTypeAbbreviation\": \"\",\n \"providerId\": \"b084d2ee-4d0a-48ba-a795-82ce69751404\",\n \"type\": \"adverseAction\",\n \"effectiveLiftDate\": \"1934-11-22\"\n }\n ]\n },\n {\n \"administratorSetStatus\": \"inactive\",\n \"compact\": \"octp\",\n \"dateOfExpiration\": \"1415-12-23\",\n \"dateOfIssuance\": \"1659-06-28\",\n \"dateOfRenewal\": \"2498-12-30\",\n \"dateOfUpdate\": \"\",\n \"jurisdiction\": \"ga\",\n \"licenseJurisdiction\": \"vi\",\n \"licenseType\": \"occupational therapy assistant\",\n \"privilegeId\": \"\",\n \"providerId\": \"10221401-d577-4f2d-8b27-b266bc6aee7a\",\n \"status\": \"active\",\n \"type\": \"privilege\",\n \"history\": [\n {\n \"compact\": \"aslp\",\n \"dateOfUpdate\": \"\",\n \"jurisdiction\": \"co\",\n \"licenseType\": \"audiologist\",\n \"previous\": {\n \"administratorSetStatus\": \"inactive\",\n \"dateOfExpiration\": \"1421-06-30\",\n \"dateOfIssuance\": \"1855-09-15\",\n \"dateOfRenewal\": \"1595-08-31\",\n \"dateOfUpdate\": \"\",\n \"licenseJurisdiction\": \"me\",\n \"privilegeId\": \"\"\n },\n \"providerId\": \"66085610-df75-4710-ab4b-f9efaa4d5a48\",\n \"type\": \"privilegeUpdate\",\n \"updateType\": \"registration\",\n \"updatedValues\": {\n \"administratorSetStatus\": \"active\",\n \"dateOfExpiration\": \"1063-05-05\",\n \"licenseJurisdiction\": \"id\",\n \"privilegeId\": \"\",\n \"dateOfRenewal\": \"2734-12-01\",\n \"dateOfIssuance\": \"1242-02-30\",\n \"dateOfUpdate\": \"\"\n }\n },\n {\n \"compact\": \"coun\",\n \"dateOfUpdate\": \"\",\n \"jurisdiction\": \"ct\",\n \"licenseType\": \"audiologist\",\n \"previous\": {\n \"administratorSetStatus\": \"active\",\n \"dateOfExpiration\": \"2309-05-06\",\n \"dateOfIssuance\": \"1513-03-11\",\n \"dateOfRenewal\": \"1002-10-06\",\n \"dateOfUpdate\": \"\",\n \"licenseJurisdiction\": \"la\",\n \"privilegeId\": \"\"\n },\n \"providerId\": \"044c7480-f34b-47b1-af31-e498cf6897b4\",\n \"type\": \"privilegeUpdate\",\n \"updateType\": \"registration\",\n \"updatedValues\": {\n \"administratorSetStatus\": \"inactive\",\n \"dateOfExpiration\": \"1004-08-09\",\n \"licenseJurisdiction\": \"ok\",\n \"privilegeId\": \"\",\n \"dateOfRenewal\": \"2055-10-11\",\n \"dateOfIssuance\": \"1616-09-26\",\n \"dateOfUpdate\": \"\"\n }\n }\n ],\n \"adverseActions\": [\n {\n \"actionAgainst\": \"\",\n \"adverseActionId\": \"\",\n \"compact\": \"aslp\",\n \"creationDate\": \"2241-10-30\",\n \"dateOfUpdate\": \"\",\n \"effectiveStartDate\": \"2845-11-03\",\n \"jurisdiction\": \"wi\",\n \"licenseType\": \"\",\n \"licenseTypeAbbreviation\": \"\",\n \"providerId\": \"f0b4e3ed-56db-427c-ace5-98aac9331e52\",\n \"type\": \"adverseAction\",\n \"effectiveLiftDate\": \"2001-11-31\"\n },\n {\n \"actionAgainst\": \"\",\n \"adverseActionId\": \"\",\n \"compact\": \"octp\",\n \"creationDate\": \"1146-04-05\",\n \"dateOfUpdate\": \"\",\n \"effectiveStartDate\": \"2655-11-05\",\n \"jurisdiction\": \"ca\",\n \"licenseType\": \"\",\n \"licenseTypeAbbreviation\": \"\",\n \"providerId\": \"b8b0856a-2fb0-4358-8339-3e7725e3fc0d\",\n \"type\": \"adverseAction\",\n \"effectiveLiftDate\": \"2978-02-06\"\n }\n ]\n }\n ],\n \"npi\": \"0033723016\",\n \"suffix\": \"\",\n \"currentHomeJurisdiction\": \"unknown\",\n \"middleName\": \"\"\n}", "code": 200, "cookie": [], "header": [ @@ -5826,7 +5826,7 @@ "value": "application/json" } ], - "id": "e5ecbe28-b28c-4101-88b1-ed125434421d", + "id": "4ded65bf-e81e-49f4-9ee1-f73db864e31d", "name": "200 response", "originalRequest": { "body": {}, @@ -5874,7 +5874,7 @@ "item": [ { "event": [], - "id": "032bf11b-1f95-4282-b74d-b38ab478b7b1", + "id": "ac679b6a-0027-426a-b092-285f1701da09", "name": "/v1/public/compacts/:compact/providers/:providerId/jurisdiction/:jurisdiction/licenseType/:licenseType/history", "protocolProfileBehavior": { "disableBodyPruning": true @@ -5958,7 +5958,7 @@ "response": [ { "_postman_previewlanguage": "json", - "body": "{\n \"compact\": \"octp\",\n \"events\": [\n {\n \"createDate\": \"\",\n \"dateOfUpdate\": \"\",\n \"effectiveDate\": \"1680-11-27\",\n \"type\": \"privilegeUpdate\",\n \"updateType\": \"encumbrance\",\n \"note\": \"\"\n },\n {\n \"createDate\": \"\",\n \"dateOfUpdate\": \"\",\n \"effectiveDate\": \"2994-12-28\",\n \"type\": \"privilegeUpdate\",\n \"updateType\": \"other\",\n \"note\": \"\"\n }\n ],\n \"jurisdiction\": \"id\",\n \"licenseType\": \"audiologist\",\n \"privilegeId\": \"\",\n \"providerId\": \"583662f6-05d7-4dd9-af6c-ce0620242a3d\"\n}", + "body": "{\n \"compact\": \"aslp\",\n \"events\": [\n {\n \"createDate\": \"\",\n \"dateOfUpdate\": \"\",\n \"effectiveDate\": \"2221-06-30\",\n \"type\": \"privilegeUpdate\",\n \"updateType\": \"lifting_encumbrance\",\n \"note\": \"\",\n \"npdbCategories\": [\n \"\",\n \"\"\n ]\n },\n {\n \"createDate\": \"\",\n \"dateOfUpdate\": \"\",\n \"effectiveDate\": \"2677-12-30\",\n \"type\": \"privilegeUpdate\",\n \"updateType\": \"emailChange\",\n \"note\": \"\",\n \"npdbCategories\": [\n \"\",\n \"\"\n ]\n }\n ],\n \"jurisdiction\": \"ne\",\n \"licenseType\": \"occupational therapist\",\n \"privilegeId\": \"\",\n \"providerId\": \"2d4b50f1-d301-4b90-a1f3-1442018658aa\"\n}", "code": 200, "cookie": [], "header": [ @@ -5967,7 +5967,7 @@ "value": "application/json" } ], - "id": "15daa21a-2465-4664-9b37-c2a2ee30a065", + "id": "521274f8-765d-4140-8e10-8e0039a6761f", "name": "200 response", "originalRequest": { "body": {}, @@ -6038,7 +6038,7 @@ "item": [ { "event": [], - "id": "9132cd97-d1c9-436b-a96f-93add3043b0b", + "id": "ceccc434-acc0-4601-b451-80faf15f9b04", "name": "/v1/public/jurisdictions/live", "protocolProfileBehavior": { "disableBodyPruning": true @@ -6084,7 +6084,7 @@ "response": [ { "_postman_previewlanguage": "json", - "body": "{\n \"ad_9\": [\n \"wy\",\n \"mi\"\n ]\n}", + "body": "{\n \"labore1\": [\n \"wi\",\n \"pa\"\n ]\n}", "code": 200, "cookie": [], "header": [ @@ -6093,7 +6093,7 @@ "value": "application/json" } ], - "id": "9227d97e-c5ff-40a3-b8ee-2f8ea5f15360", + "id": "6fae83ca-d1c7-4fb1-825b-2aea568154bd", "name": "200 response", "originalRequest": { "body": {}, @@ -6149,7 +6149,7 @@ "item": [ { "event": [], - "id": "7ba27680-1914-4b91-888c-2f1b960d4065", + "id": "cb4b5f90-be10-4f47-a6a3-5758da6557a8", "name": "/v1/purchases/privileges", "protocolProfileBehavior": { "disableBodyPruning": true @@ -6163,7 +6163,7 @@ "language": "json" } }, - "raw": "{\n \"attestations\": [\n {\n \"attestationId\": \"\",\n \"version\": \"974\"\n },\n {\n \"attestationId\": \"\",\n \"version\": \"217\"\n }\n ],\n \"licenseType\": \"licensed professional counselor\",\n \"orderInformation\": {\n \"opaqueData\": {\n \"dataDescriptor\": \"\",\n \"dataValue\": \"\"\n }\n },\n \"selectedJurisdictions\": [\n \"mn\",\n \"dc\"\n ]\n}" + "raw": "{\n \"attestations\": [\n {\n \"attestationId\": \"\",\n \"version\": \"61504\"\n },\n {\n \"attestationId\": \"\",\n \"version\": \"8946531\"\n }\n ],\n \"licenseType\": \"licensed professional counselor\",\n \"orderInformation\": {\n \"opaqueData\": {\n \"dataDescriptor\": \"\",\n \"dataValue\": \"\"\n }\n },\n \"selectedJurisdictions\": [\n \"ma\",\n \"sc\"\n ]\n}" }, "description": {}, "header": [ @@ -6203,7 +6203,7 @@ "value": "application/json" } ], - "id": "30af5f95-06b7-4957-8dee-3f70c8d62e50", + "id": "d81abb7e-d7ee-4b30-8a4d-4c311aa1993b", "name": "200 response", "originalRequest": { "body": { @@ -6214,7 +6214,7 @@ "language": "json" } }, - "raw": "{\n \"attestations\": [\n {\n \"attestationId\": \"\",\n \"version\": \"974\"\n },\n {\n \"attestationId\": \"\",\n \"version\": \"217\"\n }\n ],\n \"licenseType\": \"licensed professional counselor\",\n \"orderInformation\": {\n \"opaqueData\": {\n \"dataDescriptor\": \"\",\n \"dataValue\": \"\"\n }\n },\n \"selectedJurisdictions\": [\n \"mn\",\n \"dc\"\n ]\n}" + "raw": "{\n \"attestations\": [\n {\n \"attestationId\": \"\",\n \"version\": \"61504\"\n },\n {\n \"attestationId\": \"\",\n \"version\": \"8946531\"\n }\n ],\n \"licenseType\": \"licensed professional counselor\",\n \"orderInformation\": {\n \"opaqueData\": {\n \"dataDescriptor\": \"\",\n \"dataValue\": \"\"\n }\n },\n \"selectedJurisdictions\": [\n \"ma\",\n \"sc\"\n ]\n}" }, "header": [ { @@ -6257,7 +6257,7 @@ "item": [ { "event": [], - "id": "4d2c35fa-d116-4e82-b750-764b0a724e52", + "id": "9b2c4774-060a-4da5-a730-8462df5758b0", "name": "/v1/purchases/privileges/options", "protocolProfileBehavior": { "disableBodyPruning": true @@ -6299,7 +6299,7 @@ "value": "application/json" } ], - "id": "2a975fc5-9ac9-4335-aa71-8e040242b2fa", + "id": "859cbff0-1502-4990-af54-3a11d893552e", "name": "200 response", "originalRequest": { "body": {}, @@ -6353,7 +6353,7 @@ "item": [ { "event": [], - "id": "35c026a5-cd78-43ad-b080-7da7b9adea31", + "id": "ff79c624-892c-4f8f-9398-b2ff3ebb5bc8", "name": "/v1/staff-users/me", "protocolProfileBehavior": { "disableBodyPruning": true @@ -6385,7 +6385,7 @@ "response": [ { "_postman_previewlanguage": "json", - "body": "{\n \"attributes\": {\n \"email\": \"\",\n \"familyName\": \"\",\n \"givenName\": \"\"\n },\n \"permissions\": {\n \"eiusmod_6cf\": {\n \"actions\": {\n \"readPrivate\": \"\",\n \"admin\": \"\",\n \"readSSN\": \"\"\n },\n \"jurisdictions\": {\n \"eu_ec\": {\n \"actions\": {\n \"readPrivate\": \"\",\n \"admin\": \"\",\n \"write\": \"\",\n \"readSSN\": \"\"\n }\n }\n }\n },\n \"adipisicing3\": {\n \"actions\": {\n \"readPrivate\": \"\",\n \"admin\": \"\",\n \"readSSN\": \"\"\n },\n \"jurisdictions\": {\n \"quis_a4b\": {\n \"actions\": {\n \"readPrivate\": \"\",\n \"admin\": \"\",\n \"write\": \"\",\n \"readSSN\": \"\"\n }\n }\n }\n },\n \"Ut_2\": {\n \"actions\": {\n \"readPrivate\": \"\",\n \"admin\": \"\",\n \"readSSN\": \"\"\n },\n \"jurisdictions\": {\n \"commodo_b03\": {\n \"actions\": {\n \"readPrivate\": \"\",\n \"admin\": \"\",\n \"write\": \"\",\n \"readSSN\": \"\"\n }\n },\n \"consequat_5b\": {\n \"actions\": {\n \"readPrivate\": \"\",\n \"admin\": \"\",\n \"write\": \"\",\n \"readSSN\": \"\"\n }\n }\n }\n }\n },\n \"status\": \"inactive\",\n \"userId\": \"\"\n}", + "body": "{\n \"attributes\": {\n \"email\": \"\",\n \"familyName\": \"\",\n \"givenName\": \"\"\n },\n \"permissions\": {\n \"occaecat55\": {\n \"actions\": {\n \"readPrivate\": \"\",\n \"admin\": \"\",\n \"readSSN\": \"\"\n },\n \"jurisdictions\": {\n \"enim_b\": {\n \"actions\": {\n \"readPrivate\": \"\",\n \"admin\": \"\",\n \"write\": \"\",\n \"readSSN\": \"\"\n }\n },\n \"nulla_e\": {\n \"actions\": {\n \"readPrivate\": \"\",\n \"admin\": \"\",\n \"write\": \"\",\n \"readSSN\": \"\"\n }\n },\n \"irure_233\": {\n \"actions\": {\n \"readPrivate\": \"\",\n \"admin\": \"\",\n \"write\": \"\",\n \"readSSN\": \"\"\n }\n }\n }\n }\n },\n \"status\": \"inactive\",\n \"userId\": \"\"\n}", "code": 200, "cookie": [], "header": [ @@ -6403,7 +6403,7 @@ "value": "" } ], - "id": "25763968-6e6e-4c41-8e8a-7c1ac49850b7", + "id": "64598cee-2b02-4c39-b990-c0aeea9adda9", "name": "200 response", "originalRequest": { "body": {}, @@ -6448,7 +6448,7 @@ "value": "application/json" } ], - "id": "c5a59cc0-89eb-40df-aacf-6dbd5383985c", + "id": "f66210b2-6b03-4052-8136-57411f9c43e9", "name": "404 response", "originalRequest": { "body": {}, @@ -6486,7 +6486,7 @@ }, { "event": [], - "id": "4023dbbc-063c-4ff6-ba74-0b6fe929bf6d", + "id": "41152571-9981-40ad-8caa-8b280edede91", "name": "/v1/staff-users/me", "protocolProfileBehavior": { "disableBodyPruning": true @@ -6531,7 +6531,7 @@ "response": [ { "_postman_previewlanguage": "json", - "body": "{\n \"attributes\": {\n \"email\": \"\",\n \"familyName\": \"\",\n \"givenName\": \"\"\n },\n \"permissions\": {\n \"eiusmod_6cf\": {\n \"actions\": {\n \"readPrivate\": \"\",\n \"admin\": \"\",\n \"readSSN\": \"\"\n },\n \"jurisdictions\": {\n \"eu_ec\": {\n \"actions\": {\n \"readPrivate\": \"\",\n \"admin\": \"\",\n \"write\": \"\",\n \"readSSN\": \"\"\n }\n }\n }\n },\n \"adipisicing3\": {\n \"actions\": {\n \"readPrivate\": \"\",\n \"admin\": \"\",\n \"readSSN\": \"\"\n },\n \"jurisdictions\": {\n \"quis_a4b\": {\n \"actions\": {\n \"readPrivate\": \"\",\n \"admin\": \"\",\n \"write\": \"\",\n \"readSSN\": \"\"\n }\n }\n }\n },\n \"Ut_2\": {\n \"actions\": {\n \"readPrivate\": \"\",\n \"admin\": \"\",\n \"readSSN\": \"\"\n },\n \"jurisdictions\": {\n \"commodo_b03\": {\n \"actions\": {\n \"readPrivate\": \"\",\n \"admin\": \"\",\n \"write\": \"\",\n \"readSSN\": \"\"\n }\n },\n \"consequat_5b\": {\n \"actions\": {\n \"readPrivate\": \"\",\n \"admin\": \"\",\n \"write\": \"\",\n \"readSSN\": \"\"\n }\n }\n }\n }\n },\n \"status\": \"inactive\",\n \"userId\": \"\"\n}", + "body": "{\n \"attributes\": {\n \"email\": \"\",\n \"familyName\": \"\",\n \"givenName\": \"\"\n },\n \"permissions\": {\n \"occaecat55\": {\n \"actions\": {\n \"readPrivate\": \"\",\n \"admin\": \"\",\n \"readSSN\": \"\"\n },\n \"jurisdictions\": {\n \"enim_b\": {\n \"actions\": {\n \"readPrivate\": \"\",\n \"admin\": \"\",\n \"write\": \"\",\n \"readSSN\": \"\"\n }\n },\n \"nulla_e\": {\n \"actions\": {\n \"readPrivate\": \"\",\n \"admin\": \"\",\n \"write\": \"\",\n \"readSSN\": \"\"\n }\n },\n \"irure_233\": {\n \"actions\": {\n \"readPrivate\": \"\",\n \"admin\": \"\",\n \"write\": \"\",\n \"readSSN\": \"\"\n }\n }\n }\n }\n },\n \"status\": \"inactive\",\n \"userId\": \"\"\n}", "code": 200, "cookie": [], "header": [ @@ -6549,7 +6549,7 @@ "value": "" } ], - "id": "a1595309-89a9-44ac-9232-e7b1564c59d7", + "id": "eb15d838-0df3-4e7e-86cc-a7f6faf45eca", "name": "200 response", "originalRequest": { "body": { @@ -6607,7 +6607,7 @@ "value": "application/json" } ], - "id": "d96b83a8-7383-4004-8e75-5f58f778e796", + "id": "99ce4832-2efe-4f86-a1c5-d3d7ad5d6a04", "name": "404 response", "originalRequest": { "body": { diff --git a/backend/compact-connect/docs/postman/postman-collection.json b/backend/compact-connect/docs/postman/postman-collection.json index 90ab6b2f7e..55a4f78afc 100644 --- a/backend/compact-connect/docs/postman/postman-collection.json +++ b/backend/compact-connect/docs/postman/postman-collection.json @@ -10,7 +10,7 @@ "type": "bearer" }, "info": { - "_postman_id": "2dd3c79b-7848-4980-8615-72b6b4c2b4b7", + "_postman_id": "fce8656b-8d54-49fe-9ca0-edaa3b31cf20", "description": { "content": "", "type": "text/plain" @@ -410,7 +410,7 @@ "item": [ { "event": [], - "id": "e4992458-08cd-4c8b-acbf-59af7b7e3a75", + "id": "cd9da060-3bb2-4257-becf-42477534cd24", "name": "/v1/compacts/:compact/jurisdictions/:jurisdiction/licenses", "protocolProfileBehavior": { "disableBodyPruning": true @@ -424,7 +424,7 @@ "language": "json" } }, - "raw": "[\n {\n \"compactEligibility\": \"ineligible\",\n \"dateOfBirth\": \"1607-11-17\",\n \"dateOfExpiration\": \"2885-05-26\",\n \"dateOfIssuance\": \"1146-03-30\",\n \"familyName\": \"\",\n \"givenName\": \"\",\n \"homeAddressCity\": \"\",\n \"homeAddressPostalCode\": \"\",\n \"homeAddressState\": \"\",\n \"homeAddressStreet1\": \"\",\n \"licenseStatus\": \"active\",\n \"licenseType\": \"licensed professional counselor\",\n \"ssn\": \"151-51-8414\",\n \"homeAddressStreet2\": \"\",\n \"npi\": \"7741340530\",\n \"suffix\": \"\",\n \"emailAddress\": \"\",\n \"phoneNumber\": \"+3849258700\",\n \"dateOfRenewal\": \"2882-02-09\",\n \"licenseNumber\": \"\",\n \"middleName\": \"\",\n \"licenseStatusName\": \"\"\n },\n {\n \"compactEligibility\": \"eligible\",\n \"dateOfBirth\": \"1479-12-06\",\n \"dateOfExpiration\": \"1919-12-10\",\n \"dateOfIssuance\": \"1300-04-29\",\n \"familyName\": \"\",\n \"givenName\": \"\",\n \"homeAddressCity\": \"\",\n \"homeAddressPostalCode\": \"\",\n \"homeAddressState\": \"\",\n \"homeAddressStreet1\": \"\",\n \"licenseStatus\": \"active\",\n \"licenseType\": \"licensed professional counselor\",\n \"ssn\": \"971-92-2380\",\n \"homeAddressStreet2\": \"\",\n \"npi\": \"6327468598\",\n \"suffix\": \"\",\n \"emailAddress\": \"\",\n \"phoneNumber\": \"+925079022127\",\n \"dateOfRenewal\": \"1269-03-19\",\n \"licenseNumber\": \"\",\n \"middleName\": \"\",\n \"licenseStatusName\": \"\"\n }\n]" + "raw": "[\n {\n \"compactEligibility\": \"eligible\",\n \"dateOfBirth\": \"1291-04-31\",\n \"dateOfExpiration\": \"1806-11-01\",\n \"dateOfIssuance\": \"1067-08-26\",\n \"familyName\": \"\",\n \"givenName\": \"\",\n \"homeAddressCity\": \"\",\n \"homeAddressPostalCode\": \"\",\n \"homeAddressState\": \"\",\n \"homeAddressStreet1\": \"\",\n \"licenseStatus\": \"inactive\",\n \"licenseType\": \"audiologist\",\n \"ssn\": \"211-74-1824\",\n \"homeAddressStreet2\": \"\",\n \"npi\": \"9018939273\",\n \"suffix\": \"\",\n \"emailAddress\": \"\",\n \"phoneNumber\": \"+18596105510\",\n \"dateOfRenewal\": \"1705-03-30\",\n \"licenseNumber\": \"\",\n \"middleName\": \"\",\n \"licenseStatusName\": \"\",\n \"previousSSN\": \"940-26-0304\"\n },\n {\n \"compactEligibility\": \"ineligible\",\n \"dateOfBirth\": \"2782-05-12\",\n \"dateOfExpiration\": \"2383-06-04\",\n \"dateOfIssuance\": \"2126-01-26\",\n \"familyName\": \"\",\n \"givenName\": \"\",\n \"homeAddressCity\": \"\",\n \"homeAddressPostalCode\": \"\",\n \"homeAddressState\": \"\",\n \"homeAddressStreet1\": \"\",\n \"licenseStatus\": \"active\",\n \"licenseType\": \"occupational therapy assistant\",\n \"ssn\": \"626-05-3418\",\n \"homeAddressStreet2\": \"\",\n \"npi\": \"9813073716\",\n \"suffix\": \"\",\n \"emailAddress\": \"\",\n \"phoneNumber\": \"+469140727689\",\n \"dateOfRenewal\": \"1688-08-06\",\n \"licenseNumber\": \"\",\n \"middleName\": \"\",\n \"licenseStatusName\": \"\",\n \"previousSSN\": \"974-52-9967\"\n }\n]" }, "description": {}, "header": [ @@ -488,7 +488,7 @@ "value": "application/json" } ], - "id": "a4a13d96-78a0-4ab6-8fb1-af82a1d3c500", + "id": "1c576ef8-19b5-40cc-94fb-e5862e8f0839", "name": "200 response", "originalRequest": { "body": { @@ -499,7 +499,7 @@ "language": "json" } }, - "raw": "[\n {\n \"compactEligibility\": \"ineligible\",\n \"dateOfBirth\": \"1607-11-17\",\n \"dateOfExpiration\": \"2885-05-26\",\n \"dateOfIssuance\": \"1146-03-30\",\n \"familyName\": \"\",\n \"givenName\": \"\",\n \"homeAddressCity\": \"\",\n \"homeAddressPostalCode\": \"\",\n \"homeAddressState\": \"\",\n \"homeAddressStreet1\": \"\",\n \"licenseStatus\": \"active\",\n \"licenseType\": \"licensed professional counselor\",\n \"ssn\": \"151-51-8414\",\n \"homeAddressStreet2\": \"\",\n \"npi\": \"7741340530\",\n \"suffix\": \"\",\n \"emailAddress\": \"\",\n \"phoneNumber\": \"+3849258700\",\n \"dateOfRenewal\": \"2882-02-09\",\n \"licenseNumber\": \"\",\n \"middleName\": \"\",\n \"licenseStatusName\": \"\"\n },\n {\n \"compactEligibility\": \"eligible\",\n \"dateOfBirth\": \"1479-12-06\",\n \"dateOfExpiration\": \"1919-12-10\",\n \"dateOfIssuance\": \"1300-04-29\",\n \"familyName\": \"\",\n \"givenName\": \"\",\n \"homeAddressCity\": \"\",\n \"homeAddressPostalCode\": \"\",\n \"homeAddressState\": \"\",\n \"homeAddressStreet1\": \"\",\n \"licenseStatus\": \"active\",\n \"licenseType\": \"licensed professional counselor\",\n \"ssn\": \"971-92-2380\",\n \"homeAddressStreet2\": \"\",\n \"npi\": \"6327468598\",\n \"suffix\": \"\",\n \"emailAddress\": \"\",\n \"phoneNumber\": \"+925079022127\",\n \"dateOfRenewal\": \"1269-03-19\",\n \"licenseNumber\": \"\",\n \"middleName\": \"\",\n \"licenseStatusName\": \"\"\n }\n]" + "raw": "[\n {\n \"compactEligibility\": \"eligible\",\n \"dateOfBirth\": \"1291-04-31\",\n \"dateOfExpiration\": \"1806-11-01\",\n \"dateOfIssuance\": \"1067-08-26\",\n \"familyName\": \"\",\n \"givenName\": \"\",\n \"homeAddressCity\": \"\",\n \"homeAddressPostalCode\": \"\",\n \"homeAddressState\": \"\",\n \"homeAddressStreet1\": \"\",\n \"licenseStatus\": \"inactive\",\n \"licenseType\": \"audiologist\",\n \"ssn\": \"211-74-1824\",\n \"homeAddressStreet2\": \"\",\n \"npi\": \"9018939273\",\n \"suffix\": \"\",\n \"emailAddress\": \"\",\n \"phoneNumber\": \"+18596105510\",\n \"dateOfRenewal\": \"1705-03-30\",\n \"licenseNumber\": \"\",\n \"middleName\": \"\",\n \"licenseStatusName\": \"\",\n \"previousSSN\": \"940-26-0304\"\n },\n {\n \"compactEligibility\": \"ineligible\",\n \"dateOfBirth\": \"2782-05-12\",\n \"dateOfExpiration\": \"2383-06-04\",\n \"dateOfIssuance\": \"2126-01-26\",\n \"familyName\": \"\",\n \"givenName\": \"\",\n \"homeAddressCity\": \"\",\n \"homeAddressPostalCode\": \"\",\n \"homeAddressState\": \"\",\n \"homeAddressStreet1\": \"\",\n \"licenseStatus\": \"active\",\n \"licenseType\": \"occupational therapy assistant\",\n \"ssn\": \"626-05-3418\",\n \"homeAddressStreet2\": \"\",\n \"npi\": \"9813073716\",\n \"suffix\": \"\",\n \"emailAddress\": \"\",\n \"phoneNumber\": \"+469140727689\",\n \"dateOfRenewal\": \"1688-08-06\",\n \"licenseNumber\": \"\",\n \"middleName\": \"\",\n \"licenseStatusName\": \"\",\n \"previousSSN\": \"974-52-9967\"\n }\n]" }, "header": [ { @@ -540,7 +540,7 @@ }, { "_postman_previewlanguage": "json", - "body": "{\n \"message\": \"\",\n \"errors\": {\n \"key_0\": {\n \"key_0\": [\n \"\",\n \"\"\n ],\n \"key_1\": [\n \"\",\n \"\"\n ]\n }\n }\n}", + "body": "{\n \"message\": \"\",\n \"errors\": {\n \"adipisicing_7b\": {\n \"voluptate_7\": [\n \"\",\n \"\"\n ],\n \"elit_d1\": [\n \"\",\n \"\"\n ],\n \"eu1\": [\n \"\",\n \"\"\n ]\n },\n \"ut_c8\": {\n \"pariatur_c07\": [\n \"\",\n \"\"\n ]\n }\n }\n}", "code": 400, "cookie": [], "header": [ @@ -549,7 +549,7 @@ "value": "application/json" } ], - "id": "a367f319-890c-4b07-82e6-f0159f7909a8", + "id": "d043e268-d5c7-49af-9ad4-a670eb52fcac", "name": "400 response", "originalRequest": { "body": { @@ -560,7 +560,7 @@ "language": "json" } }, - "raw": "[\n {\n \"compactEligibility\": \"ineligible\",\n \"dateOfBirth\": \"1607-11-17\",\n \"dateOfExpiration\": \"2885-05-26\",\n \"dateOfIssuance\": \"1146-03-30\",\n \"familyName\": \"\",\n \"givenName\": \"\",\n \"homeAddressCity\": \"\",\n \"homeAddressPostalCode\": \"\",\n \"homeAddressState\": \"\",\n \"homeAddressStreet1\": \"\",\n \"licenseStatus\": \"active\",\n \"licenseType\": \"licensed professional counselor\",\n \"ssn\": \"151-51-8414\",\n \"homeAddressStreet2\": \"\",\n \"npi\": \"7741340530\",\n \"suffix\": \"\",\n \"emailAddress\": \"\",\n \"phoneNumber\": \"+3849258700\",\n \"dateOfRenewal\": \"2882-02-09\",\n \"licenseNumber\": \"\",\n \"middleName\": \"\",\n \"licenseStatusName\": \"\"\n },\n {\n \"compactEligibility\": \"eligible\",\n \"dateOfBirth\": \"1479-12-06\",\n \"dateOfExpiration\": \"1919-12-10\",\n \"dateOfIssuance\": \"1300-04-29\",\n \"familyName\": \"\",\n \"givenName\": \"\",\n \"homeAddressCity\": \"\",\n \"homeAddressPostalCode\": \"\",\n \"homeAddressState\": \"\",\n \"homeAddressStreet1\": \"\",\n \"licenseStatus\": \"active\",\n \"licenseType\": \"licensed professional counselor\",\n \"ssn\": \"971-92-2380\",\n \"homeAddressStreet2\": \"\",\n \"npi\": \"6327468598\",\n \"suffix\": \"\",\n \"emailAddress\": \"\",\n \"phoneNumber\": \"+925079022127\",\n \"dateOfRenewal\": \"1269-03-19\",\n \"licenseNumber\": \"\",\n \"middleName\": \"\",\n \"licenseStatusName\": \"\"\n }\n]" + "raw": "[\n {\n \"compactEligibility\": \"eligible\",\n \"dateOfBirth\": \"1291-04-31\",\n \"dateOfExpiration\": \"1806-11-01\",\n \"dateOfIssuance\": \"1067-08-26\",\n \"familyName\": \"\",\n \"givenName\": \"\",\n \"homeAddressCity\": \"\",\n \"homeAddressPostalCode\": \"\",\n \"homeAddressState\": \"\",\n \"homeAddressStreet1\": \"\",\n \"licenseStatus\": \"inactive\",\n \"licenseType\": \"audiologist\",\n \"ssn\": \"211-74-1824\",\n \"homeAddressStreet2\": \"\",\n \"npi\": \"9018939273\",\n \"suffix\": \"\",\n \"emailAddress\": \"\",\n \"phoneNumber\": \"+18596105510\",\n \"dateOfRenewal\": \"1705-03-30\",\n \"licenseNumber\": \"\",\n \"middleName\": \"\",\n \"licenseStatusName\": \"\",\n \"previousSSN\": \"940-26-0304\"\n },\n {\n \"compactEligibility\": \"ineligible\",\n \"dateOfBirth\": \"2782-05-12\",\n \"dateOfExpiration\": \"2383-06-04\",\n \"dateOfIssuance\": \"2126-01-26\",\n \"familyName\": \"\",\n \"givenName\": \"\",\n \"homeAddressCity\": \"\",\n \"homeAddressPostalCode\": \"\",\n \"homeAddressState\": \"\",\n \"homeAddressStreet1\": \"\",\n \"licenseStatus\": \"active\",\n \"licenseType\": \"occupational therapy assistant\",\n \"ssn\": \"626-05-3418\",\n \"homeAddressStreet2\": \"\",\n \"npi\": \"9813073716\",\n \"suffix\": \"\",\n \"emailAddress\": \"\",\n \"phoneNumber\": \"+469140727689\",\n \"dateOfRenewal\": \"1688-08-06\",\n \"licenseNumber\": \"\",\n \"middleName\": \"\",\n \"licenseStatusName\": \"\",\n \"previousSSN\": \"974-52-9967\"\n }\n]" }, "header": [ { @@ -631,7 +631,7 @@ } } ], - "id": "17440cb5-a8c9-4e7b-8660-6588d8a9d001", + "id": "8556a6ce-a094-41be-9ba1-6ea40ae687ac", "name": "/v1/compacts/:compact/jurisdictions/:jurisdiction/licenses/bulk-upload", "protocolProfileBehavior": { "disableBodyPruning": true @@ -688,7 +688,7 @@ "response": [ { "_postman_previewlanguage": "json", - "body": "{\n \"upload\": {\n \"fields\": {\n \"key_0\": \"\",\n \"key_1\": \"\",\n \"key_2\": \"\"\n },\n \"url\": \"\"\n }\n}", + "body": "{\n \"upload\": {\n \"fields\": {\n \"adipisicingae4\": \"\",\n \"irure63\": \"\"\n },\n \"url\": \"\"\n }\n}", "code": 200, "cookie": [], "header": [ @@ -697,7 +697,7 @@ "value": "application/json" } ], - "id": "1e851c00-2aab-4ab4-9e5a-0a2a05bc1bf7", + "id": "a4aea037-07ad-4d81-84ee-e992dc44e88d", "name": "200 response", "originalRequest": { "body": {}, @@ -751,7 +751,7 @@ "item": [ { "event": [], - "id": "7aff71f6-348b-440e-8fbc-bae6dff7732b", + "id": "d51ebf8e-7e59-4385-ae1b-990d257c926f", "name": "/v1/compacts/:compact/jurisdictions/:jurisdiction/providers/query", "protocolProfileBehavior": { "disableBodyPruning": true @@ -765,7 +765,7 @@ "language": "json" } }, - "raw": "{\n \"query\": {\n \"endDateTime\": \"2768-12-31T21:50:21Z\",\n \"startDateTime\": \"1344-03-17T23:04:37Z\"\n },\n \"pagination\": {\n \"lastKey\": \"\",\n \"pageSize\": \"\"\n },\n \"sorting\": {\n \"direction\": \"ascending\"\n }\n}" + "raw": "{\n \"query\": {\n \"endDateTime\": \"2976-04-06T13:23:24.936Z\",\n \"startDateTime\": \"2894-10-29T16:30:15Z\"\n },\n \"pagination\": {\n \"lastKey\": \"\",\n \"pageSize\": \"\"\n },\n \"sorting\": {\n \"direction\": \"ascending\"\n }\n}" }, "description": {}, "header": [ @@ -821,7 +821,7 @@ "response": [ { "_postman_previewlanguage": "json", - "body": "{\n \"pagination\": {\n \"prevLastKey\": {},\n \"lastKey\": {},\n \"pageSize\": \"\"\n },\n \"providers\": [\n {\n \"birthMonthDay\": \"05-36\",\n \"compact\": \"octp\",\n \"compactEligibility\": \"ineligible\",\n \"dateOfExpiration\": \"1378-07-14\",\n \"dateOfUpdate\": \"1918-09-24\",\n \"familyName\": \"\",\n \"givenName\": \"\",\n \"jurisdictionUploadedCompactEligibility\": \"ineligible\",\n \"jurisdictionUploadedLicenseStatus\": \"inactive\",\n \"licenseJurisdiction\": \"nh\",\n \"licenseStatus\": \"active\",\n \"privilegeJurisdictions\": [\n \"nd\",\n \"ma\"\n ],\n \"providerId\": \"1e754c66-bcb7-48d9-8b0b-12a108fb94fd\",\n \"type\": \"provider\",\n \"npi\": \"8783122207\",\n \"dateOfBirth\": \"2750-12-30\",\n \"suffix\": \"\",\n \"ssnLastFour\": \"5085\",\n \"middleName\": \"\",\n \"compactConnectRegisteredEmailAddress\": \"\"\n },\n {\n \"birthMonthDay\": \"19-02\",\n \"compact\": \"coun\",\n \"compactEligibility\": \"ineligible\",\n \"dateOfExpiration\": \"1439-02-11\",\n \"dateOfUpdate\": \"1062-09-25\",\n \"familyName\": \"\",\n \"givenName\": \"\",\n \"jurisdictionUploadedCompactEligibility\": \"eligible\",\n \"jurisdictionUploadedLicenseStatus\": \"inactive\",\n \"licenseJurisdiction\": \"mi\",\n \"licenseStatus\": \"active\",\n \"privilegeJurisdictions\": [\n \"sd\",\n \"ky\"\n ],\n \"providerId\": \"379ed99f-db30-41b5-88e3-369a153f1716\",\n \"type\": \"provider\",\n \"npi\": \"0274556733\",\n \"dateOfBirth\": \"1391-10-06\",\n \"suffix\": \"\",\n \"ssnLastFour\": \"8798\",\n \"middleName\": \"\",\n \"compactConnectRegisteredEmailAddress\": \"\"\n }\n ],\n \"sorting\": {\n \"direction\": \"ascending\"\n }\n}", + "body": "{\n \"pagination\": {\n \"prevLastKey\": {},\n \"lastKey\": {},\n \"pageSize\": \"\"\n },\n \"providers\": [\n {\n \"birthMonthDay\": \"18-26\",\n \"compact\": \"coun\",\n \"compactEligibility\": \"eligible\",\n \"dateOfExpiration\": \"1406-11-30\",\n \"dateOfUpdate\": \"2326-01-01\",\n \"familyName\": \"\",\n \"givenName\": \"\",\n \"jurisdictionUploadedCompactEligibility\": \"eligible\",\n \"jurisdictionUploadedLicenseStatus\": \"active\",\n \"licenseJurisdiction\": \"dc\",\n \"licenseStatus\": \"active\",\n \"privilegeJurisdictions\": [\n \"id\",\n \"dc\"\n ],\n \"providerId\": \"106b2ba4-3edc-48db-9b6a-0bcc5cdb6539\",\n \"type\": \"provider\",\n \"npi\": \"3973260914\",\n \"dateOfBirth\": \"2418-11-07\",\n \"suffix\": \"\",\n \"ssnLastFour\": \"9009\",\n \"middleName\": \"\",\n \"compactConnectRegisteredEmailAddress\": \"\"\n },\n {\n \"birthMonthDay\": \"18-34\",\n \"compact\": \"octp\",\n \"compactEligibility\": \"eligible\",\n \"dateOfExpiration\": \"2925-11-30\",\n \"dateOfUpdate\": \"2074-03-16\",\n \"familyName\": \"\",\n \"givenName\": \"\",\n \"jurisdictionUploadedCompactEligibility\": \"eligible\",\n \"jurisdictionUploadedLicenseStatus\": \"active\",\n \"licenseJurisdiction\": \"mo\",\n \"licenseStatus\": \"active\",\n \"privilegeJurisdictions\": [\n \"mi\",\n \"ut\"\n ],\n \"providerId\": \"3e030fce-26f9-4803-a476-ed9c0543699e\",\n \"type\": \"provider\",\n \"npi\": \"5517513279\",\n \"dateOfBirth\": \"2327-07-27\",\n \"suffix\": \"\",\n \"ssnLastFour\": \"3050\",\n \"middleName\": \"\",\n \"compactConnectRegisteredEmailAddress\": \"\"\n }\n ],\n \"sorting\": {\n \"direction\": \"descending\"\n }\n}", "code": 200, "cookie": [], "header": [ @@ -830,7 +830,7 @@ "value": "application/json" } ], - "id": "614232ac-e418-4618-a5c8-acaf9aeef875", + "id": "fa818531-5e6a-4f7e-81da-2e1dc3c7f5fe", "name": "200 response", "originalRequest": { "body": { @@ -841,7 +841,7 @@ "language": "json" } }, - "raw": "{\n \"query\": {\n \"endDateTime\": \"2768-12-31T21:50:21Z\",\n \"startDateTime\": \"1344-03-17T23:04:37Z\"\n },\n \"pagination\": {\n \"lastKey\": \"\",\n \"pageSize\": \"\"\n },\n \"sorting\": {\n \"direction\": \"ascending\"\n }\n}" + "raw": "{\n \"query\": {\n \"endDateTime\": \"2976-04-06T13:23:24.936Z\",\n \"startDateTime\": \"2894-10-29T16:30:15Z\"\n },\n \"pagination\": {\n \"lastKey\": \"\",\n \"pageSize\": \"\"\n },\n \"sorting\": {\n \"direction\": \"ascending\"\n }\n}" }, "header": [ { @@ -891,7 +891,7 @@ "item": [ { "event": [], - "id": "a454e304-f168-451f-8def-24a377fa6f44", + "id": "6ed01ff6-3b60-47ae-ba04-d5d843c833fd", "name": "/v1/compacts/:compact/jurisdictions/:jurisdiction/providers/:providerId", "protocolProfileBehavior": { "disableBodyPruning": true @@ -958,7 +958,7 @@ "response": [ { "_postman_previewlanguage": "json", - "body": "{\n \"privileges\": [\n {\n \"compact\": \"aslp\",\n \"compactEligibility\": \"eligible\",\n \"dateOfExpiration\": \"1744-10-04\",\n \"dateOfIssuance\": \"2547-11-19\",\n \"dateOfRenewal\": \"2433-11-03\",\n \"dateOfUpdate\": \"1383-11-12\",\n \"familyName\": \"\",\n \"givenName\": \"\",\n \"jurisdiction\": \"ct\",\n \"licenseJurisdiction\": \"nj\",\n \"licenseStatus\": \"inactive\",\n \"licenseType\": \"licensed professional counselor\",\n \"privilegeId\": \"\",\n \"providerId\": \"339453dd-5ddf-4592-bf48-0491ee1dd127\",\n \"status\": \"inactive\",\n \"type\": \"statePrivilege\",\n \"homeAddressStreet2\": \"\",\n \"homeAddressStreet1\": \"\",\n \"suffix\": \"\",\n \"emailAddress\": \"\",\n \"homeAddressState\": \"\",\n \"homeAddressCity\": \"\",\n \"licenseNumber\": \"\",\n \"compactConnectRegisteredEmailAddress\": \"\",\n \"npi\": \"1692585889\",\n \"homeAddressPostalCode\": \"\",\n \"dateOfBirth\": \"1741-11-30\",\n \"ssnLastFour\": \"6801\",\n \"phoneNumber\": \"+7617633940581\",\n \"middleName\": \"\",\n \"licenseStatusName\": \"\"\n },\n {\n \"compact\": \"octp\",\n \"compactEligibility\": \"ineligible\",\n \"dateOfExpiration\": \"2460-10-30\",\n \"dateOfIssuance\": \"2105-02-30\",\n \"dateOfRenewal\": \"1126-02-27\",\n \"dateOfUpdate\": \"2918-08-30\",\n \"familyName\": \"\",\n \"givenName\": \"\",\n \"jurisdiction\": \"ct\",\n \"licenseJurisdiction\": \"ca\",\n \"licenseStatus\": \"active\",\n \"licenseType\": \"speech-language pathologist\",\n \"privilegeId\": \"\",\n \"providerId\": \"e943dcb6-630b-4510-bba5-e6fb1bf7b688\",\n \"status\": \"inactive\",\n \"type\": \"statePrivilege\",\n \"homeAddressStreet2\": \"\",\n \"homeAddressStreet1\": \"\",\n \"suffix\": \"\",\n \"emailAddress\": \"\",\n \"homeAddressState\": \"\",\n \"homeAddressCity\": \"\",\n \"licenseNumber\": \"\",\n \"compactConnectRegisteredEmailAddress\": \"\",\n \"npi\": \"3740829736\",\n \"homeAddressPostalCode\": \"\",\n \"dateOfBirth\": \"1879-06-31\",\n \"ssnLastFour\": \"8866\",\n \"phoneNumber\": \"+59156261550\",\n \"middleName\": \"\",\n \"licenseStatusName\": \"\"\n }\n ],\n \"providerUIUrl\": \"\"\n}", + "body": "{\n \"privileges\": [\n {\n \"compact\": \"coun\",\n \"compactEligibility\": \"ineligible\",\n \"dateOfExpiration\": \"2460-12-31\",\n \"dateOfIssuance\": \"1749-04-30\",\n \"dateOfRenewal\": \"1594-10-21\",\n \"dateOfUpdate\": \"1053-12-11\",\n \"familyName\": \"\",\n \"givenName\": \"\",\n \"jurisdiction\": \"dc\",\n \"licenseJurisdiction\": \"de\",\n \"licenseStatus\": \"active\",\n \"licenseType\": \"audiologist\",\n \"privilegeId\": \"\",\n \"providerId\": \"89970d09-2400-458a-b238-592a9d644eb7\",\n \"status\": \"active\",\n \"type\": \"statePrivilege\",\n \"homeAddressStreet2\": \"\",\n \"homeAddressStreet1\": \"\",\n \"suffix\": \"\",\n \"emailAddress\": \"\",\n \"homeAddressState\": \"\",\n \"homeAddressCity\": \"\",\n \"licenseNumber\": \"\",\n \"compactConnectRegisteredEmailAddress\": \"\",\n \"npi\": \"6862417665\",\n \"homeAddressPostalCode\": \"\",\n \"dateOfBirth\": \"1952-03-31\",\n \"ssnLastFour\": \"8875\",\n \"phoneNumber\": \"+458209558282\",\n \"middleName\": \"\",\n \"licenseStatusName\": \"\"\n },\n {\n \"compact\": \"aslp\",\n \"compactEligibility\": \"eligible\",\n \"dateOfExpiration\": \"2274-11-30\",\n \"dateOfIssuance\": \"2473-12-30\",\n \"dateOfRenewal\": \"2644-09-08\",\n \"dateOfUpdate\": \"2718-05-15\",\n \"familyName\": \"\",\n \"givenName\": \"\",\n \"jurisdiction\": \"ms\",\n \"licenseJurisdiction\": \"ny\",\n \"licenseStatus\": \"active\",\n \"licenseType\": \"licensed professional counselor\",\n \"privilegeId\": \"\",\n \"providerId\": \"4f11822f-8f0c-40e5-98a1-375557fd86f5\",\n \"status\": \"inactive\",\n \"type\": \"statePrivilege\",\n \"homeAddressStreet2\": \"\",\n \"homeAddressStreet1\": \"\",\n \"suffix\": \"\",\n \"emailAddress\": \"\",\n \"homeAddressState\": \"\",\n \"homeAddressCity\": \"\",\n \"licenseNumber\": \"\",\n \"compactConnectRegisteredEmailAddress\": \"\",\n \"npi\": \"1910067143\",\n \"homeAddressPostalCode\": \"\",\n \"dateOfBirth\": \"2004-11-25\",\n \"ssnLastFour\": \"9077\",\n \"phoneNumber\": \"+1782613788\",\n \"middleName\": \"\",\n \"licenseStatusName\": \"\"\n }\n ],\n \"providerUIUrl\": \"\"\n}", "code": 200, "cookie": [], "header": [ @@ -967,7 +967,7 @@ "value": "application/json" } ], - "id": "1ad41a6a-7de7-4bcc-8979-407410439cf2", + "id": "c0d99e8c-399f-4923-b282-90d71746dba2", "name": "200 response", "originalRequest": { "body": {}, diff --git a/backend/compact-connect/docs/search-internal/api-specification/latest-oas30.json b/backend/compact-connect/docs/search-internal/api-specification/latest-oas30.json index c1c006228a..ce4438cbd7 100644 --- a/backend/compact-connect/docs/search-internal/api-specification/latest-oas30.json +++ b/backend/compact-connect/docs/search-internal/api-specification/latest-oas30.json @@ -2,17 +2,81 @@ "openapi": "3.0.1", "info": { "title": "SearchApi", - "version": "2025-12-02T19:49:45Z" + "version": "2026-04-06T22:57:43Z" }, "servers": [ { - "url": "https://search.beta.compactconnect.org" + "url": "https://search.beta.compactconnect.org", + "x-amazon-apigateway-endpoint-configuration": { + "disableExecuteApiEndpoint": true + } } ], "paths": { + "/v1/compacts/{compact}/privileges/export": { + "post": { + "parameters": [ + { + "name": "Authorization", + "in": "header", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "compact", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TestSSearcRYf5G4aSTDXd" + } + } + }, + "required": true + }, + "responses": { + "200": { + "description": "200 response", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TestSSearcGlPW7cNyzeqp" + } + } + } + } + }, + "security": [ + { + "TestBackendPipelineStackTestSearchAPIStackSearchApiStaffUsersPoolAuthorizer39D6272D": [ + "aslp/readGeneral", + "octp/readGeneral", + "coun/readGeneral" + ] + } + ] + } + }, "/v1/compacts/{compact}/providers/search": { "post": { "parameters": [ + { + "name": "Authorization", + "in": "header", + "required": true, + "schema": { + "type": "string" + } + }, { "name": "compact", "in": "path", @@ -26,7 +90,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/SandboSearctZ4sfzliddmr" + "$ref": "#/components/schemas/TestSSearcu4mL449iif5D" } } }, @@ -38,18 +102,62 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/SandboSearcRcmFGOzNZ5TZ" + "$ref": "#/components/schemas/TestSSearcIjL4O2X3Q6NH" } } } } - } + }, + "security": [ + { + "TestBackendPipelineStackTestSearchAPIStackSearchApiStaffUsersPoolAuthorizer39D6272D": [ + "aslp/readGeneral", + "octp/readGeneral", + "coun/readGeneral" + ] + } + ] } } }, "components": { "schemas": { - "SandboSearcRcmFGOzNZ5TZ": { + "TestSSearcu4mL449iif5D": { + "required": [ + "query" + ], + "type": "object", + "properties": { + "search_after": { + "type": "array", + "description": "Sort values from the last hit of the previous page for cursor-based pagination" + }, + "size": { + "maximum": 100, + "minimum": 1, + "type": "integer", + "description": "Number of results to return" + }, + "query": { + "type": "object", + "description": "The OpenSearch query body" + }, + "from": { + "minimum": 0, + "type": "integer", + "description": "Starting document offset for pagination" + }, + "sort": { + "type": "array", + "description": "Sort order for results (required for search_after pagination)", + "items": { + "type": "object" + } + } + }, + "additionalProperties": false + }, + "TestSSearcIjL4O2X3Q6NH": { "required": [ "providers", "total" @@ -1399,41 +1507,39 @@ } } }, - "SandboSearctZ4sfzliddmr": { + "TestSSearcGlPW7cNyzeqp": { + "required": [ + "fileUrl" + ], + "type": "object", + "properties": { + "fileUrl": { + "type": "string", + "description": "Presigned URL to download the CSV file containing the export results" + } + } + }, + "TestSSearcRYf5G4aSTDXd": { "required": [ "query" ], "type": "object", "properties": { - "search_after": { - "type": "array", - "description": "Sort values from the last hit of the previous page for cursor-based pagination" - }, - "size": { - "maximum": 100, - "minimum": 1, - "type": "integer", - "description": "Number of results to return" - }, "query": { "type": "object", "description": "The OpenSearch query body" - }, - "from": { - "minimum": 0, - "type": "integer", - "description": "Starting document offset for pagination" - }, - "sort": { - "type": "array", - "description": "Sort order for results (required for search_after pagination)", - "items": { - "type": "object" - } } }, "additionalProperties": false } + }, + "securitySchemes": { + "TestBackendPipelineStackTestSearchAPIStackSearchApiStaffUsersPoolAuthorizer39D6272D": { + "type": "apiKey", + "name": "Authorization", + "in": "header", + "x-amazon-apigateway-authtype": "cognito_user_pools" + } } }, "x-amazon-apigateway-security-policy": "TLS_1_0" From d7f72dea3edeb5e4d414b630203b6d03b2750f3d Mon Sep 17 00:00:00 2001 From: Landon Shumway Date: Tue, 14 Jul 2026 15:49:34 -0700 Subject: [PATCH 26/41] Increase lambda timeouts to account for migration API calls --- backend/compact-connect/stacks/ingest_stack.py | 9 ++++++--- .../compact-connect/stacks/persistent_stack/ssn_table.py | 9 ++++++--- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/backend/compact-connect/stacks/ingest_stack.py b/backend/compact-connect/stacks/ingest_stack.py index 51c5cc6e0f..4c8352513a 100644 --- a/backend/compact-connect/stacks/ingest_stack.py +++ b/backend/compact-connect/stacks/ingest_stack.py @@ -47,7 +47,7 @@ def _add_v1_ingest_chain( lambda_dir='provider-data-v1', index=os.path.join('handlers', 'ingest.py'), handler='ingest_license_message', - timeout=Duration.minutes(1), + timeout=Duration.minutes(5), environment={ 'EVENT_BUS_NAME': data_event_bus.event_bus_name, 'PROVIDER_TABLE_NAME': persistent_stack.provider_table.table_name, @@ -101,9 +101,12 @@ def _add_v1_ingest_chain( self, 'V1Ingest', process_function=ingest_handler, - visibility_timeout=Duration.minutes(5), + # SQS visibility timeout is set to 4x the function timeout, + # so a message stays invisible long enough to cover the full batch's processing before it can be + # redelivered. See https://docs.aws.amazon.com/lambda/latest/dg/services-sqs-configure.html + visibility_timeout=Duration.minutes(20), retention_period=Duration.hours(12), - max_batching_window=Duration.minutes(5), + max_batching_window=Duration.minutes(1), max_receive_count=3, batch_size=50, encryption_key=persistent_stack.shared_encryption_key, diff --git a/backend/compact-connect/stacks/persistent_stack/ssn_table.py b/backend/compact-connect/stacks/persistent_stack/ssn_table.py index 8e1dcd12f4..c4c7d9cace 100644 --- a/backend/compact-connect/stacks/persistent_stack/ssn_table.py +++ b/backend/compact-connect/stacks/persistent_stack/ssn_table.py @@ -457,7 +457,7 @@ def _setup_license_preprocessor_queue(self, data_event_bus: EventBus, alarm_topi index=os.path.join('handlers', 'ingest.py'), handler='preprocess_license_ingest', role=self.ingest_role, - timeout=Duration.minutes(1), + timeout=Duration.minutes(2), environment={ 'EVENT_BUS_NAME': data_event_bus.event_bus_name, 'SSN_TABLE_NAME': self.table_name, @@ -487,9 +487,12 @@ def _setup_license_preprocessor_queue(self, data_event_bus: EventBus, alarm_topi self, 'LicenseQueuePreprocessor', process_function=preprocess_handler, - visibility_timeout=Duration.minutes(5), + # SQS visibility timeout is set to 4x the function timeout, + # so a message stays invisible long enough to cover the full batch's processing before it can be + # redelivered. See https://docs.aws.amazon.com/lambda/latest/dg/services-sqs-configure.html + visibility_timeout=Duration.minutes(8), retention_period=Duration.hours(12), - max_batching_window=Duration.minutes(5), + max_batching_window=Duration.minutes(1), max_receive_count=3, batch_size=50, # Use the SSN key for encryption to protect sensitive data From 88ac5938c977f91da3e16956c11f7fea49d0815b Mon Sep 17 00:00:00 2001 From: Landon Shumway Date: Tue, 14 Jul 2026 16:18:57 -0700 Subject: [PATCH 27/41] Move S3 objects before DynamoDB transactions for replayability --- .../cc_common/data_model/data_client.py | 59 +++++++++++++++++- .../lambdas/python/common/tests/__init__.py | 1 + .../python/common/tests/function/__init__.py | 7 +++ .../provider-data-v1/handlers/ingest.py | 60 ++----------------- 4 files changed, 70 insertions(+), 57 deletions(-) diff --git a/backend/compact-connect/lambdas/python/common/cc_common/data_model/data_client.py b/backend/compact-connect/lambdas/python/common/cc_common/data_model/data_client.py index 80da80fede..a320d72535 100644 --- a/backend/compact-connect/lambdas/python/common/cc_common/data_model/data_client.py +++ b/backend/compact-connect/lambdas/python/common/cc_common/data_model/data_client.py @@ -2880,9 +2880,10 @@ def migrate_provider_for_ssn_correction( - Full migration (sole license): the person-level records (military affiliations, provider update history) are moved to the new provider id as well, with military document keys re-pointed at the new provider id's keyspace, and the old provider's partition, including its top-level provider record, is - deleted. The caller is responsible for moving the practitioner's S3 documents (by listing the old - provider id's keyspace directly, not by relying on any single record type's tracked keys), deleting - the old Cognito user, and notifying the practitioner. + deleted. The practitioner's S3 documents are moved here as well (by listing the old provider id's + keyspace directly, not by relying on any single record type's tracked keys), before the DynamoDB + migration commits so the move is replay-safe. The caller is responsible for deleting the old Cognito + user and notifying the practitioner. - Partial (other licenses remain): the old provider keeps its person-level records and its top-level record is repopulated from its remaining licenses. The old provider id remains a valid (partial) practitioner until its remaining licenses are also corrected. @@ -3049,6 +3050,18 @@ def migrate_provider_for_ssn_correction( ) final_transaction_items.append(self._build_delete_transaction_item(target_license_key)) + # Move the practitioner's S3 documents to the new provider id's keyspace before committing the DynamoDB + # migration. Doing this before the commit (the point at which the idempotency guard flips, since the + # target license leaves the old partition) keeps the move replay-safe: a crash before the commit leaves + # the old provider intact so a retry re-detects the migration and re-runs the move (already-moved + # objects are a no-op), and a crash after the commit means the move already completed. + if full_migration: + self._move_provider_documents_to_new_keyspace( + compact=compact, + previous_provider_id=previous_provider_id, + new_provider_id=new_provider_id, + ) + all_transaction_items = [ *create_transaction_items, *delete_transaction_items, @@ -3150,6 +3163,46 @@ def _log_ssn_migration_transaction_items(phase: str, transaction_items: list[dic deleting_items={pk: sorted(sks) for pk, sks in deleted_sks_by_pk.items()}, ) + def _move_provider_documents_to_new_keyspace( + self, *, compact: str, previous_provider_id: str, new_provider_id: str + ): + """Move every object under the old provider id's S3 keyspace to the new provider id's. + + Rather than relying on any single record type's tracked document keys, this lists everything under the + old provider's keyspace prefix (`compact/{compact}/provider/{provider_id}/`) and moves it, changing only + the provider id segment of each key. This picks up every document type a provider might have uploaded, + including ones this migration logic has no other knowledge of. + + Runs before the DynamoDB migration commits. Best-effort per object: a copy/delete failure is logged and + skipped rather than failing the migration, and re-running against an already-moved key is a no-op (the + source object is simply absent). + """ + old_prefix = f'compact/{compact}/provider/{previous_provider_id}/' + new_prefix = f'compact/{compact}/provider/{new_provider_id}/' + + paginator = self.config.s3_client.get_paginator('list_objects_v2') + for page in paginator.paginate(Bucket=self.config.provider_user_bucket_name, Prefix=old_prefix): + for s3_object in page.get('Contents', []): + old_key = s3_object['Key'] + new_key = new_prefix + old_key[len(old_prefix) :] + self._move_s3_object(old_key=old_key, new_key=new_key) + + def _move_s3_object(self, *, old_key: str, new_key: str): + try: + self.config.s3_client.copy_object( + Bucket=self.config.provider_user_bucket_name, + CopySource={'Bucket': self.config.provider_user_bucket_name, 'Key': old_key}, + Key=new_key, + ) + self.config.s3_client.delete_object(Bucket=self.config.provider_user_bucket_name, Key=old_key) + except ClientError as e: + logger.error( + 'Failed to move provider document to the new keyspace', + old_key=old_key, + new_key=new_key, + error=str(e), + ) + def _build_put_transaction_item(self, record: CCDataClass, condition: dict | None = None) -> dict: return { 'Put': { diff --git a/backend/compact-connect/lambdas/python/common/tests/__init__.py b/backend/compact-connect/lambdas/python/common/tests/__init__.py index 01c1fdd22c..bf8dfb178c 100644 --- a/backend/compact-connect/lambdas/python/common/tests/__init__.py +++ b/backend/compact-connect/lambdas/python/common/tests/__init__.py @@ -17,6 +17,7 @@ def setUpClass(cls): 'ALLOWED_ORIGINS': '["https://example.org", "http://localhost:1234"]', 'AWS_DEFAULT_REGION': 'us-east-1', 'BULK_BUCKET_NAME': 'cc-license-data-bulk-bucket', + 'PROVIDER_USER_BUCKET_NAME': 'provider-users-bucket', 'EVENT_BUS_NAME': 'license-data-events', 'EVENT_STATE_TABLE_NAME': 'event-state-table', 'PROVIDER_TABLE_NAME': 'provider-table', diff --git a/backend/compact-connect/lambdas/python/common/tests/function/__init__.py b/backend/compact-connect/lambdas/python/common/tests/function/__init__.py index d2cc53f859..3939fa5f92 100644 --- a/backend/compact-connect/lambdas/python/common/tests/function/__init__.py +++ b/backend/compact-connect/lambdas/python/common/tests/function/__init__.py @@ -44,6 +44,7 @@ def build_resources(self): self.create_license_preprocessing_queue() self.create_rate_limiting_table() self.create_event_state_table() + self.create_provider_users_bucket() # Adding a waiter allows for testing against an actual AWS account, if needed waiter = self._compact_configuration_table.meta.client.get_waiter('table_exists') @@ -204,6 +205,8 @@ def create_license_preprocessing_queue(self): os.environ['LICENSE_PREPROCESSING_QUEUE_URL'] = self._license_preprocessing_queue.url def delete_resources(self): + self._provider_user_bucket.objects.delete() + self._provider_user_bucket.delete() self._compact_configuration_table.delete() self._provider_table.delete() self._ssn_table.delete() @@ -367,6 +370,10 @@ def _create_cognito_user(self, *, email: str): def _create_write_permissions(jurisdiction: str): return {'actions': {'read'}, 'jurisdictions': {jurisdiction: {'write'}}} + def create_provider_users_bucket(self): + """Create the provider users S3 bucket, used by the SSN-correction migration to move documents.""" + self._provider_user_bucket = boto3.resource('s3').create_bucket(Bucket=os.environ['PROVIDER_USER_BUCKET_NAME']) + def create_rate_limiting_table(self): """Create the rate limiting table for testing.""" self._rate_limiting_table = boto3.resource('dynamodb').create_table( diff --git a/backend/compact-connect/lambdas/python/provider-data-v1/handlers/ingest.py b/backend/compact-connect/lambdas/python/provider-data-v1/handlers/ingest.py index dbb2d99473..f8f5454d78 100644 --- a/backend/compact-connect/lambdas/python/provider-data-v1/handlers/ingest.py +++ b/backend/compact-connect/lambdas/python/provider-data-v1/handlers/ingest.py @@ -2,7 +2,6 @@ from copy import deepcopy from boto3.dynamodb.types import TypeSerializer -from botocore.exceptions import ClientError from cc_common.config import config, logger from cc_common.data_model.provider_record_util import ProviderRecordType, ProviderRecordUtility from cc_common.data_model.schema import LicenseRecordSchema @@ -384,10 +383,9 @@ def _perform_ssn_correction_migration( """ Orchestrate the migration of a practitioner's records after a state corrected the SSN on a license upload. - The DynamoDB migration runs first; on a full migration the S3 document move, Cognito user deletion, and - re-registration email follow, each idempotent so an SQS retry of a partially-completed migration converges. - A concurrency conflict inside the migration raises, letting SQS redeliver the message after the visibility - timeout. + The data client performs the DynamoDB migration and the S3 document move together; on a full migration the + old Cognito user deletion and re-registration email follow here. A concurrency conflict inside the + migration raises, letting SQS redeliver the message after the visibility timeout. """ logger.info('Performing SSN correction migration', previous_provider_id=previous_provider_id) @@ -403,56 +401,10 @@ def _perform_ssn_correction_migration( logger.info('No records to migrate for previous provider id; proceeding with normal ingest') return - if result.full_migration: - _move_provider_documents_to_new_keyspace( + if result.full_migration and result.old_provider_registered_email is not None: + _delete_old_cognito_user_and_send_reregistration_email( compact=compact, - previous_provider_id=previous_provider_id, - new_provider_id=new_provider_id, - ) - if result.old_provider_registered_email is not None: - _delete_old_cognito_user_and_send_reregistration_email( - compact=compact, - old_registered_email=result.old_provider_registered_email, - ) - - -def _move_provider_documents_to_new_keyspace(*, compact: str, previous_provider_id: str, new_provider_id: str): - """Move every object under the old provider id's S3 keyspace to the new provider id's. - - Rather than relying on any single record type's tracked document keys, this lists everything under the - old provider's keyspace prefix (`compact/{compact}/provider/{provider_id}/`) and moves it, changing only - the provider id segment of each key. This picks up every document type a provider might have uploaded, - including ones this migration logic has no other knowledge of. - - Runs after the DynamoDB migration (whose migrated militaryAffiliation records already reference the new - keys). Best-effort per object: a copy/delete failure is logged and skipped rather than failing the - migration, and re-running against an already-moved key is a no-op (the source object is simply absent). - """ - old_prefix = f'compact/{compact}/provider/{previous_provider_id}/' - new_prefix = f'compact/{compact}/provider/{new_provider_id}/' - - paginator = config.s3_client.get_paginator('list_objects_v2') - for page in paginator.paginate(Bucket=config.provider_user_bucket_name, Prefix=old_prefix): - for s3_object in page.get('Contents', []): - old_key = s3_object['Key'] - new_key = new_prefix + old_key[len(old_prefix) :] - _move_s3_object(old_key=old_key, new_key=new_key) - - -def _move_s3_object(*, old_key: str, new_key: str): - try: - config.s3_client.copy_object( - Bucket=config.provider_user_bucket_name, - CopySource={'Bucket': config.provider_user_bucket_name, 'Key': old_key}, - Key=new_key, - ) - config.s3_client.delete_object(Bucket=config.provider_user_bucket_name, Key=old_key) - except ClientError as e: - logger.error( - 'Failed to move provider document to the new keyspace', - old_key=old_key, - new_key=new_key, - error=str(e), + old_registered_email=result.old_provider_registered_email, ) From e1082dad6d88de62e413cd5ce644f2d4970d2367 Mon Sep 17 00:00:00 2001 From: Landon Shumway Date: Tue, 14 Jul 2026 16:34:27 -0700 Subject: [PATCH 28/41] update comment --- backend/compact-connect/stacks/ingest_stack.py | 6 +++--- .../compact-connect/stacks/persistent_stack/ssn_table.py | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/backend/compact-connect/stacks/ingest_stack.py b/backend/compact-connect/stacks/ingest_stack.py index 4c8352513a..03aa223b31 100644 --- a/backend/compact-connect/stacks/ingest_stack.py +++ b/backend/compact-connect/stacks/ingest_stack.py @@ -101,9 +101,9 @@ def _add_v1_ingest_chain( self, 'V1Ingest', process_function=ingest_handler, - # SQS visibility timeout is set to 4x the function timeout, - # so a message stays invisible long enough to cover the full batch's processing before it can be - # redelivered. See https://docs.aws.amazon.com/lambda/latest/dg/services-sqs-configure.html + # SQS visibility timeout is larger than the function timeout, + # so a message stays invisible long enough to cover the full batch's processing, plus potential retries, + # before it can be redelivered. See https://docs.aws.amazon.com/lambda/latest/dg/services-sqs-configure.html visibility_timeout=Duration.minutes(20), retention_period=Duration.hours(12), max_batching_window=Duration.minutes(1), diff --git a/backend/compact-connect/stacks/persistent_stack/ssn_table.py b/backend/compact-connect/stacks/persistent_stack/ssn_table.py index c4c7d9cace..50a301b5a1 100644 --- a/backend/compact-connect/stacks/persistent_stack/ssn_table.py +++ b/backend/compact-connect/stacks/persistent_stack/ssn_table.py @@ -487,9 +487,9 @@ def _setup_license_preprocessor_queue(self, data_event_bus: EventBus, alarm_topi self, 'LicenseQueuePreprocessor', process_function=preprocess_handler, - # SQS visibility timeout is set to 4x the function timeout, - # so a message stays invisible long enough to cover the full batch's processing before it can be - # redelivered. See https://docs.aws.amazon.com/lambda/latest/dg/services-sqs-configure.html + # SQS visibility timeout is larger than the function timeout, + # so a message stays invisible long enough to cover the full batch's processing, plus potential retries, + # before it can be redelivered. See https://docs.aws.amazon.com/lambda/latest/dg/services-sqs-configure.html visibility_timeout=Duration.minutes(8), retention_period=Duration.hours(12), max_batching_window=Duration.minutes(1), From 610870cb748c36cf2aba440c9af1d7b0139abc4e Mon Sep 17 00:00:00 2001 From: Landon Shumway Date: Tue, 14 Jul 2026 16:55:19 -0700 Subject: [PATCH 29/41] Add error log alarm for ingest handler --- .../compact-connect/stacks/ingest_stack.py | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/backend/compact-connect/stacks/ingest_stack.py b/backend/compact-connect/stacks/ingest_stack.py index 03aa223b31..52a602778b 100644 --- a/backend/compact-connect/stacks/ingest_stack.py +++ b/backend/compact-connect/stacks/ingest_stack.py @@ -7,6 +7,7 @@ from aws_cdk.aws_cloudwatch_actions import SnsAction from aws_cdk.aws_events import EventBus, EventPattern, Rule from aws_cdk.aws_events_targets import SqsQueue +from aws_cdk.aws_logs import FilterPattern, MetricFilter from cdk_nag import NagSuppressions from common_constructs.python_function import PythonFunction from common_constructs.queued_lambda_processor import QueuedLambdaProcessor @@ -97,6 +98,33 @@ def _add_v1_ingest_chain( treat_missing_data=TreatMissingData.NOT_BREACHING, ).add_alarm_action(SnsAction(persistent_stack.alarm_topic)) + # The invocation-error alarm above only catches failures that escape the handler. The sqs_handler + # reports per-message failures as batch item failures (the invocation still succeeds), and some paths + # (e.g. the best-effort S3 document move during an SSN-correction migration) log an ERROR without + # raising, so we also alarm directly on ERROR-level log lines to catch those. + error_log_metric = MetricFilter( + self, + 'V1IngestErrorLogMetric', + log_group=ingest_handler.log_group, + metric_namespace='CompactConnect/Ingest', + metric_name='V1IngestErrors', + filter_pattern=FilterPattern.string_value(json_field='$.level', comparison='=', value='ERROR'), + metric_value='1', + default_value=0, + ) + Alarm( + self, + 'V1IngestErrorLogAlarm', + metric=error_log_metric.metric(statistic='Sum'), + evaluation_periods=1, + threshold=1, + actions_enabled=True, + alarm_description=f'The ingest handler Lambda logged an ERROR level message. Investigate the logs ' + f'for the {ingest_handler.function_name} lambda to determine the cause.', + comparison_operator=ComparisonOperator.GREATER_THAN_OR_EQUAL_TO_THRESHOLD, + treat_missing_data=TreatMissingData.NOT_BREACHING, + ).add_alarm_action(SnsAction(persistent_stack.alarm_topic)) + processor = QueuedLambdaProcessor( self, 'V1Ingest', From 6db47d044616778ac5fe9397141d98f2d42e141b Mon Sep 17 00:00:00 2001 From: Landon Shumway Date: Tue, 14 Jul 2026 21:30:34 -0700 Subject: [PATCH 30/41] refresh test staff tokens on 401 in smoke tests --- .../tests/smoke/smoke_common.py | 40 ++++++++++++++----- .../tests/smoke/ssn_migration_smoke_tests.py | 38 +++++++++++++----- 2 files changed, 58 insertions(+), 20 deletions(-) diff --git a/backend/compact-connect/tests/smoke/smoke_common.py b/backend/compact-connect/tests/smoke/smoke_common.py index b512be0c07..7bd0fe0b92 100644 --- a/backend/compact-connect/tests/smoke/smoke_common.py +++ b/backend/compact-connect/tests/smoke/smoke_common.py @@ -362,23 +362,36 @@ def upload_license_record(staff_headers: dict, compact: str, jurisdiction: str, return post_response.json() -def query_provider_by_name(staff_headers: dict, compact: str, given_name: str, family_name: str): +def query_provider_by_name( + staff_headers: dict, compact: str, given_name: str, family_name: str, staff_user_email: str | None = None +): """Query for a provider by name and return the provider ID if found. :param staff_headers: Authentication headers for staff user :param compact: The compact abbreviation :param given_name: Provider's given name :param family_name: Provider's family name + :param staff_user_email: If provided, the staff user's email to re-authenticate with when the access token + has expired. On a 401 the token is refreshed (mutating staff_headers in place so callers holding this + dict pick up the new token) and the query retried once. Callers polling this endpoint across long + waits should pass this so the loop doesn't fail once the token expires. :return: The provider ID if found, None otherwise """ query_body = {'query': {'familyName': family_name, 'givenName': given_name}} - query_response = requests.post( - url=f'{config.api_base_url}/v1/compacts/{compact}/providers/query', - headers=staff_headers, - json=query_body, - timeout=10, - ) + def _post(): + return requests.post( + url=f'{config.api_base_url}/v1/compacts/{compact}/providers/query', + headers=staff_headers, + json=query_body, + timeout=10, + ) + + query_response = _post() + if query_response.status_code == 401 and staff_user_email is not None: + logger.info('Staff auth token expired (401); refreshing and retrying provider query') + staff_headers.update(get_staff_user_auth_headers(staff_user_email)) + query_response = _post() if query_response.status_code != 200: logger.warning(f'Query failed with status {query_response.status_code}') @@ -393,7 +406,12 @@ def query_provider_by_name(staff_headers: dict, compact: str, given_name: str, f def wait_for_provider_creation( - staff_headers: dict, compact: str, given_name: str, family_name: str, max_wait_time: int = 300 + staff_headers: dict, + compact: str, + given_name: str, + family_name: str, + max_wait_time: int = 300, + staff_user_email: str | None = None, ): """Poll for provider creation after license upload. @@ -402,6 +420,8 @@ def wait_for_provider_creation( :param given_name: Provider's given name :param family_name: Provider's family name :param max_wait_time: Maximum time to wait in seconds (default: 300 = 5 minutes) + :param staff_user_email: If provided, the staff user's email to re-authenticate with when the access token + expires mid-poll (long waits can outlast the token). Passed through to query_provider_by_name. :return: The provider ID when found :raises SmokeTestFailureException: If provider not found within max_wait_time """ @@ -417,7 +437,9 @@ def wait_for_provider_creation( while attempts < max_attempts: attempts += 1 - provider_id = query_provider_by_name(staff_headers, compact, given_name, family_name) + provider_id = query_provider_by_name( + staff_headers, compact, given_name, family_name, staff_user_email=staff_user_email + ) if provider_id: elapsed_time = time.time() - start_time logger.info(f'✅ Provider found after {elapsed_time:.1f} seconds. Provider ID: {provider_id}') diff --git a/backend/compact-connect/tests/smoke/ssn_migration_smoke_tests.py b/backend/compact-connect/tests/smoke/ssn_migration_smoke_tests.py index 7b62ce58a4..d2e35a5139 100644 --- a/backend/compact-connect/tests/smoke/ssn_migration_smoke_tests.py +++ b/backend/compact-connect/tests/smoke/ssn_migration_smoke_tests.py @@ -134,9 +134,7 @@ def _create_test_app_client_headers(client_name: str, compact: str, jurisdiction client_credentials = create_test_app_client(client_name, compact, jurisdiction) client_id = client_credentials['client_id'] try: - client_headers = get_client_auth_headers( - client_id, client_credentials['client_secret'], compact, jurisdiction - ) + client_headers = get_client_auth_headers(client_id, client_credentials['client_secret'], compact, jurisdiction) except Exception: delete_test_app_client(client_id) raise @@ -285,13 +283,27 @@ def _wait_until(description: str, predicate: Callable, max_wait_seconds: int = _ def _query_provider_ids_by_name(staff_headers: dict, compact: str, given_name: str, family_name: str) -> list[str]: - """Query the providers endpoint by name and return all matching provider ids.""" - query_response = requests.post( - url=f'{get_api_base_url()}/v1/compacts/{compact}/providers/query', - headers=staff_headers, - json={'query': {'familyName': family_name, 'givenName': given_name}}, - timeout=10, - ) + """Query the providers endpoint by name and return all matching provider ids. + + The migration waits below poll this endpoint for many minutes - long enough for the staff user's access + token to expire, after which the endpoint returns 401. On a 401 we refresh the token (mutating + staff_headers in place so every caller holding this dict picks up the new token) and retry once, so the + long-running polling loops don't fail spuriously. + """ + + def _post(): + return requests.post( + url=f'{get_api_base_url()}/v1/compacts/{compact}/providers/query', + headers=staff_headers, + json={'query': {'familyName': family_name, 'givenName': given_name}}, + timeout=10, + ) + + query_response = _post() + if query_response.status_code == 401: + logger.info('Staff auth token expired (401); refreshing and retrying provider query') + staff_headers.update(get_staff_user_auth_headers(TEST_STAFF_USER_EMAIL)) + query_response = _post() if query_response.status_code != 200: logger.warning(f'Provider query failed with status {query_response.status_code}') return [] @@ -747,7 +759,11 @@ def test_partial_ssn_migration(): ], ) old_provider_id = wait_for_provider_creation( - staff_headers, PARTIAL_MIGRATION_COMPACT, PARTIAL_MIGRATION_GIVEN_NAME, PARTIAL_MIGRATION_FAMILY_NAME + staff_headers, + PARTIAL_MIGRATION_COMPACT, + PARTIAL_MIGRATION_GIVEN_NAME, + PARTIAL_MIGRATION_FAMILY_NAME, + staff_user_email=TEST_STAFF_USER_EMAIL, ) _wait_until( f'both license records to exist under provider {old_provider_id}', From 63ec88f5fbf9770e4867e9ae4620e8646d0a33d3 Mon Sep 17 00:00:00 2001 From: Landon Shumway Date: Tue, 14 Jul 2026 22:06:50 -0700 Subject: [PATCH 31/41] Feedback - remove dead guard checks --- .../cc_common/data_model/data_client.py | 55 +++++++++---------- 1 file changed, 26 insertions(+), 29 deletions(-) diff --git a/backend/compact-connect/lambdas/python/common/cc_common/data_model/data_client.py b/backend/compact-connect/lambdas/python/common/cc_common/data_model/data_client.py index a320d72535..6a275cfbc4 100644 --- a/backend/compact-connect/lambdas/python/common/cc_common/data_model/data_client.py +++ b/backend/compact-connect/lambdas/python/common/cc_common/data_model/data_client.py @@ -3021,34 +3021,31 @@ def migrate_provider_for_ssn_correction( if record is not target_license ] - # final: bounded to at most three items (ssnCorrection put, old provider fence, target license delete). - # On a defensive replay that already lost the old provider record, old_provider_data is None: the - # ssnCorrection record and fence are skipped (the run that deleted the record already wrote the - # ssnCorrection), leaving only the target license delete. - final_transaction_items = [] - if old_top_level_provider_data is not None: - ssn_correction_update = ProviderUpdateData.create_new( - { - 'type': ProviderRecordType.PROVIDER_UPDATE, - 'updateType': UpdateCategory.SSN_CORRECTION, - 'providerId': new_provider_id, - 'compact': compact, - 'previous': old_top_level_provider_data.to_dict(), - 'createDate': config.current_standard_datetime, - 'updatedValues': {'ssnLastFour': new_ssn_last_four}, - } - ) - final_transaction_items.append(self._build_put_transaction_item(ssn_correction_update)) - final_transaction_items.append( - self._build_conditioned_old_provider_transaction_item( - old_provider_data=old_top_level_provider_data, - old_provider_records=old_provider_records, - full_migration=full_migration, - jurisdiction=jurisdiction, - license_type=license_type, - ) - ) - final_transaction_items.append(self._build_delete_transaction_item(target_license_key)) + # final: exactly three items — the ssnCorrection provider-update record, the conditioned deletion + # (full migration) or repopulation (partial migration) of the old top-level provider record (the + # concurrency fence), and the target license delete. + ssn_correction_update = ProviderUpdateData.create_new( + { + 'type': ProviderRecordType.PROVIDER_UPDATE, + 'updateType': UpdateCategory.SSN_CORRECTION, + 'providerId': new_provider_id, + 'compact': compact, + 'previous': old_top_level_provider_data.to_dict(), + 'createDate': config.current_standard_datetime, + 'updatedValues': {'ssnLastFour': new_ssn_last_four}, + } + ) + final_transaction_items = [ + self._build_put_transaction_item(ssn_correction_update), + self._build_conditioned_old_provider_transaction_item( + old_provider_data=old_top_level_provider_data, + old_provider_records=old_provider_records, + full_migration=full_migration, + jurisdiction=jurisdiction, + license_type=license_type, + ), + self._build_delete_transaction_item(target_license_key), + ] # Move the practitioner's S3 documents to the new provider id's keyspace before committing the DynamoDB # migration. Doing this before the commit (the point at which the idempotency guard flips, since the @@ -3091,7 +3088,7 @@ def migrate_provider_for_ssn_correction( full_migration=full_migration, old_provider_registered_email=( old_top_level_provider_data.to_dict().get('compactConnectRegisteredEmailAddress') - if full_migration and old_top_level_provider_data is not None + if full_migration else None ), ) From 730461e1c87ba9613bcd9a5377f9f4b6663283a7 Mon Sep 17 00:00:00 2001 From: Landon Shumway Date: Wed, 15 Jul 2026 07:38:15 -0700 Subject: [PATCH 32/41] Add micro unit tests for get_records_associated_with_license method --- .../tests/unit/test_provider_record_util.py | 241 ++++++++++++++++++ 1 file changed, 241 insertions(+) diff --git a/backend/compact-connect/lambdas/python/common/tests/unit/test_provider_record_util.py b/backend/compact-connect/lambdas/python/common/tests/unit/test_provider_record_util.py index d7526d31de..e88268e235 100644 --- a/backend/compact-connect/lambdas/python/common/tests/unit/test_provider_record_util.py +++ b/backend/compact-connect/lambdas/python/common/tests/unit/test_provider_record_util.py @@ -1335,3 +1335,244 @@ def test_calculation_returns_oldest_renewal_date_if_privilege_expired_and_then_w ) self.assertEqual(datetime.fromisoformat('2098-04-04T12:59:59+00:00'), active_since) + + +class TestGetRecordsAssociatedWithLicense(TstLambdas): + """Coverage for ProviderUserRecords.get_records_associated_with_license. + + The SSN-correction migration (migrate_provider_for_ssn_correction) relies on this method to return the + target license together with EVERY record that depends on it across MULTIPLE privileges and multiples + of each dependent record type so nothing is silently left behind on the old provider. Each test asserts + that one record type comes back in full. + + A single rich record set is generated once in setUp: the target license (oh / slp) with two privileges + purchased against it (in ne and ky), two of every dependent record type for the license and for each + privilege, plus noise that must be excluded (a different license type in the same jurisdiction with its + own dependents, and person-level records). + """ + + # The two jurisdictions the privileges purchased against the target license live in. + PRIVILEGE_JURISDICTIONS = ('ne', 'ky') + + def setUp(self): + from common_test import test_constants as constants + from common_test.test_data_generator import TestDataGenerator + + gen = TestDataGenerator + self.target_jurisdiction = constants.DEFAULT_LICENSE_JURISDICTION + self.target_license_type = constants.DEFAULT_LICENSE_TYPE + abbr = constants.DEFAULT_LICENSE_TYPE_ABBREVIATION + + # --- target license and the two privileges purchased against it (home license oh/slp) --- + self.target_license = gen.generate_default_license() + self.associated_privileges = [ + gen.generate_default_privilege( + {'jurisdiction': jurisdiction, 'privilegeId': f'{abbr.upper()}-{jurisdiction.upper()}-1'} + ) + for jurisdiction in self.PRIVILEGE_JURISDICTIONS + ] + + # --- two of every dependent record type for the LICENSE itself --- + self.license_adverse_actions = [ + gen.generate_default_adverse_action( + {'actionAgainst': 'license', 'jurisdiction': self.target_jurisdiction, 'adverseActionId': aa_id} + ) + for aa_id in ('11111111-0000-0000-0000-000000000001', '11111111-0000-0000-0000-000000000002') + ] + # one open and one closed - the method requests include_closed=True, so BOTH must come back + self.license_investigations = [ + gen.generate_default_investigation( + { + 'investigationAgainst': 'license', + 'jurisdiction': self.target_jurisdiction, + 'investigationId': '22222222-0000-0000-0000-000000000001', + } + ), + gen.generate_default_investigation( + { + 'investigationAgainst': 'license', + 'jurisdiction': self.target_jurisdiction, + 'investigationId': '22222222-0000-0000-0000-000000000002', + 'closeDate': datetime.fromisoformat('2024-06-01T00:00:00+00:00'), + } + ), + ] + self.license_updates = [ + gen.generate_default_license_update({'createDate': datetime.fromisoformat(create_date)}) + for create_date in ('2024-01-01T00:00:00+00:00', '2024-02-01T00:00:00+00:00') + ] + + # --- two of every dependent record type for EACH privilege (the multi-privilege path) --- + self.privilege_adverse_actions = [] + self.privilege_investigations = [] + self.privilege_updates = [] + for index, jurisdiction in enumerate(self.PRIVILEGE_JURISDICTIONS): + for n in (1, 2): + self.privilege_adverse_actions.append( + gen.generate_default_adverse_action( + { + 'actionAgainst': 'privilege', + 'jurisdiction': jurisdiction, + 'adverseActionId': f'33333333-000{index}-0000-0000-00000000000{n}', + } + ) + ) + self.privilege_investigations.append( + gen.generate_default_investigation( + { + 'investigationAgainst': 'privilege', + 'jurisdiction': jurisdiction, + 'investigationId': f'44444444-000{index}-0000-0000-00000000000{n}', + } + ) + ) + self.privilege_updates.append( + gen.generate_default_privilege_update( + { + 'jurisdiction': jurisdiction, + 'createDate': datetime.fromisoformat(f'2024-0{n}-1{index}T00:00:00+00:00'), + } + ) + ) + + # --- noise: a different license type (oh / audiologist) with its own full set of dependents --- + other_type, other_abbr = 'audiologist', 'aud' + self.other_license_records = [ + gen.generate_default_license({'licenseType': other_type}), + gen.generate_default_privilege( + {'licenseType': other_type, 'jurisdiction': 'ne', 'privilegeId': 'AUD-NE-1'} + ), + gen.generate_default_adverse_action( + { + 'actionAgainst': 'license', + 'jurisdiction': self.target_jurisdiction, + 'licenseType': other_type, + 'licenseTypeAbbreviation': other_abbr, + 'adverseActionId': '99999999-0000-0000-0000-000000000001', + } + ), + gen.generate_default_investigation( + { + 'investigationAgainst': 'license', + 'jurisdiction': self.target_jurisdiction, + 'licenseType': other_type, + 'licenseTypeAbbreviation': other_abbr, + 'investigationId': '99999999-0000-0000-0000-000000000002', + } + ), + gen.generate_default_license_update({'licenseType': other_type}), + gen.generate_default_adverse_action( + { + 'actionAgainst': 'privilege', + 'jurisdiction': 'ne', + 'licenseType': other_type, + 'licenseTypeAbbreviation': other_abbr, + 'adverseActionId': '99999999-0000-0000-0000-000000000003', + } + ), + gen.generate_default_investigation( + { + 'investigationAgainst': 'privilege', + 'jurisdiction': 'ne', + 'licenseType': other_type, + 'licenseTypeAbbreviation': other_abbr, + 'investigationId': '99999999-0000-0000-0000-000000000004', + } + ), + gen.generate_default_privilege_update({'jurisdiction': 'ne', 'licenseType': other_type}), + ] + # --- noise: person-level records, never associated with any single license --- + self.person_level_records = [ + gen.generate_default_military_affiliation(), + gen.generate_default_provider_update(), + gen.generate_default_provider(), + ] + + self.all_associated_records = [ + self.target_license, + *self.associated_privileges, + *self.license_adverse_actions, + *self.license_investigations, + *self.license_updates, + *self.privilege_adverse_actions, + *self.privilege_investigations, + *self.privilege_updates, + ] + + from cc_common.data_model.provider_record_util import ProviderUserRecords + + self.records = ProviderUserRecords( + [ + record.serialize_to_database_record() + for record in [*self.all_associated_records, *self.other_license_records, *self.person_level_records] + ] + ) + self.result = self.records.get_records_associated_with_license( + self.target_jurisdiction, self.target_license_type + ) + + @staticmethod + def _keys(records): + """Reduce records to their unique (pk, sk) identity for order-independent set comparison.""" + return {(rec.serialize_to_database_record()['pk'], rec.serialize_to_database_record()['sk']) for rec in records} + + def _returned(self, record_type, predicate=None): + return [ + record for record in self.result if record.type == record_type and (predicate is None or predicate(record)) + ] + + def test_returns_empty_list_when_provider_has_no_matching_license(self): + # 'ky' holds a privilege but no license, so there is no license to anchor the association + self.assertEqual([], self.records.get_records_associated_with_license('ky', self.target_license_type)) + + def test_includes_the_target_license_record(self): + self.assertEqual(self._keys([self.target_license]), self._keys(self._returned('license'))) + + def test_includes_all_privileges_associated_with_the_license(self): + self.assertEqual(self._keys(self.associated_privileges), self._keys(self._returned('privilege'))) + + def test_includes_all_adverse_actions_against_the_license(self): + self.assertEqual( + self._keys(self.license_adverse_actions), + self._keys(self._returned('adverseAction', lambda r: r.actionAgainst == 'license')), + ) + + def test_includes_all_adverse_actions_against_every_associated_privilege(self): + # multi-privilege path: two privileges, two adverse actions each -> all four must return + self.assertEqual( + self._keys(self.privilege_adverse_actions), + self._keys(self._returned('adverseAction', lambda r: r.actionAgainst == 'privilege')), + ) + + def test_includes_all_investigations_against_the_license_including_closed(self): + self.assertEqual( + self._keys(self.license_investigations), + self._keys(self._returned('investigation', lambda r: r.investigationAgainst == 'license')), + ) + + def test_includes_all_investigations_against_every_associated_privilege(self): + # multi-privilege path: two privileges, two investigations each -> all four must return + self.assertEqual( + self._keys(self.privilege_investigations), + self._keys(self._returned('investigation', lambda r: r.investigationAgainst == 'privilege')), + ) + + def test_includes_all_license_update_records(self): + self.assertEqual(self._keys(self.license_updates), self._keys(self._returned('licenseUpdate'))) + + def test_includes_all_privilege_update_records_for_every_associated_privilege(self): + # multi-privilege path: two privileges, two update records each -> all four must return + self.assertEqual(self._keys(self.privilege_updates), self._keys(self._returned('privilegeUpdate'))) + + def test_excludes_records_belonging_to_a_different_license_type(self): + returned_keys = self._keys(self.result) + leaked = self._keys(self.other_license_records) & returned_keys + self.assertEqual(set(), leaked, f'records for a different license type leaked into the result: {leaked}') + + def test_excludes_person_level_records(self): + returned_keys = self._keys(self.result) + leaked = self._keys(self.person_level_records) & returned_keys + self.assertEqual(set(), leaked, f'person-level records leaked into the result: {leaked}') + + def test_returns_only_the_expected_records_and_nothing_else(self): + self.assertEqual(self._keys(self.all_associated_records), self._keys(self.result)) From 306f579c2d230ca03c798e2f6a6b829701c666a5 Mon Sep 17 00:00:00 2001 From: Landon Shumway Date: Thu, 16 Jul 2026 07:22:54 -0700 Subject: [PATCH 33/41] update wording for email based on feedback --- .../lambdas/nodejs/lib/email/email-notification-service.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/compact-connect/lambdas/nodejs/lib/email/email-notification-service.ts b/backend/compact-connect/lambdas/nodejs/lib/email/email-notification-service.ts index 5005174877..0fbc729e93 100644 --- a/backend/compact-connect/lambdas/nodejs/lib/email/email-notification-service.ts +++ b/backend/compact-connect/lambdas/nodejs/lib/email/email-notification-service.ts @@ -455,7 +455,7 @@ export class EmailNotificationService extends BaseEmailService { const report = this.getNewEmailTemplate(); const subject = `Action Required: Registration Update - CompactConnect`; const registrationUrl = `${environmentVariableService.getUiBasePathUrl()}/register`; - const bodyText = `Your state licensing board recently corrected the information on one of your license records in the CompactConnect system. As part of this correction, you will need to register again with your license record.\n\nAny active privileges you currently hold remain active and unaffected by this change, so you may continue practicing under them.\n\nTo continue using CompactConnect, please register again using the link below:\n\n${registrationUrl}\n\nIf you have any questions, please contact your state licensing board.`; + const bodyText = `Your state licensing board recently corrected the information on one of your license records in the CompactConnect system. As part of this correction, you will need to register again with your license record.\n\nAny active compact privileges to practice you currently hold remain active and unaffected by this change, so you may continue practicing under them.\n\nTo continue using CompactConnect, please register again using the link below:\n\n${registrationUrl}\n\nIf you have any questions, please contact your state licensing board.`; this.insertHeader(report, 'Registration Update Required'); this.insertBody(report, bodyText, 'center', true); From 44b3b09884539ed754d38eb64b814c8ca07e5340 Mon Sep 17 00:00:00 2001 From: Landon Shumway Date: Thu, 16 Jul 2026 07:36:34 -0700 Subject: [PATCH 34/41] Reject previousSSN requests if flag is disabled This will prevent any accidental duplicates or deletions from occurring if we need to disable the feature. --- .../provider-data-v1/handlers/bulk_upload.py | 9 ++++--- .../provider-data-v1/handlers/licenses.py | 7 ++--- .../test_handlers/test_bulk_upload.py | 8 ++---- .../function/test_handlers/test_licenses.py | 27 ++++++++++++++----- 4 files changed, 29 insertions(+), 22 deletions(-) diff --git a/backend/compact-connect/lambdas/python/provider-data-v1/handlers/bulk_upload.py b/backend/compact-connect/lambdas/python/provider-data-v1/handlers/bulk_upload.py index 07b427c6f0..f28b6a24a8 100644 --- a/backend/compact-connect/lambdas/python/provider-data-v1/handlers/bulk_upload.py +++ b/backend/compact-connect/lambdas/python/provider-data-v1/handlers/bulk_upload.py @@ -182,11 +182,12 @@ def process_bulk_upload_file( logger.error('License contains unsupported fields', fields=list(raw_license.keys()), exc_info=e) raise ValidationError('License contains unsupported fields') from e # TODO - remove this flag once the feature is proven stable # noqa: FIX002 - if not ssn_correction_migration_flag_enabled: - logger.info( - 'SSN-correction migration feature is disabled. Ignoring the previousSSN field if present' + if not ssn_correction_migration_flag_enabled and validated_license.get('previousSSN'): + logger.warning( + 'SSN-correction migration feature is disabled. Skipping record with previousSSN', + record_number=i + 1, ) - validated_license.pop('previousSSN', None) + continue current_batch.append(schema.dump(validated_license)) # When batch is full, send to preprocessing queue diff --git a/backend/compact-connect/lambdas/python/provider-data-v1/handlers/licenses.py b/backend/compact-connect/lambdas/python/provider-data-v1/handlers/licenses.py index ec0bfeac38..bf798b55e9 100644 --- a/backend/compact-connect/lambdas/python/provider-data-v1/handlers/licenses.py +++ b/backend/compact-connect/lambdas/python/provider-data-v1/handlers/licenses.py @@ -58,11 +58,8 @@ def post_licenses(event: dict, context: LambdaContext): # noqa: ARG001 unused-a license_entry = {**license_record, 'compact': compact, 'jurisdiction': jurisdiction} try: # TODO - remove this flag once the feature is proven stable # noqa: FIX002 - if not ssn_correction_migration_flag_enabled: - logger.info( - 'SSN-correction migration feature is disabled. Ignoring the previousSSN field if present' - ) - license_entry.pop('previousSSN', None) + if not ssn_correction_migration_flag_enabled and license_entry.get('previousSSN'): + raise CCInvalidRequestException('The previousSSN feature is not currently enabled') licenses.append(schema.load(license_entry)) except ValidationError as e: logger.debug( diff --git a/backend/compact-connect/lambdas/python/provider-data-v1/tests/function/test_handlers/test_bulk_upload.py b/backend/compact-connect/lambdas/python/provider-data-v1/tests/function/test_handlers/test_bulk_upload.py index e262dc406b..3dd89f70e9 100644 --- a/backend/compact-connect/lambdas/python/provider-data-v1/tests/function/test_handlers/test_bulk_upload.py +++ b/backend/compact-connect/lambdas/python/provider-data-v1/tests/function/test_handlers/test_bulk_upload.py @@ -157,15 +157,11 @@ def test_bulk_upload_passes_previous_ssn_through_when_flag_enabled(self): self.assertEqual('123-45-9876', message_data['previousSSN']) # TODO - remove this test once the LICENSE_SSN_CORRECTION_MIGRATION_FLAG scaffolding is removed # noqa: FIX002 - def test_bulk_upload_strips_previous_ssn_when_flag_disabled(self): + def test_bulk_upload_skips_record_with_previous_ssn_when_flag_disabled(self): with patch('handlers.bulk_upload.ssn_correction_migration_flag_enabled', False): messages = self._process_csv_with_previous_ssn() - self.assertEqual(1, len(messages)) - message_data = json.loads(messages[0].body) - self.assertNotIn('previousSSN', message_data) - # the rest of the license data must be unaffected - self.assertEqual('123-45-6789', message_data['ssn']) + self.assertEqual(0, len(messages)) def test_bulk_upload_strips_whitespace_from_string_fields(self): """Test that whitespace is stripped from all string fields in CSV data.""" diff --git a/backend/compact-connect/lambdas/python/provider-data-v1/tests/function/test_handlers/test_licenses.py b/backend/compact-connect/lambdas/python/provider-data-v1/tests/function/test_handlers/test_licenses.py index 43e8739f48..286b7aa59d 100644 --- a/backend/compact-connect/lambdas/python/provider-data-v1/tests/function/test_handlers/test_licenses.py +++ b/backend/compact-connect/lambdas/python/provider-data-v1/tests/function/test_handlers/test_licenses.py @@ -128,15 +128,28 @@ def test_post_licenses_passes_previous_ssn_through_when_flag_enabled(self): self.assertEqual('123-12-9876', message['previousSSN']) # TODO - remove this test once the LICENSE_SSN_CORRECTION_MIGRATION_FLAG scaffolding is removed # noqa: FIX002 - def test_post_licenses_strips_previous_ssn_when_flag_disabled(self): + def test_post_licenses_rejects_previous_ssn_when_flag_disabled(self): + from handlers.licenses import post_licenses + + with open('../common/tests/resources/api-event.json') as f: + event = json.load(f) + + event['requestContext']['authorizer']['claims']['scope'] = 'openid email aslp/readGeneral oh/aslp.write' + event['pathParameters'] = {'compact': 'aslp', 'jurisdiction': 'oh'} + with open('../common/tests/resources/api/license-post.json') as f: + license_data = json.load(f) + license_data['previousSSN'] = '123-12-9876' + event['body'] = json.dumps([license_data]) + + event = self._create_signed_event(event) + with patch('handlers.licenses.ssn_correction_migration_flag_enabled', False): - queue_messages = self._post_license_with_previous_ssn() + resp = post_licenses(event, self.mock_context) - self.assertEqual(1, len(queue_messages)) - message = json.loads(queue_messages[0].body) - self.assertNotIn('previousSSN', message) - # the rest of the license data must be unaffected - self.assertEqual('123-12-1234', message['ssn']) + self.assertEqual(400, resp['statusCode']) + body = json.loads(resp['body']) + self.assertEqual('The previousSSN feature is not currently enabled', body['message']) + self.assertEqual(0, len(self._license_preprocessing_queue.receive_messages(MaxNumberOfMessages=10))) def test_post_licenses_does_not_let_request_body_override_path_parameters(self): from handlers.licenses import post_licenses From 968e907221e736fcb3e84692ed0d4eb367f2984a Mon Sep 17 00:00:00 2001 From: Landon Shumway Date: Tue, 21 Jul 2026 09:50:32 -0500 Subject: [PATCH 35/41] feedback - test/comment updates --- .../test_data_client_ssn_correction.py | 28 +++++++++---------- .../tests/unit/test_provider_record_util.py | 5 ++-- 2 files changed, 15 insertions(+), 18 deletions(-) diff --git a/backend/compact-connect/lambdas/python/common/tests/function/test_data_client_ssn_correction.py b/backend/compact-connect/lambdas/python/common/tests/function/test_data_client_ssn_correction.py index 482c3494fc..a8554c197d 100644 --- a/backend/compact-connect/lambdas/python/common/tests/function/test_data_client_ssn_correction.py +++ b/backend/compact-connect/lambdas/python/common/tests/function/test_data_client_ssn_correction.py @@ -116,8 +116,6 @@ def test_full_migration_moves_all_records_and_empties_old_partition(self): self.assertEqual(NEW_SSN_LAST_FOUR, migrated_license['ssnLastFour']) # the migrated military affiliation record must reference document keys under the new provider id. - # (The caller moves the underlying S3 objects by listing the old provider id's keyspace directly, not - # from this DynamoDB record, so it is not reflected in the migration result.) migrated_military = self._get_records_of_type(NEW_PROVIDER_ID, 'militaryAffiliation')[0] for document_key in migrated_military['documentKeys']: self.assertIn(NEW_PROVIDER_ID, document_key) @@ -663,12 +661,12 @@ def test_small_migration_is_committed_as_single_atomic_transaction(self): self.assertEqual(1, len(executed_transactions)) - def test_large_migration_creates_before_deletes_and_tears_down_critical_records_atomically(self): - """When a migration exceeds the DynamoDB transaction limit it must (a) create every new record before - deleting any old record, and (b) tear down the old top-level provider record (the fence) and the - target license together in a single atomic final transaction. This keeps replay safe: until the final - transaction commits, both critical records survive for the replay's idempotency guard to find, and the - old provider stays readable for the Cognito/email path. + def test_large_migration_batches_phases_and_tears_down_critical_records_atomically(self): + """When a migration exceeds the DynamoDB transaction limit it must split work into create and delete + batches (verified by expected batch counts for this fixture) and tear down the old top-level provider + record (the fence) and the target license together in a single atomic final transaction. This keeps + replay safe: until the final transaction commits, both critical records survive for the replay's + idempotency guard to find, and the old provider stays readable for the Cognito/email path. """ self._put_full_old_provider_records() @@ -680,9 +678,9 @@ def test_large_migration_creates_before_deletes_and_tears_down_critical_records_ old_provider_pk = f'{DEFAULT_COMPACT}#PROVIDER#{DEFAULT_PROVIDER_ID}' new_provider_pk = f'{DEFAULT_COMPACT}#PROVIDER#{NEW_PROVIDER_ID}' - # the create phase (transactions that only put records under the new provider) must fully precede the - # delete phase (transactions that only delete records from the old provider); the mixed final - # transaction is neither and is checked separately below + # the create phase (transactions that only put records under the new provider) and the delete phase + # (transactions that only delete records from the old provider) must each produce the expected number + # of batches. The mixed final transaction is checked separately below create_transaction_indexes = [ index for index, transaction in enumerate(executed_transactions) @@ -693,9 +691,8 @@ def test_large_migration_creates_before_deletes_and_tears_down_critical_records_ for index, transaction in enumerate(executed_transactions) if all('Delete' in item and self._key(item)['pk']['S'] == old_provider_pk for item in transaction) ] - self.assertTrue(create_transaction_indexes) - self.assertTrue(delete_transaction_indexes) - self.assertLess(max(create_transaction_indexes), min(delete_transaction_indexes)) + self.assertEqual(4, len(create_transaction_indexes)) + self.assertEqual(3, len(delete_transaction_indexes)) # the final transaction is a single atomic transaction that both tears down the old top-level provider # record (conditioned on its dateOfUpdate) and deletes the target license @@ -753,7 +750,8 @@ def _fail_on_target_license_delete(**kwargs): self.assertEqual(1, len(self._get_records_of_type(DEFAULT_PROVIDER_ID, 'license'))) # replay: succeeds, tears the old provider down, and still reports the registered email for the - # Cognito/email path (the bug this ordering fixes was losing that email on replay) + # cqller to delete the Cognito account and send the email notification + # (the bug this ordering fixes was losing that email on replay) result = self._migrate() self.assertTrue(result.migration_performed) diff --git a/backend/compact-connect/lambdas/python/common/tests/unit/test_provider_record_util.py b/backend/compact-connect/lambdas/python/common/tests/unit/test_provider_record_util.py index e88268e235..4a008f5d82 100644 --- a/backend/compact-connect/lambdas/python/common/tests/unit/test_provider_record_util.py +++ b/backend/compact-connect/lambdas/python/common/tests/unit/test_provider_record_util.py @@ -1341,9 +1341,8 @@ class TestGetRecordsAssociatedWithLicense(TstLambdas): """Coverage for ProviderUserRecords.get_records_associated_with_license. The SSN-correction migration (migrate_provider_for_ssn_correction) relies on this method to return the - target license together with EVERY record that depends on it across MULTIPLE privileges and multiples - of each dependent record type so nothing is silently left behind on the old provider. Each test asserts - that one record type comes back in full. + target license together with EVERY record that depends on it across MULTIPLE privileges so nothing is + silently left behind on the old provider. Each test asserts that one record type comes back in full. A single rich record set is generated once in setUp: the target license (oh / slp) with two privileges purchased against it (in ne and ky), two of every dependent record type for the license and for each From 682ceee041817d3b6fec185548691a6d22d95530 Mon Sep 17 00:00:00 2001 From: Landon Shumway Date: Tue, 21 Jul 2026 09:55:37 -0500 Subject: [PATCH 36/41] Apply license rollback fix to all compact folders --- .../handlers/rollback_license_upload.py | 14 ++++----- .../handlers/rollback_license_upload.py | 21 +++++++++----- .../function/test_rollback_license_upload.py | 29 +++++++++++++++++++ .../handlers/rollback_license_upload.py | 21 +++++++++----- .../function/test_rollback_license_upload.py | 29 +++++++++++++++++++ 5 files changed, 91 insertions(+), 23 deletions(-) diff --git a/backend/compact-connect/lambdas/python/disaster-recovery/handlers/rollback_license_upload.py b/backend/compact-connect/lambdas/python/disaster-recovery/handlers/rollback_license_upload.py index 0898017f80..6cc5d7b4ce 100644 --- a/backend/compact-connect/lambdas/python/disaster-recovery/handlers/rollback_license_upload.py +++ b/backend/compact-connect/lambdas/python/disaster-recovery/handlers/rollback_license_upload.py @@ -432,20 +432,20 @@ def _query_gsi_for_affected_providers( # Generate list of year-month strings to query. # NOTE: We must zero out the time-of-day components here, not just the day. Otherwise, if # start_datetime's time-of-day is later than end_datetime's time-of-day (e.g. start=21:09:55, - # end=12:00:00), the initial current_date <= end_month comparison below can incorrectly evaluate + # end=12:00:00), the initial current_month <= end_month comparison below can incorrectly evaluate # to False even though both timestamps fall within the same month, causing this loop to produce # zero year-months and silently skip the GSI query entirely. - current_date = start_datetime.replace(day=1, hour=0, minute=0, second=0, microsecond=0) + current_month = start_datetime.replace(day=1, hour=0, minute=0, second=0, microsecond=0) end_month = end_datetime.replace(day=1, hour=0, minute=0, second=0, microsecond=0) year_months = [] - while current_date <= end_month: - year_months.append(current_date.strftime('%Y-%m')) + while current_month <= end_month: + year_months.append(current_month.strftime('%Y-%m')) # Move to next month - if current_date.month == 12: - current_date = current_date.replace(year=current_date.year + 1, month=1) + if current_month.month == 12: + current_month = current_month.replace(year=current_month.year + 1, month=1) else: - current_date = current_date.replace(month=current_date.month + 1) + current_month = current_month.replace(month=current_month.month + 1) start_epoch = int(start_datetime.timestamp()) end_epoch = int(end_datetime.timestamp()) diff --git a/backend/cosmetology-app/lambdas/python/disaster-recovery/handlers/rollback_license_upload.py b/backend/cosmetology-app/lambdas/python/disaster-recovery/handlers/rollback_license_upload.py index 52d4227460..78465c622a 100644 --- a/backend/cosmetology-app/lambdas/python/disaster-recovery/handlers/rollback_license_upload.py +++ b/backend/cosmetology-app/lambdas/python/disaster-recovery/handlers/rollback_license_upload.py @@ -398,18 +398,23 @@ def _query_gsi_for_affected_providers( """ affected_provider_ids = set() - # Generate list of year-month strings to query - current_date = start_datetime.replace(day=1) - end_month = end_datetime.replace(day=1) + # Generate list of year-month strings to query. + # NOTE: We must zero out the time-of-day components here, not just the day. Otherwise, if + # start_datetime's time-of-day is later than end_datetime's time-of-day (e.g. start=21:09:55, + # end=12:00:00), the initial current_month <= end_month comparison below can incorrectly evaluate + # to False even though both timestamps fall within the same month, causing this loop to produce + # zero year-months and silently skip the GSI query entirely. + current_month = start_datetime.replace(day=1, hour=0, minute=0, second=0, microsecond=0) + end_month = end_datetime.replace(day=1, hour=0, minute=0, second=0, microsecond=0) year_months = [] - while current_date <= end_month: - year_months.append(current_date.strftime('%Y-%m')) + while current_month <= end_month: + year_months.append(current_month.strftime('%Y-%m')) # Move to next month - if current_date.month == 12: - current_date = current_date.replace(year=current_date.year + 1, month=1) + if current_month.month == 12: + current_month = current_month.replace(year=current_month.year + 1, month=1) else: - current_date = current_date.replace(month=current_date.month + 1) + current_month = current_month.replace(month=current_month.month + 1) start_epoch = int(start_datetime.timestamp()) end_epoch = int(end_datetime.timestamp()) diff --git a/backend/cosmetology-app/lambdas/python/disaster-recovery/tests/function/test_rollback_license_upload.py b/backend/cosmetology-app/lambdas/python/disaster-recovery/tests/function/test_rollback_license_upload.py index 5d69d04931..72c6366c05 100644 --- a/backend/cosmetology-app/lambdas/python/disaster-recovery/tests/function/test_rollback_license_upload.py +++ b/backend/cosmetology-app/lambdas/python/disaster-recovery/tests/function/test_rollback_license_upload.py @@ -351,6 +351,35 @@ def _when_provider_top_level_record_needs_reverted(self, before_upload_datetime: return provider, updated_provider + def test_provider_found_when_start_time_of_day_is_after_end_time_of_day(self): + """ + Regression test: _query_gsi_for_affected_providers must zero out the time-of-day when + computing year-month boundaries, not only the day-of-month. Without this fix, whenever + startDateTime's time-of-day is later than endDateTime's time-of-day within the same month + (e.g. start=21:09:55Z, end=12:00:00Z), the loop exits immediately and produces an empty + year-months list, causing the GSI to be skipped and 0 providers found. + """ + from handlers.rollback_license_upload import rollback_license_upload + + # start and end fall in the same month, but start's time-of-day is later than end's + start_datetime = datetime.fromisoformat('2025-10-20T21:09:55+00:00') + end_datetime = datetime.fromisoformat('2025-10-23T07:00:00+00:00') + upload_datetime = datetime.fromisoformat('2025-10-22T10:00:00+00:00') + + self._when_provider_had_license_updated_from_upload( + upload_datetime=upload_datetime, + license_upload_datetime=start_datetime - timedelta(days=30), + ) + + event = self._generate_test_event() + event['startDateTime'] = start_datetime.isoformat() + event['endDateTime'] = end_datetime.isoformat() + + result = rollback_license_upload(event, Mock()) + + self.assertEqual(result['rollbackStatus'], 'COMPLETE') + self.assertEqual(1, result['providersReverted']) + def test_provider_top_level_record_reset_to_prior_values_when_upload_reverted(self): """Test that provider top-level record is reset to values before upload.""" from handlers.rollback_license_upload import rollback_license_upload diff --git a/backend/social-work-app/lambdas/python/disaster-recovery/handlers/rollback_license_upload.py b/backend/social-work-app/lambdas/python/disaster-recovery/handlers/rollback_license_upload.py index 2754129f5a..23b1da6297 100644 --- a/backend/social-work-app/lambdas/python/disaster-recovery/handlers/rollback_license_upload.py +++ b/backend/social-work-app/lambdas/python/disaster-recovery/handlers/rollback_license_upload.py @@ -404,18 +404,23 @@ def _query_gsi_for_affected_providers( """ affected_provider_ids = set() - # Generate list of year-month strings to query - current_date = start_datetime.replace(day=1) - end_month = end_datetime.replace(day=1) + # Generate list of year-month strings to query. + # NOTE: We must zero out the time-of-day components here, not just the day. Otherwise, if + # start_datetime's time-of-day is later than end_datetime's time-of-day (e.g. start=21:09:55, + # end=12:00:00), the initial current_month <= end_month comparison below can incorrectly evaluate + # to False even though both timestamps fall within the same month, causing this loop to produce + # zero year-months and silently skip the GSI query entirely. + current_month = start_datetime.replace(day=1, hour=0, minute=0, second=0, microsecond=0) + end_month = end_datetime.replace(day=1, hour=0, minute=0, second=0, microsecond=0) year_months = [] - while current_date <= end_month: - year_months.append(current_date.strftime('%Y-%m')) + while current_month <= end_month: + year_months.append(current_month.strftime('%Y-%m')) # Move to next month - if current_date.month == 12: - current_date = current_date.replace(year=current_date.year + 1, month=1) + if current_month.month == 12: + current_month = current_month.replace(year=current_month.year + 1, month=1) else: - current_date = current_date.replace(month=current_date.month + 1) + current_month = current_month.replace(month=current_month.month + 1) start_epoch = int(start_datetime.timestamp()) end_epoch = int(end_datetime.timestamp()) diff --git a/backend/social-work-app/lambdas/python/disaster-recovery/tests/function/test_rollback_license_upload.py b/backend/social-work-app/lambdas/python/disaster-recovery/tests/function/test_rollback_license_upload.py index 0fcca6510f..9e3a3b1209 100644 --- a/backend/social-work-app/lambdas/python/disaster-recovery/tests/function/test_rollback_license_upload.py +++ b/backend/social-work-app/lambdas/python/disaster-recovery/tests/function/test_rollback_license_upload.py @@ -561,6 +561,35 @@ def test_provider_home_state_license_jurisdiction_restored_when_upload_reverted( 'In-window provider update history records should be deleted by rollback', ) + def test_provider_found_when_start_time_of_day_is_after_end_time_of_day(self): + """ + Regression test: _query_gsi_for_affected_providers must zero out the time-of-day when + computing year-month boundaries, not only the day-of-month. Without this fix, whenever + startDateTime's time-of-day is later than endDateTime's time-of-day within the same month + (e.g. start=21:09:55Z, end=12:00:00Z), the loop exits immediately and produces an empty + year-months list, causing the GSI to be skipped and 0 providers found. + """ + from handlers.rollback_license_upload import rollback_license_upload + + # start and end fall in the same month, but start's time-of-day is later than end's + start_datetime = datetime.fromisoformat('2025-10-20T21:09:55+00:00') + end_datetime = datetime.fromisoformat('2025-10-23T07:00:00+00:00') + upload_datetime = datetime.fromisoformat('2025-10-22T10:00:00+00:00') + + self._when_provider_had_license_updated_from_upload( + upload_datetime=upload_datetime, + license_upload_datetime=start_datetime - timedelta(days=30), + ) + + event = self._generate_test_event() + event['startDateTime'] = start_datetime.isoformat() + event['endDateTime'] = end_datetime.isoformat() + + result = rollback_license_upload(event, Mock()) + + self.assertEqual(result['rollbackStatus'], 'COMPLETE') + self.assertEqual(1, result['providersReverted']) + def test_provider_top_level_record_reset_to_prior_values_when_upload_reverted(self): """Test that provider top-level record is reset to values before upload.""" from handlers.rollback_license_upload import rollback_license_upload From d52c6aaeadae8893529ad67a4b23b2521747c757 Mon Sep 17 00:00:00 2001 From: Landon Shumway Date: Tue, 21 Jul 2026 10:05:40 -0500 Subject: [PATCH 37/41] feedback - test/comment refinement --- .../function/test_handlers/test_ingest.py | 10 ++++--- .../function/test_handlers/test_licenses.py | 29 +++++-------------- backend/compact-connect/tests/smoke/config.py | 2 +- 3 files changed, 14 insertions(+), 27 deletions(-) diff --git a/backend/compact-connect/lambdas/python/provider-data-v1/tests/function/test_handlers/test_ingest.py b/backend/compact-connect/lambdas/python/provider-data-v1/tests/function/test_handlers/test_ingest.py index 1a82a2ff3c..d340b8c6d8 100644 --- a/backend/compact-connect/lambdas/python/provider-data-v1/tests/function/test_handlers/test_ingest.py +++ b/backend/compact-connect/lambdas/python/provider-data-v1/tests/function/test_handlers/test_ingest.py @@ -5,6 +5,8 @@ from cc_common.data_model.update_tier_enum import UpdateTierEnum from moto import mock_aws +from common_test.test_constants import DEFAULT_PROVIDER_ID + from .. import TstFunction @@ -908,7 +910,7 @@ class TestIngestSsnCorrection(TstFunction): resolves to NEW_PROVIDER_ID. """ - OLD_PROVIDER_ID = '89a6377e-c3a5-40e5-bca5-317ec854c570' + OLD_PROVIDER_ID = DEFAULT_PROVIDER_ID NEW_PROVIDER_ID = 'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee' NEW_SSN_LAST_FOUR = '6789' OLD_REGISTERED_EMAIL = 'old-provider@example.com' @@ -964,7 +966,7 @@ def _create_old_cognito_user(self): UserAttributes=[{'Name': 'email', 'Value': self.OLD_REGISTERED_EMAIL}], ) - def _when_old_cognito_user_exists(self) -> bool: + def _does_old_cognito_user_exists(self) -> bool: from botocore.exceptions import ClientError try: @@ -1074,7 +1076,7 @@ def test_full_migration_deletes_cognito_user(self): resp = self._run_ingest_with_previous_provider_id() self.assertEqual({'batchItemFailures': []}, resp) - self.assertFalse(self._when_old_cognito_user_exists()) + self.assertFalse(self._does_old_cognito_user_exists()) def test_full_migration_sends_reregistration_email(self): self._put_old_provider_records() @@ -1107,7 +1109,7 @@ def test_partial_migration_keeps_cognito_user_and_sends_no_email(self): self.assertNotIn('militaryAffiliation', new_record_types) # the old Cognito user remains and no re-registration email was sent - self.assertTrue(self._when_old_cognito_user_exists()) + self.assertTrue(self._does_old_cognito_user_exists()) self._mock_send_reregistration_email.assert_not_called() def test_no_op_migration_still_ingests_license_normally(self): diff --git a/backend/compact-connect/lambdas/python/provider-data-v1/tests/function/test_handlers/test_licenses.py b/backend/compact-connect/lambdas/python/provider-data-v1/tests/function/test_handlers/test_licenses.py index 286b7aa59d..c098b7b000 100644 --- a/backend/compact-connect/lambdas/python/provider-data-v1/tests/function/test_handlers/test_licenses.py +++ b/backend/compact-connect/lambdas/python/provider-data-v1/tests/function/test_handlers/test_licenses.py @@ -94,8 +94,8 @@ def test_post_licenses_puts_expected_messages_on_the_queue(self): expected_message['eventTime'] = '2024-11-08T23:59:59+00:00' self.assertEqual(expected_message, json.loads(queue_messages[0].body)) - def _post_license_with_previous_ssn(self) -> list: - """POST a single license carrying a previousSSN and return the resulting queue messages.""" + def _post_license_with_previous_ssn(self) -> dict: + """POST a single license carrying a previousSSN and return the raw response.""" from handlers.licenses import post_licenses with open('../common/tests/resources/api-event.json') as f: @@ -111,40 +111,25 @@ def _post_license_with_previous_ssn(self) -> list: event = self._create_signed_event(event) - resp = post_licenses(event, self.mock_context) - self.assertEqual(200, resp['statusCode']) - - return self._license_preprocessing_queue.receive_messages(MaxNumberOfMessages=10) + return post_licenses(event, self.mock_context) # TODO - once LICENSE_SSN_CORRECTION_MIGRATION_FLAG is removed, remove the patch and rename test # noqa: FIX002 # (previousSSN will always pass through) rather than removing this test outright def test_post_licenses_passes_previous_ssn_through_when_flag_enabled(self): # patch the module-level cached flag value directly, so this test is independent of module import order with patch('handlers.licenses.ssn_correction_migration_flag_enabled', True): - queue_messages = self._post_license_with_previous_ssn() + resp = self._post_license_with_previous_ssn() + self.assertEqual(200, resp['statusCode']) + queue_messages = self._license_preprocessing_queue.receive_messages(MaxNumberOfMessages=10) self.assertEqual(1, len(queue_messages)) message = json.loads(queue_messages[0].body) self.assertEqual('123-12-9876', message['previousSSN']) # TODO - remove this test once the LICENSE_SSN_CORRECTION_MIGRATION_FLAG scaffolding is removed # noqa: FIX002 def test_post_licenses_rejects_previous_ssn_when_flag_disabled(self): - from handlers.licenses import post_licenses - - with open('../common/tests/resources/api-event.json') as f: - event = json.load(f) - - event['requestContext']['authorizer']['claims']['scope'] = 'openid email aslp/readGeneral oh/aslp.write' - event['pathParameters'] = {'compact': 'aslp', 'jurisdiction': 'oh'} - with open('../common/tests/resources/api/license-post.json') as f: - license_data = json.load(f) - license_data['previousSSN'] = '123-12-9876' - event['body'] = json.dumps([license_data]) - - event = self._create_signed_event(event) - with patch('handlers.licenses.ssn_correction_migration_flag_enabled', False): - resp = post_licenses(event, self.mock_context) + resp = self._post_license_with_previous_ssn() self.assertEqual(400, resp['statusCode']) body = json.loads(resp['body']) diff --git a/backend/compact-connect/tests/smoke/config.py b/backend/compact-connect/tests/smoke/config.py index 5402889182..28f319b286 100644 --- a/backend/compact-connect/tests/smoke/config.py +++ b/backend/compact-connect/tests/smoke/config.py @@ -100,7 +100,7 @@ def test_provider_original_provider_id(self): @property def provider_user_bucket_name(self): - """The provider users S3 bucket, which holds practitioner-uploaded documents.""" + """The provider users' S3 bucket, which holds practitioner-uploaded documents.""" return os.environ['CC_TEST_PROVIDER_USER_BUCKET_NAME'] @property From 7f467ca65508d2b4238526513bb3a29cdfafc1fe Mon Sep 17 00:00:00 2001 From: Landon Shumway Date: Tue, 21 Jul 2026 10:31:25 -0500 Subject: [PATCH 38/41] feedback - add warning to README about incorrect SSNs --- backend/compact-connect/docs/README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/backend/compact-connect/docs/README.md b/backend/compact-connect/docs/README.md index fab8a17348..f68b6de66d 100644 --- a/backend/compact-connect/docs/README.md +++ b/backend/compact-connect/docs/README.md @@ -112,6 +112,10 @@ Yes. CompactConnect is designed to automatically detect and track changes to lic Because accounts are matched on SSN, simply changing the SSN in your state's system and then uploading the corrected license will **not** update the practitioner's existing CompactConnect account. It will create a brand new, separate account under the new SSN and leave the original account (and any privileges tied to it) unchanged. If a license was previously uploaded with an incorrect SSN, use the `previousSSN` field (see the field table above) when uploading the corrected SSN so CompactConnect can migrate the practitioner's existing account instead of creating a duplicate. +> **⚠️ Verify SSNs before you upload.** The SSN is the sole identifier CompactConnect uses to match a license to a practitioner's account, and every downstream consequence of an upload (account creation, privilege eligibility, public lookup, etc.) follows from it. Uploading an incorrect SSN is not a low-risk mistake to leave unaddressed, as it silently creates or attaches records to the wrong account, fragmenting the practitioner's licensure history and leaving privileges tied to whichever account was in place at the time they were purchased. +> +> Correcting an SSN with `previousSSN` is itself a significant action which migrates (moves) the affected license and any privileges purchased against it to the account associated with the corrected SSN and forces the practitioner to re-register if they have already registered under the account with the incorrect SSN. Because of this, during onboarding testing before a state begins uploading licenses into the production account, it is imperative that the state verify there is no issue with the internal process or data pipeline that sends licensure data to CompactConnect which could result in incorrect or inconsistent SSNs being uploaded, so that this feature is not relied upon to routinely correct otherwise avoidable upload errors. + ### Which of these license values will be publicly visible? The following license fields are publicly visible through CompactConnect's public lookup endpoints: From 5fa30af6391e03557fa42b7876d1975c3b4cb9b6 Mon Sep 17 00:00:00 2001 From: Landon Shumway Date: Tue, 21 Jul 2026 11:00:11 -0500 Subject: [PATCH 39/41] formatting/linter --- .../python/common/tests/unit/test_provider_record_util.py | 2 +- .../tests/function/test_handlers/test_ingest.py | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/backend/compact-connect/lambdas/python/common/tests/unit/test_provider_record_util.py b/backend/compact-connect/lambdas/python/common/tests/unit/test_provider_record_util.py index 4a008f5d82..b8375414c0 100644 --- a/backend/compact-connect/lambdas/python/common/tests/unit/test_provider_record_util.py +++ b/backend/compact-connect/lambdas/python/common/tests/unit/test_provider_record_util.py @@ -1341,7 +1341,7 @@ class TestGetRecordsAssociatedWithLicense(TstLambdas): """Coverage for ProviderUserRecords.get_records_associated_with_license. The SSN-correction migration (migrate_provider_for_ssn_correction) relies on this method to return the - target license together with EVERY record that depends on it across MULTIPLE privileges so nothing is + target license together with EVERY record that depends on it across MULTIPLE privileges so nothing is silently left behind on the old provider. Each test asserts that one record type comes back in full. A single rich record set is generated once in setUp: the target license (oh / slp) with two privileges diff --git a/backend/compact-connect/lambdas/python/provider-data-v1/tests/function/test_handlers/test_ingest.py b/backend/compact-connect/lambdas/python/provider-data-v1/tests/function/test_handlers/test_ingest.py index d340b8c6d8..5013f595f8 100644 --- a/backend/compact-connect/lambdas/python/provider-data-v1/tests/function/test_handlers/test_ingest.py +++ b/backend/compact-connect/lambdas/python/provider-data-v1/tests/function/test_handlers/test_ingest.py @@ -3,9 +3,8 @@ from unittest.mock import MagicMock, patch from cc_common.data_model.update_tier_enum import UpdateTierEnum -from moto import mock_aws - from common_test.test_constants import DEFAULT_PROVIDER_ID +from moto import mock_aws from .. import TstFunction From b7948e6008aeb909b678d55338e5eb6cc658c626 Mon Sep 17 00:00:00 2001 From: Landon Shumway Date: Wed, 22 Jul 2026 09:19:02 -0500 Subject: [PATCH 40/41] feedback - update test method name --- .../tests/function/test_handlers/test_ingest.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/backend/compact-connect/lambdas/python/provider-data-v1/tests/function/test_handlers/test_ingest.py b/backend/compact-connect/lambdas/python/provider-data-v1/tests/function/test_handlers/test_ingest.py index 5013f595f8..d49881b976 100644 --- a/backend/compact-connect/lambdas/python/provider-data-v1/tests/function/test_handlers/test_ingest.py +++ b/backend/compact-connect/lambdas/python/provider-data-v1/tests/function/test_handlers/test_ingest.py @@ -965,7 +965,7 @@ def _create_old_cognito_user(self): UserAttributes=[{'Name': 'email', 'Value': self.OLD_REGISTERED_EMAIL}], ) - def _does_old_cognito_user_exists(self) -> bool: + def _does_old_cognito_user_exist(self) -> bool: from botocore.exceptions import ClientError try: @@ -1075,7 +1075,7 @@ def test_full_migration_deletes_cognito_user(self): resp = self._run_ingest_with_previous_provider_id() self.assertEqual({'batchItemFailures': []}, resp) - self.assertFalse(self._does_old_cognito_user_exists()) + self.assertFalse(self._does_old_cognito_user_exist()) def test_full_migration_sends_reregistration_email(self): self._put_old_provider_records() @@ -1108,7 +1108,7 @@ def test_partial_migration_keeps_cognito_user_and_sends_no_email(self): self.assertNotIn('militaryAffiliation', new_record_types) # the old Cognito user remains and no re-registration email was sent - self.assertTrue(self._does_old_cognito_user_exists()) + self.assertTrue(self._does_old_cognito_user_exist()) self._mock_send_reregistration_email.assert_not_called() def test_no_op_migration_still_ingests_license_normally(self): From df1f7c97ec89f6a587bdabe99ed93937afb7d4e4 Mon Sep 17 00:00:00 2001 From: landonshumway-ia Date: Wed, 22 Jul 2026 09:46:39 -0500 Subject: [PATCH 41/41] Update backend/compact-connect/lambdas/python/common/tests/function/test_data_client_ssn_correction.py Co-authored-by: Joshua Kravitz --- .../common/tests/function/test_data_client_ssn_correction.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/compact-connect/lambdas/python/common/tests/function/test_data_client_ssn_correction.py b/backend/compact-connect/lambdas/python/common/tests/function/test_data_client_ssn_correction.py index a8554c197d..c10f721223 100644 --- a/backend/compact-connect/lambdas/python/common/tests/function/test_data_client_ssn_correction.py +++ b/backend/compact-connect/lambdas/python/common/tests/function/test_data_client_ssn_correction.py @@ -750,7 +750,7 @@ def _fail_on_target_license_delete(**kwargs): self.assertEqual(1, len(self._get_records_of_type(DEFAULT_PROVIDER_ID, 'license'))) # replay: succeeds, tears the old provider down, and still reports the registered email for the - # cqller to delete the Cognito account and send the email notification + # caller to delete the Cognito account and send the email notification # (the bug this ordering fixes was losing that email on replay) result = self._migrate()