openapi: 3.2.0
info:
description: API for Certify application
title: Certify API Layer Facility API
version: 1.0.0
servers:
- url: http://localhost:9000
description: Local Development Server
- url: https://api-service.staging.certifyos.com
description: Staging Server
- url: https://api-service.internal.certifyos.com
description: Internal Server
- url: https://api-service.test.certifyos.com
description: Test Server
- url: https://api-service.demo.certifyos.com
description: Demo Server
- url: https://api-service.certifyos.com
description: Production Server
tags:
- name: Facility
description: APIs for managing facility entities
paths:
/facilities:
post:
summary: Create a new facility
description: Upserts CoreFacility from payload and creates/updates TenantFacility for the tenant.
operationId: facilityUpsert
tags:
- Facility
parameters:
- name: tenant-id
in: header
schema:
type: string
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/JsonNode'
required: true
responses:
'201':
description: Facility successfully created or updated
content:
application/json:
schema:
$ref: '#/components/schemas/JsonNode'
'400':
description: Invalid request body
content:
application/json: {}
'401':
description: Unauthorized - Authentication required
content:
application/json: {}
'403':
description: Forbidden - Insufficient permissions
content:
application/json: {}
'500':
description: Internal server error
content:
application/json:
schema:
$ref: '#/components/schemas/ApiError1'
security:
- jwt: []
get:
summary: Find Facility by filter criteria with pagination
description: 'Returns a paginated list of all Facilities. Supports both offset-based (page/size) pagination. 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.'
operationId: facilityFindMany
tags:
- Facility
parameters:
- description: Filter by delegation status (Direct or Delegated)
name: delegationStatus
in: query
schema:
type: string
- description: Filter by external ID
name: externalId
in: query
schema:
type: string
- description: "Filter criteria as JSON (must be URL encoded). Multiple filters are combined with AND logic.\n\n**Filterable Fields:**\n\n**Credentialing Fields:**\n- `credentialingCycle` (String): Credentialing cycle type - `INITIAL` or `RECREDENTIALING` (eq, neq, in, nin, contains)\n- `nextCredentialingDate` (Date): Next credentialing date in ISO format `YYYY-MM-DD` (eq, neq, gt, gte, lt, lte, in, nin)\n- `credentialingStatus` (String): Current credentialing status (eq, neq, in, nin, contains)\n\n**User Defined Fields (UDFs):**\n- `userDefinedFields.*` (Dynamic): Any user-defined field stored in `data.userDefinedFields` object\n - **Field Path Format**: Use `userDefinedFields.{fieldName}` or `userDefinedFields.{fieldName}.{subfield}` for nested fields\n - **Important**: Clients should use only the `userDefinedFields.*` format (without `data.` prefix).\n The backend automatically transforms `userDefinedFields.*` to `data.userDefinedFields.*` internally.\n - **Supported Operations** (varies by field type):\n - **String fields**: `eq`, `neq`, `in`, `nin`, `contains`\n - **Numeric fields**: `eq`, `neq`, `gt`, `gte`, `lt`, `lte`, `in`, `nin`\n - **Array fields**: `eq`, `in` (checks if array contains the value)\n - **Boolean fields**: `eq`, `neq`\n - **Examples**: `userDefinedFields.market`, `userDefinedFields.location.region`\n\n**Other Data Fields:**\n- `data.*` (Any): Any data field (eq, neq, in, nin, contains, gt, gte, lt, lte)\n\n**Operations:**\n- **String**: `eq`, `neq`, `in`, `nin`, `contains`\n- **Date**: `eq`, `neq`, `gt`, `gte`, `lt`, `lte`, `in`, `nin`\n- **Numeric**: `eq`, `neq`, `gt`, `gte`, `lt`, `lte`, `in`, `nin`\n- **Array**: `eq`, `in` (checks if array contains the value)\n- **Boolean**: `eq`, `neq`\n\n**Examples:**\n\n**Credentialing Filters:**\n```json\n{\"credentialingCycle\":{\"eq\":\"INITIAL\"}}\n{\"nextCredentialingDate\":{\"gte\":\"2025-01-01\"}}\n{\"credentialingStatus\":{\"eq\":\"CRED_APPROVED\"}}\n{\"credentialingCycle\":{\"eq\":\"INITIAL\"},\"nextCredentialingDate\":{\"gte\":\"2025-01-01\",\"lte\":\"2025-12-31\"}}\n{\"credentialingStatus\":{\"in\":[\"CRED_APPROVED\",\"PSV_READY\"]},\"credentialingCycle\":{\"eq\":\"RECREDENTIALING\"}}\n```\n\n**User Defined Fields (UDFs):**\n```json\n{\"userDefinedFields.market\":{\"eq\":\"MARKET1\"}}\n{\"userDefinedFields.market\":{\"in\":[\"MARKET1\",\"MARKET2\"]}}\n{\"userDefinedFields.location.region\":{\"eq\":\"NORTH\"}}\n```\n**Note**: Clients must use the `userDefinedFields.*` format (without `data.` prefix).\nThe backend automatically transforms these to `data.userDefinedFields.*` internally.\n\n**Combined Filters:**\n```json\n{\"credentialingStatus\":{\"eq\":\"CRED_APPROVED\"},\"userDefinedFields.market\":{\"eq\":\"MARKET1\"}}\n{\"credentialingCycle\":{\"eq\":\"INITIAL\"},\"nextCredentialingDate\":{\"gte\":\"2025-01-01\"},\"userDefinedFields.market\":{\"in\":[\"MARKET1\",\"MARKET2\"]}}\n```\n\n**Notes:**\n- Filter must be URL encoded when sent as query parameter\n- `tenantId` from header always takes precedence (cannot be overridden)\n- Credentialing fields are derived from the most recent CRED_APPROVED timeline event\n- Date format must be ISO 8601: `YYYY-MM-DD`\n- String operations are case-sensitive\n- UDF field names are case-sensitive - use exact field names as stored in your data\n- UDF fields are dynamically supported - any field under `userDefinedFields.*` can be filtered\n (use `userDefinedFields.*` format, not `data.userDefinedFields.*`)\n- UDF nested fields are supported using dot notation: `userDefinedFields.parent.child`\n"
name: filter
in: query
schema:
type: string
- description: Filter by line of business (comma-separated list, e.g., MEDICARE,MEDICAID)
name: lineOfBusiness
in: query
schema:
type: string
- description: Filter by facility name
name: name
in: query
schema:
type: string
- description: 'Filter by facility NPI. May contain one or more values for bulk search (copy-paste): separate with comma, newline, tab, semicolon, or space. When one or more NPI values are provided via this parameter, the response includes optional identifiersSearched, matchedCount, notFoundCount, unmatchedIdentifiers. When the query parameter is omitted (standard list request), those fields are null.'
name: npi
in: query
schema:
type: string
- description: Page number for offset-based pagination (0-based index). Defaults to 0 if not specified.
name: page
in: query
schema:
type: integer
format: int32
default: '0'
- description: Number of items per page in offset-based pagination. Defaults to 10 if not specified.
name: size
in: query
schema:
type: integer
format: int32
default: '10'
- description: Filter by facility TIN
name: tin
in: query
schema:
type: string
- name: tenant-id
in: header
schema:
type: string
responses:
'200':
description: List of Facilities
content:
application/json:
schema:
$ref: '#/components/schemas/PagedResponse'
'401':
description: Unauthorized - Authentication required
content:
application/json: {}
'403':
description: Forbidden - Insufficient permissions
content:
application/json: {}
'500':
description: Internal server error
content:
application/json:
schema:
$ref: '#/components/schemas/ApiError1'
security:
- jwt: []
/facilities/tenant-facility-specialties/specialty:
patch:
summary: Update a tenant facility specialty record
description: 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. Does not trigger termination events or lifecycle changes.
operationId: updateTenantFacilitySpecialty
tags:
- Facility
parameters:
- name: tenant-id
in: header
schema:
type: string
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/CorrectFacilitySpecialtyRequest'
required: true
responses:
'200':
description: Specialty updated successfully
content:
application/json:
schema:
$ref: '#/components/schemas/JsonNode'
'400':
description: Bad request - Invalid input
content:
application/json: {}
'401':
description: Unauthorized - Authentication required
content:
application/json: {}
'403':
description: Forbidden - Insufficient permissions
content:
application/json: {}
'404':
description: Tenant facility specialty not found
content:
application/json: {}
'409':
description: Conflict - Facility already has this specialty
content:
application/json: {}
'500':
description: Internal server error
content:
application/json: {}
security:
- jwt: []
/facilities/tenant-group-facility-location-specialties/specialty:
patch:
summary: Correct the specialty at the group-facility-location level
description: Resolves the FK chain from TenantGroupFacilityLocationSpecialty (Level 4) to the parent TenantGroupFacilitySpecialty (Level 3) and replaces its tenant_specialty_id. Does not trigger termination events.
operationId: correctTenantGroupFacilityLocationSpecialty
tags:
- Facility
parameters:
- name: tenant-id
in: header
schema:
type: string
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/CorrectGroupFacilityLocationSpecialtyRequest'
required: true
responses:
'200':
description: Specialty corrected successfully
content:
application/json:
schema:
$ref: '#/components/schemas/JsonNode'
'400':
description: Bad request - Invalid input
content:
application/json: {}
'401':
description: Unauthorized - Authentication required
content:
application/json: {}
'403':
description: Forbidden - Insufficient permissions
content:
application/json: {}
'404':
description: Group facility location specialty not found
content:
application/json: {}
'409':
description: Conflict - Group facility already has this specialty
content:
application/json: {}
'500':
description: Internal server error
content:
application/json: {}
security:
- jwt: []
/facilities/tenant-group-facility-network-specialties/specialty:
patch:
summary: Correct the specialty at the group-facility-network level
description: Resolves the FK chain from TenantGroupFacilityNetworkSpecialty (Level 5) to the parent TenantGroupFacilitySpecialty (Level 3) and replaces its tenant_specialty_id. Does not trigger termination events.
operationId: correctTenantGroupFacilityNetworkSpecialty
tags:
- Facility
parameters:
- name: tenant-id
in: header
schema:
type: string
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/CorrectGroupFacilityNetworkSpecialtyRequest'
required: true
responses:
'200':
description: Specialty corrected successfully
content:
application/json:
schema:
$ref: '#/components/schemas/JsonNode'
'400':
description: Bad request - Invalid input
content:
application/json: {}
'401':
description: Unauthorized - Authentication required
content:
application/json: {}
'403':
description: Forbidden - Insufficient permissions
content:
application/json: {}
'404':
description: Group facility network specialty not found
content:
application/json: {}
'409':
description: Conflict - Group facility already has this specialty
content:
application/json: {}
'500':
description: Internal server error
content:
application/json: {}
security:
- jwt: []
/facilities/tenant-group-facility-specialties/specialty:
patch:
summary: Correct the specialty on a group facility specialty record
description: Replaces the tenant_specialty_id on a TenantGroupFacilitySpecialty record (Level 3) in data-correction mode. Cascades to Levels 4, 5, 6 via FK chain. Does not trigger termination events.
operationId: correctTenantGroupFacilitySpecialty
tags:
- Facility
parameters:
- name: tenant-id
in: header
schema:
type: string
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/CorrectGroupFacilitySpecialtyRequest'
required: true
responses:
'200':
description: Specialty corrected successfully
content:
application/json:
schema:
$ref: '#/components/schemas/JsonNode'
'400':
description: Bad request - Invalid input
content:
application/json: {}
'401':
description: Unauthorized - Authentication required
content:
application/json: {}
'403':
description: Forbidden - Insufficient permissions
content:
application/json: {}
'404':
description: Tenant group facility specialty not found
content:
application/json: {}
'409':
description: Conflict - Group facility already has this specialty
content:
application/json: {}
'500':
description: Internal server error
content:
application/json: {}
security:
- jwt: []
/facilities/{certifyFacilityId}/groups/{certifyGroupId}/effective-date:
put:
summary: Update effective dates for facility-group association
description: 'Updates the effective start date and/or termination date for a facility-group association. Optionally cascades the date change to network participation based on the cascadeToNetworkParticipation flag. 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.'
operationId: updateFacilityGroupEffectiveDate
tags:
- Facility
parameters:
- description: Certify facility ID
required: true
name: certifyFacilityId
in: path
schema:
type: string
- description: Certify group ID
required: true
name: certifyGroupId
in: path
schema:
type: string
- name: tenant-id
in: header
schema:
type: string
requestBody:
description: Effective and/or termination dates plus optional cascade flag and change reason
content:
application/json:
schema:
$ref: '#/components/schemas/UpdateFacilityGroupEffectiveDateRequest'
required: true
responses:
'200':
description: Facility-group effective date successfully updated
content:
application/json:
schema:
$ref: '#/components/schemas/JsonNode'
'400':
description: Invalid request body, missing required parameters, or validation failure
content:
application/json:
schema:
$ref: '#/components/schemas/ApiError1'
'401':
description: Unauthorized - Authentication required
content:
application/json:
schema:
$ref: '#/components/schemas/ApiError1'
'403':
description: Forbidden - Insufficient permissions
content:
application/json:
schema:
$ref: '#/components/schemas/ApiError1'
'404':
description: Facility, group, or facility-group association not found
content:
application/json:
schema:
$ref: '#/components/schemas/ApiError1'
'500':
description: Internal server error during request processing
content:
application/json:
schema:
$ref: '#/components/schemas/ApiError1'
security:
- jwt: []
/facilities/{certifyFacilityId}/groups/{certifyGroupId}/networks/{networkId}/effective-date:
put:
summary: Update effective dates for a single facility-group-network association
description: 'Updates the effective start date and/or termination date for a single TenantGroupFacilityNetwork (TGFN) leaf row independently of its parent TenantGroupFacility association. The service-layer enforces a cross-level invariant: the request effectiveDate cannot precede the parent facility-group effectiveDate. CP-28250 phase-2 / sub-task CP-28250-Phase2-BE.'
operationId: updateFacilityGroupNetworkEffectiveDate
tags:
- Facility
parameters:
- description: Certify facility ID
required: true
name: certifyFacilityId
in: path
schema:
type: string
- description: Certify group ID
required: true
name: certifyGroupId
in: path
schema:
type: string
- description: Network ID
required: true
name: networkId
in: path
schema:
type: string
- name: tenant-id
in: header
schema:
type: string
requestBody:
description: Effective and/or termination dates plus optional change reason
content:
application/json:
schema:
$ref: '#/components/schemas/UpdateFacilityGroupNetworkEffectiveDateRequest'
required: true
responses:
'200':
description: Facility-group-network effective date successfully updated
content:
application/json:
schema:
$ref: '#/components/schemas/JsonNode'
'400':
description: Invalid request body, missing required parameters, format violation, or cross-level validation failure (request effectiveDate before parent facility-group effectiveDate)
content:
application/json:
schema:
$ref: '#/components/schemas/ApiError1'
'401':
description: Unauthorized - Authentication required
content:
application/json:
schema:
$ref: '#/components/schemas/ApiError1'
'403':
description: Forbidden - Insufficient permissions
content:
application/json:
schema:
$ref: '#/components/schemas/ApiError1'
'404':
description: Facility, group, or facility-group association not found
content:
application/json:
schema:
$ref: '#/components/schemas/ApiError1'
'500':
description: Internal server error during request processing
content:
application/json:
schema:
$ref: '#/components/schemas/ApiError1'
security:
- jwt: []
/facilities/{certifyFacilityId}/locations:
post:
summary: Add a location to a facility
description: Creates a new location and associates it with the specified facility.
operationId: facilityAddLocation
tags:
- Facility
parameters:
- description: The certify facility ID
required: true
name: certifyFacilityId
in: path
schema:
type: string
- name: tenant-id
in: header
schema:
type: string
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/JsonNode'
required: true
responses:
'201':
description: Location successfully created and associated with facility
content:
application/json:
schema:
$ref: '#/components/schemas/JsonNode'
'400':
description: Invalid request body
content:
application/json: {}
'401':
description: Unauthorized - Authentication required
content:
application/json: {}
'403':
description: Forbidden - Insufficient permissions
content:
application/json: {}
'404':
description: Facility not found
content:
application/json: {}
'500':
description: Internal server error
content:
application/json:
schema:
$ref: '#/components/schemas/ApiError1'
security:
- jwt: []
/facilities/{certifyFacilityId}/locations/{certifyLocationId}:
put:
summary: Update a facility location (simple CoreLocation fields)
description: '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). 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.'
operationId: facilityUpdateLocation
tags:
- Facility
parameters:
- description: The certify facility ID
required: true
name: certifyFacilityId
in: path
schema:
type: string
- description: The certify location ID (CoreLocation)
required: true
name: certifyLocationId
in: path
schema:
type: string
- name: tenant-id
in: header
schema:
type: string
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/JsonNode'
required: true
responses:
'200':
description: Location successfully updated
content:
application/json:
schema:
$ref: '#/components/schemas/JsonNode'
'400':
description: Invalid request body
content:
application/json: {}
'401':
description: Unauthorized - Authentication required
content:
application/json: {}
'403':
description: Forbidden - Insufficient permissions
content:
application/json: {}
'404':
description: Facility or facility–location association not found
content:
application/json:
schema:
$ref: '#/components/schemas/ApiError1'
'500':
description: Internal server error
content:
application/json:
schema:
$ref: '#/components/schemas/ApiError1'
security:
- jwt: []
/facilities/{certifyFacilityId}/locations/{certifyLocationId}/terminate:
post:
summary: Terminate a facility location (full or network-only)
description: '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. Only network/plan participation records (TenantGroupLocationNetwork, TenantGroupLocationFacilityNetwork) are terminated; 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).'
operationId: terminateFacilityLocation
tags:
- Facility
parameters:
- description: Certify facility ID
required: true
name: certifyFacilityId
in: path
schema:
type: string
- description: Certify location ID
required: true
name: certifyLocationId
in: path
schema:
type: string
- name: tenant-id
in: header
schema:
type: string
requestBody:
description: Termination details including facility and network dates, reason, consent
content:
application/json:
schema:
$ref: '#/components/schemas/TerminateFacilityLocationRequest'
required: true
responses:
'200':
description: Facility location terminated
content:
application/json:
schema:
$ref: '#/components/schemas/TerminateFacilityLocationResponse'
'400':
description: Bad request — validation failed, consent missing, already terminated, or no date provided
content:
application/json:
schema:
$ref: '#/components/schemas/ApiError1'
'401':
description: Unauthorized - Authentication required
content:
application/json: {}
'403':
description: Forbidden - Insufficient permissions
content:
application/json: {}
'404':
description: Facility or location not found on this facility
content:
application/json:
schema:
$ref: '#/components/schemas/ApiError1'
'500':
description: Internal server error
content:
application/json:
schema:
$ref: '#/components/schemas/ApiError1'
security:
- jwt: []
/facilities/{certifyFacilityId}/locations/{certifyLocationId}/termination-impact:
get:
summary: Preview facility location termination impact
description: Returns the location name, address summary, networks/plans the location participates in via the facility, and whether the location is already terminated.
operationId: getFacilityLocationTerminationImpact
tags:
- Facility
parameters:
- description: Certify facility ID
required: true
name: certifyFacilityId
in: path
schema:
type: string
- description: Certify location ID
required: true
name: certifyLocationId
in: path
schema:
type: string
- name: tenant-id
in: header
schema:
type: string
responses:
'200':
description: Impact preview
content:
application/json:
schema:
$ref: '#/components/schemas/FacilityLocationTerminationImpactResponse'
'401':
description: Unauthorized - Authentication required
content:
application/json: {}
'403':
description: Forbidden - Insufficient permissions
content:
application/json: {}
'404':
description: Facility or location not found on this facility
content:
application/json:
schema:
$ref: '#/components/schemas/ApiError1'
'500':
description: Internal server error
content:
application/json:
schema:
$ref: '#/components/schemas/ApiError1'
security:
- jwt: []
/facilities/{id}:
put:
summary: Update a facility and its tenant association
description: Update a facility and its tenant association
operationId: facilityUpdate
tags:
- Facility
parameters:
- description: Facility ID
required: true
name: id
in: path
schema:
type: string
- name: tenant-id
in: header
schema:
type: string
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/JsonNode'
required: true
responses:
'201':
description: Facility successfully updated
content:
application/json:
schema:
$ref: '#/components/schemas/JsonNode'
'400':
description: Invalid request body
content:
application/json: {}
'401':
description: Unauthorized - Authentication required
content:
application/json: {}
'403':
description: Forbidden - Insufficient permissions
content:
application/json: {}
'409':
description: Conflict - Initial credentialing workflow already exists
content:
application/json: {}
'500':
description: Internal server error
content:
application/json:
schema:
$ref: '#/components/schemas/ApiError1'
security:
- jwt: []
get:
summary: Get a facility by their ID
description: Returns a single facility by their ID
operationId: facilityGet
tags:
- Facility
parameters:
- description: Facility ID
example: '1234567890'
required: true
name: id
in: path
schema:
type: string
- name: tenant-id
in: header
schema:
type: string
responses:
'200':
description: Facility successfully retrieved
content:
application/json:
schema:
$ref: '#/components/schemas/JsonNode'
'401':
description: Unauthorized - Authentication required
content:
application/json: {}
'403':
description: Forbidden - Insufficient permissions
content:
application/json: {}
'404':
description: Facility not found with the given ID for tenant
content:
application/json:
schema:
$ref: '#/components/schemas/ApiError1'
'500':
description: Internal server error
content:
application/json:
schema:
$ref: '#/components/schemas/ApiError1'
security:
# --- truncated at 32 KB (86 KB total) ---
# Full source: https://raw.githubusercontent.com/api-evangelist/certifyos/refs/heads/main/openapi/certifyos-facility-api-openapi.yml