Facility
APIs for managing facility entities
Create a facility for the tenant
Creates a CoreFacility and its TenantFacility association for the tenant (create path; certifyFacilityId is not yet known). Use this to establish a new facility record; use PUT /facilities/{id} instead when the certifyFacilityId is already known. A crosswalk ID is generated from the facility's tenant-scoped identity (NPI+TIN, NCPDP, externalId, or an address hash, depending on tenant identity configuration) β this crosswalk, not any single submitted field, is what the platform treats as the facility's unique identity key. Request body is validated against the facility-upsert JSON Schema and, for tenants configured with isFacilityExternalIdAsCrosswalk=true, against additional externalId/address-hash uniqueness rules (see FacilityValidationService). The primary address, if present, may be standardized via the address cleanser before being persisted. When facility.upsert.sync-survivorship.enabled is true (default), a synchronous (not queued/async) survivorship call is also triggered to the core data layer. Returns HTTP 201 with an ID map only (crosswalkId, corefacilityId, tenantfacilityId, and related batch-alias IDs) β not the full facility object; call GET /facilities/{id} afterwards to read back the created record. See PUT /facilities/{id} for the update path. Tenant UDF values go under `userDefinedFields`, keyed by the field names returned by `GET /udf-schemas/entity-type/facility`; UDF keys sent at the top level of the payload are rejected with 400.
Search facilities by filter criteria with pagination
Returns a paginated list of facilities matching the supplied filter criteria. Use this to search or list facilities for a tenant; use GET /facilities/{id} instead when you already know the facility identifier. Supports offset-based pagination via page/size. Search by Roster (CP-35452): the rosterIds query param filters to facilities belonging to one or more selected rosters; combines with other filters using AND logic. Filtering by credentialingCycle, nextCredentialingDate, credentialingStatus, and User Defined Fields (UDFs) is supported through the filter parameter. UDF fields can be filtered using the path format: `data.userDefinedFields.{fieldName}` for simple fields or `data.userDefinedFields.{parent}.{child}` for nested fields.
Bulk upload supporting documents to multiple facilities
Uploads one or more files exactly once each and links them as shared supporting-document attachments (source: 'Bulk') to every selected facility. Facilities may be selected explicitly via certifyFacilityIds (JSON string form field), or via isSelectAll=true plus a filter JSON string (same encoding as GET /facilities). When isSelectAll is true, certifyFacilityIds is ignored; when false, filter is ignored. Target set size is capped (default 1000; see api-layer.facility.bulk-document-upload.max-facilities). HTTP 200 is returned for both full and partial success: per-file and per-facility outcomes are reported independently in the response body. When the feature kill switch is off, the endpoint returns 404 with an empty body (CP-35450).
Correct a facility-level specialty record
Updates a TenantFacilitySpecialty record (Level 1) by replacing the tenant_specialty_id, updating the effective date, or both. At least one of newTenantSpecialtyId or effectiveDate must be provided. Use this for data-correction scenarios where a facility's direct specialty association needs to be updated without triggering credentialing lifecycle events β it does not propagate changes to group, location, or network specialty records. For corrections that need to cascade to group-level records, use PATCH /facilities/tenant-group-facility-specialties/specialty instead. Does not trigger termination events or lifecycle changes. Returns the raw updated record (id, tenantSpecialtyId, data) as an untyped JSON object, not a curated response DTO.
Correct the specialty at the group-facility-location level
Resolves the FK chain from TenantGroupFacilityLocationSpecialty (Level 4) to the parent TenantGroupFacilitySpecialty (Level 3) and replaces its tenant_specialty_id by updating that Level-3 record. Use this for data-correction scenarios where a group-facility-location specialty record is linked to the wrong specialty and you need to scope the correction to a single location without cascading across all locations β for the broader group-facility correction, use PATCH /facilities/tenant-group-facility-specialties/specialty instead. Does not trigger termination events. Because this operation delegates to the Level-3 update, the response body is shaped like the Level-3 record (id, tenantSpecialtyId, data) β not a Level-4-specific record.
Correct the specialty at the group-facility-network level
Resolves the FK chain from TenantGroupFacilityNetworkSpecialty (Level 5) to the parent TenantGroupFacilitySpecialty (Level 3) and replaces its tenant_specialty_id by updating that Level-3 record. Use this for data-correction scenarios where a group-facility-network specialty record is linked to the wrong specialty and you need to scope the correction to a single network association without cascading across all locations β for the broader group-facility correction, use PATCH /facilities/tenant-group-facility-specialties/specialty instead. Does not trigger termination events. Because this operation delegates to the Level-3 update, the response body is shaped like the Level-3 record (id, tenantSpecialtyId, data) β not a Level-5-specific record.
Correct the specialty on a group facility specialty record
Replaces the tenant_specialty_id on a TenantGroupFacilitySpecialty record (Level 3) in data-correction mode. Use this for data-correction scenarios where a group-level facility specialty association is incorrect and the correction must propagate to its child records β cascades to the group-facility-location and group-facility-network specialty records (Levels 4, 5) that are children of this association via FK chain. For correcting only a facility-level (non-group) record, use PATCH /facilities/tenant-facility-specialties/specialty instead. Does not trigger termination events. Returns the raw updated Level-3 record (id, tenantSpecialtyId, data) as an untyped JSON object.
Globally terminate one or more facilities
Terminates each facility without the caller naming a group, network or location: every active group, network and location relationship across all of the facility's locations ends and the facility's tenant-level record is terminated with the given date and reason (and, when supplied, terminationInformedDate: the date the termination notice was provided, stored beside the termination date on the same records and restored by a rollback). Practitioner relationships are never changed. Supply certifyLocationId to terminate only that location (the group affiliation ends too when it was the facility's last location in it, and the tenant-level record stays active). Relationships that already carry a termination (any termination date, past, today or future) are left exactly as they were and reported as skipped: a future-dated termination is a regular termination, and a wrong date is corrected by rolling the request back and terminating again. Open credentialing workflows are cancelled immediately, whatever the effective date; the response says how many. The cancellation is the Credentialing API's own operation and is simply called: if it passes the termination stands, if it fails (for example for a tabled workflow) the termination is reversed and the error carries the cancel failure's message (502 for a single facility, because a dependency failed; several facilities report it per facility as a failed item). Rolling a request back restores the records only; it does not reopen workflows. A single facility is processed synchronously and an already-terminated facility is rejected with 409; several facilities succeed or fail independently and the response reports each. Use the returned requestId with POST /facilities/terminations/{requestId}/rollback to undo exactly what this request changed. A webhook (facility.global_termination.completed) is sent once per terminated facility.
Roll back a facility global termination request
Restores exactly the relationship rows the given termination request changed, to their exact prior values, and nothing else. All-or-nothing: if any row has since changed or cannot be safely restored, nothing is touched and 409 names the blocking rows. Rows the request skipped because they were already terminated are not touched. Does not reinstate NPDB enrollment or monitoring (these do not apply to facilities) and does not reopen cancelled credentialing workflows.
Update effective dates for facility-group association
Updates the effective start date and/or termination date for a facility-group association. Use this to adjust when a facility's membership in a group begins or ends; use PUT .../groups/{certifyGroupId}/networks/{networkId}/effective-date to independently adjust a single network enrollment date within that group association instead. Optionally cascades the date change to network participation based on the cascadeToNetworkParticipation flag β this cascade is best-effort: if it fails, the failure is swallowed and does not fail the request or change the response, so a 200 does not guarantee the cascade itself succeeded. If terminationDate is provided, it must be after the effectiveDate. Mirrors the practitioner-group precedent endpoint with one deliberate divergence: the request body carries an optional changeReason field per facility-resource convention. Returns the raw updated TenantGroupFacility record as an untyped JSON object, not a curated response DTO.
Update effective date for a facility-group-network enrollment
Updates the effective start date and/or termination date for a single TenantGroupFacilityNetwork (TGFN) leaf row independently of its parent TenantGroupFacility association. Use this when you need to adjust a network enrollment date without changing the parent facility-group association dates; use PUT .../groups/{certifyGroupId}/effective-date to change the parent facility-group dates (which can optionally cascade to network records) instead. The service-layer enforces a cross-level invariant: the request effectiveDate cannot precede the parent facility-group effectiveDate. Returns the raw updated TenantGroupFacilityNetwork record as an untyped JSON object, not a curated response DTO. CP-28250 phase-2 / sub-task CP-28250-Phase2-BE.
Add a new location to a facility
Creates a new location record and associates it with the specified facility. Use this to register a new physical location under an existing facility; to update an existing location's attributes, use PUT /facilities/{certifyFacilityId}/locations/{certifyLocationId} instead. The facility identified by certifyFacilityId must already exist. The request body is validated against a JSON schema; a 400 is returned if validation fails. Returns HTTP 201 with a batch alias ID map (e.g. corelocation, tenantfacilitylocation, and related address IDs) plus a message field β not the full created location record; call the location's read endpoint afterwards if you need the complete record.
Update a facility location (simple CoreLocation fields)
Updates allowed CoreLocation fields for a location linked to the facility. Requires consentAcknowledged: true. Includes contacts, office hours, ADA, limitations, isPrimaryLocation (primary indicator), optional userDefinedFields (full replace of that object on CoreLocation data), and optional specialityDetails (same shape as add-location; syncs TenantFacilityLocationSpecialty via replace semantics). When api-layer.facility.location-tfl-contacts.enabled is on, contacts is stored on the facility's TenantFacilityLocation (data.contacts, the list replaces the stored one; [] clears it) instead of CoreLocation, needs no crosswalkId, and is validated with facility-location-tfl-contacts.schema.json (flat objects, string or null values, at most 50 items, an all-blank item is rejected with 400 INVALID_CONTACT). Location name and service address cannot be changed via this API (crosswalk safety). The authenticated user is the actor (updated_by); scoped effective-date PATCH and cascade to group/network denormalized rows are not part of this endpoint.
Update effective date for facility location network participation (TGLFN)
Updates the effective start date on TenantGroupLocationFacilityNetwork rows for this facility at the given practice location and network. Updates all intrinsic group memberships (no group picker). Does not create rows or modify TGLN/TGFN. CP-29886.
Terminate a facility location (full or network-only)
Supports two modes controlled by which dates are provided in the request body: (1) Full termination β provide facilityTerminationDate to terminate the facilityβlocation relationship and cascade to all network/plan participation records in one atomic batch. (2) Network-only termination β omit facilityTerminationDate and provide networkTerminationDate. Terminates TenantGroupLocationFacilityNetwork (TGLFN) for this facility at the location and cascades to plan and practitioner network participation (TenantGroupLocationNetworkPlan, TenantGroupLocationPractitionerNetwork, TenantGroupPractitionerNetwork). Does not write TenantGroupLocationNetwork (TGLN). The facilityβlocation record itself remains active. Optionally scope to a single network by providing networkId. At least one date field is required. Does not terminate TenantGroupFacilityNetwork (whole-facility network enrollment).
Preview facility location termination impact
Returns a read-only pre-flight summary of what would be affected if the specified facility location were terminated: the location name, address summary, the networks (and optional plan ids) the location participates in via the facility, and whether the location is already terminated. Use this before calling POST .../locations/{certifyLocationId}/terminate to understand the scope of the change β this endpoint makes no changes itself.
Update location-network billing NPI and participation date for a facility location
As of CP-36570: updates NPI and/or participation date on TenantGroupLocationNetwork and TenantGroupLocationFacilityNetwork for this facility at the specified location-network, writing the `addressType2Npi` array on both (supports multiple NPIs per location-network). Preferred: pass `npiEntries` with the location-network's complete NPI set β it REPLACES the stored array, so an NPI omitted from the list is deleted (the only way to remove one) and an entry without `npiParticipationDate` is stored with no date. Deprecated: the scalar `npi`/`npiParticipationDate` fields still work (repeat the call to add/update additional NPIs; `npi` is required to disambiguate `npiParticipationDate` when more than one NPI exists) but are mutually exclusive with `npiEntries` in the same request.
Roll back a facility termination by its exact date and reason
For a termination that has no global termination request id (it predates the global termination framework, so there is no provenance to restore from). Clears the termination from every relationship row of the facility whose termination date AND reason match the request exactly. Fails closed: both fields are required, and if any terminated row has a blank reason (so it cannot be attributed to one event) or no row matches, nothing is changed. Rows from a different termination event are left alone. Does not reinstate NPDB enrollment or monitoring (these do not apply to facilities) and does not reopen cancelled credentialing workflows.
Update a facility and its tenant association
Updates the CoreFacility identified by the path certifyFacilityId and its TenantFacility association for the tenant (update path). Use this when the certifyFacilityId is already known; use POST /facilities instead to create a new facility. Stored identity fields not present in the payload are merged in before validation, then the same crosswalk identity used at create time is re-resolved and updated β retrying this call with the same payload is safe and updates the same rows (idempotent). Request body is validated against the same facility-upsert JSON Schema and tenant-specific rules used by POST /facilities. Returns HTTP 201 (not 200) on success β this matches the create endpoint's status code by historical convention rather than by REST semantics for an update; treat any 2xx as success rather than branching on the exact code. Response body is the same ID map as create (crosswalkId, corefacilityId, tenantfacilityId, etc.), not the full facility object. See POST /facilities for the create path.
Retrieve a single facility by ID
Returns the complete facility record for the given certify facility id, scoped to the calling tenant, as it exists in the Facility Operational View. Use this when you already know the facility identifier; use GET /facilities to search or list facilities instead. Preconditions: supply the tenant-id header; id must be a valid Certify facility identifier for that tenant. Returns an untyped JSON object built from the CoreFacility Operational View data β a top-level id plus name, npi, tin, dba, facilityType, externalId, delegationStatus, businessPurpose, userDefinedFields, addresses, emails, phoneNumbers, and array fields for licenses, certifications, insurances, accreditations, specialties, sanctions, siteVisits, medicareStarRatings, appVerifications, performanceIndicators, npdbReports, nppes, and supportingDocuments β plus credentialing fields (credentialingStatus, nextCredentialingDate, credentialingCycle, credentialingDecisionDate, credentialingStatusLastUpdatedDate) merged in at read time. When facility global termination is enabled, the tenant-level termination fields terminationDate, terminationReason and terminationInformedDate (the date the termination notice was provided) are merged in too, only when the tenant record has them, and win over a stale Operational View copy. Soft-deleted entries are filtered out of accreditations, insurances, certifications, and licenses. Locations are not embedded here β use GET /facilities/{id}/relationships/all for a facility's group, network, location, and practitioner relationships.
Replace all accreditations for a facility
Replaces the complete set of accreditations for the specified facility in the Operational View (the live, editable roster data). Records not present in the new list are removed β sending the same list twice produces the same result (idempotent). Use this to bulk-synchronize a facility's accreditation data; this endpoint does not affect snapshots captured inside active credentialing workflow runs β those are managed separately through the credentialing workflow. Returns only a confirmation message, not the replaced records or their IDs.
Replace all certificates for a facility
Replaces the complete set of certificates for the specified facility in the Operational View (the live, editable roster data). Records not present in the new list are removed β sending the same list twice produces the same result (idempotent). Use this to bulk-synchronize a facility's certificate data; this endpoint does not affect snapshots captured inside active credentialing workflow runs β those are managed separately through the credentialing workflow. Returns only a confirmation message, not the replaced records or their IDs.
Replace all insurances for a facility
Replaces the complete set of insurance records for the specified facility in the Operational View (the live, editable roster data). Records not present in the new list are removed β sending the same list twice produces the same result (idempotent). Use this to bulk-synchronize a facility's insurance data; this endpoint does not affect snapshots captured inside active credentialing workflow runs β those are managed separately through the credentialing workflow. Returns only a confirmation message, not the replaced records or their IDs.
Replace all licenses for a facility
Replaces the complete set of license records for the specified facility in the Operational View (the live, editable roster data). Records not present in the new list are removed β sending the same list twice produces the same result (idempotent). Use this to bulk-synchronize a facility's license data; this endpoint does not affect snapshots captured inside active credentialing workflow runs β those are managed separately through the credentialing workflow. Returns only a confirmation message, not the replaced records or their IDs.
Associate a facility with a network
Associates a facility with a network following the roster ingestion pattern. The network must belong to a group (groupId provided in request body). If the facility is not associated with the group, the group association will also be created simultaneously. Validates that the network belongs to the specified group before creating the association. Supports three request shapes, each with different conflict behavior: (1) legacy single networkId β always upserts the TenantGroupFacilityNetwork relationship with no pre-check, silently updating an existing association rather than rejecting it; (2) bulk networkIds or (3) grouped networkAssociations β both explicitly check for an existing active association per facility-network and per facility-location-network pair first, and return 409 Conflict if one already exists rather than silently updating it. Choose the bulk/grouped shapes when you need to detect pre-existing associations instead of upserting over them.
Terminate a facility's participation in a network
Terminates the facility's enrollment in the specified network from the given termination date. Cascades termination to all associated facility locations (TenantGroupLocationFacilityNetwork) and all facility-practitioners in that network (TenantGroupLocationPractitionerNetwork and TenantGroupPractitionerNetwork). The facility's location enrollments and practitioner associations are preserved β only their network participation is terminated. All operations are executed as a single atomic batch transaction.
Get all relationships for a facility
Returns a single, deeply-nested snapshot of every relationship a facility participates in β groups (groupMemberships[]), the networks and contracts under each group (facilityNetworks[]), the facility's locations both independent of any group (tenantFacilityLocations[]) and nested under each group (groupMemberships[].facilityLocations[] with their own network/plan participation), and practitioners. Use this to get a comprehensive view of a facility's associations in a single call instead of querying individual relationship endpoints separately. Note: practitioners are only present nested under groupMemberships[].facilityLocations[].practitionersAtLocation[] (i.e. scoped to a specific group-location) β there is no facility-wide practitioner roster field on this endpoint; a prior facility-wide practitioner array was intentionally removed to avoid oversized rows, so consumers needing the full practitioner roster for a facility must call a dedicated practitioner endpoint instead.
List TGFL-anchored location associations for a facility
Returns a paginated list of all tenant_group_facility_locations (TGFL) associations for the given facility. Each item in data[] contains the TGFL anchor row plus flat sibling entities: tenantGroupFacility, tenantFacility, tenantGroup, tenantGroupLocation, groupLocation, locationEntityAddresses, and tenantGroupLocationFacilityNetworks (TGLFN). All entity foreign-key references are wrapped as objects ({id, data}) to allow future schema evolution without breaking consumers. certifyGroupId: restrict to TGFLs whose group resolves to this certify group id. networkId: restrict to TGFLs that have a matching TGLFN row for this network id (EXISTS semantics β TGFL rows are never duplicated); the TGLFN array on each item is also filtered to the requested network when set. locationSearch: restrict to TGFLs whose resolved location name contains this text (case-insensitive substring match). Blank/omitted = no filter. Note: operationId retains the legacy plural form for OpenAPI stability; the Java method is getFacilityLocationRelationships.
Batch add and update facility specialties
Applies a batch of specialty updates and additions to a facility in a single request. Use this when you need to modify multiple specialty associations at once; for a single new specialty, use POST /facilities/{id}/specialties instead. Updates modify existing TenantFacilitySpecialty records (specialty FK, effective date, isPrimary) and are applied as one all-or-nothing batch β if that batch call fails, the entire request fails, with no partial-success path for updates. Additions create new specialty associations at the facility level and are processed independently β a failure on one addition is captured in the response's errors array and does not stop or fail the other additions.
Add a specialty to a facility
Adds a new specialty association to an existing facility. Use this to associate a single specialty with a facility; to apply multiple specialty changes in one call, use PATCH /facilities/{id}/specialties instead. Cascade to each additional level is not a separate flag β it is triggered purely by which optional ID fields are present in the request body: certifyLocationId alone cascades to the facility-location level; certifyGroupId cascades to the group level; certifyLocationId together with locationSpecialtyEffectiveDate cascades to the group-location level; certifyNetworkId cascades to the group-network level. Cascade levels are attempted independently β a failure on one level does not roll back or block the others; any level that fails is reported in the notCreated array of the response rather than failing the whole request.
Replace a facility's supporting documents
Replaces the complete set of supporting documents for the specified facility β this is a full-array replace, not a per-item update-or-delete operation. There is no action field on the request items; every submitted item is written as-is, and any existing document not present in the submitted array is removed. To remove one document, resend the full array with that item omitted; to remove all documents, submit an empty array. The underlying stored file is never deleted from GCS by this call β only the metadata entry is removed from the facility's document list, so the file itself remains in storage. To attach a new document without affecting existing ones, use POST /facilities/{id}/supporting-documents instead, which appends rather than replaces.
Attach a new supporting document to a facility
Uploads and attaches a new supporting document to the specified facility. Use this to add a document for the first time; to update or remove an existing document, use PUT /facilities/{id}/supporting-documents instead (see that endpoint's description for its full-array-replace semantics). The file is uploaded as multipart/form-data and its bytes are read directly into GCS by this service β there is no pre-signed-URL pattern. There is no MIME-type allowlist; size is bounded only by the service's global request-body limits (currently 500MB body / 250MB per form attribute), not by a document-specific limit. fileType is a free-form optional string with no enforced enum or whitelist β callers may send any value. Returns HTTP 201 with only a message and the stored fileName β no document ID or file URL is returned by this endpoint.