openapi: 3.2.0 info: title: MedTrainer Public Locations API version: 1.0.0 description: "The MedTrainer Public API allows external integrations to search and manage\ncore directory resources such as locations, divisions, and practitioners.\n\nThis specification describes the available endpoints, request parameters,\npayloads, authentication requirements, and expected responses for clients\nintegrating with the platform.\n\nMost resource endpoints return FHIR-aligned JSON with content type\n`application/fhir+json`. Protected endpoints accept authentication through\neither the `X-API-Key` header or `Authorization: Bearer `.\n\n## Getting started\n\nThis guide walks you through how to generate an API Key from the platform.\nThis key will allow you to authenticate and interact with the available\nAPIs.\n\n## Prerequisites\n\n**Before you begin, make sure you have:**\n\nA valid account and access to the platform\n\nProper permissions to generate an `API Key (Super Admin / Admin)`\n\nLogged into the platform\n\n## Step-by-step\n\n**1. Log in to the platform**\n\nSign in to the MedTrainer platform using your credentials.\n\n**2. Open Organization settings**\n\n**Once you're in:**\n\nOn the left-side menu\n\nClick on **Organization**\n\n**3. Expand “Organization Management” and find the “API keys manager”**\n\n**Inside Organization Settings:**\n\nExpand “Organization Management” menu\n\nHere you’ll find all options to configure the organization\n\nLook for “API keys manager” option and click on it\n\n**4. Create a new API Key group**\n\n**In the “API keys manager” page:**\n\nClick on “Create API key group”\n\nFill the following fields:\n\n`name`\n\n`description`\n\nConfirm the action by clicking the “Create” button.\n\n**5. Activate the API Key “Default”**\n\n**After generating the API key group:**\n\nOn the “API Keys Manager” page, you will see the newly created API key\ngroup, along with a “Default” API key in an “Inactive” status.\n\nTo activate the “Default” API key:\n\nClick on the status to expand the available options.\n\nOnce the list is expanded, select the “Active” status\n\n
\n \n This API key is required and must be available to use the MedTrainer Public APIs\n
\n\n
\n \n Important: Treat your API Key like a password. Keep it private and secure.\n
\n\n
\n" servers: - url: / description: Public API base URL tags: - name: Locations description: Public location lookup and mutation endpoints paths: /api/v1/locations: get: tags: - Locations summary: Search locations description: 'Returns a FHIR `Bundle` with location resources. ' operationId: searchLocations security: - ApiKeyHeader: [] - BearerAuth: [] parameters: - $ref: '#/components/parameters/SearchCount' - $ref: '#/components/parameters/SearchPage' - $ref: '#/components/parameters/SearchElements' responses: '200': description: Location search results content: application/fhir+json: schema: $ref: '#/components/schemas/LocationSearchBundle' examples: default: value: resourceType: Bundle type: searchset total: 3 link: - relation: self url: /api/v1/locations?_count=2&_page=2&_elements=id%2Cname - relation: first url: /api/v1/locations?_count=2&_page=1&_elements=id%2Cname - relation: previous url: /api/v1/locations?_count=2&_page=1&_elements=id%2Cname - relation: last url: /api/v1/locations?_count=2&_page=2&_elements=id%2Cname entry: - resource: resourceType: Location id: LOC-003 name: Location test state: State division: id: DIV-003 name: East Division city: City addressLine: Address 1 zipCode: '33101' phoneNumber: 0987654321 fax: '1234567890' email: test@example.com sendEmail: false enabledCredentialing: false '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/InvalidOrMissingApiKey' '422': $ref: '#/components/responses/SearchValidationError' '429': $ref: '#/components/responses/TooManyRequests' '500': $ref: '#/components/responses/InternalServerError' post: tags: - Locations summary: Create a location operationId: createLocation security: - ApiKeyHeader: [] - BearerAuth: [] requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/LocationCreateRequest' examples: default: value: name: North Campus state: State city: City addressLine: Address 1 zipCode: '33101' phoneNumber: 0987654321 fax: '1234567890' email: first@test.com sendEmail: false enabledCredentialing: false division: reference: DIV-001 responses: '200': $ref: '#/components/responses/LocationCreated' '201': $ref: '#/components/responses/LocationCreated' '400': $ref: '#/components/responses/BadRequest' '401': $ref: '#/components/responses/InvalidOrMissingApiKey' '404': $ref: '#/components/responses/DivisionWriteNotFound' '422': $ref: '#/components/responses/LocationValidationError' '429': $ref: '#/components/responses/TooManyRequests' '500': $ref: '#/components/responses/InternalServerError' '502': $ref: '#/components/responses/LocationBadGateway' /api/v1/locations/{publicId}: get: tags: - Locations summary: Get a location by public ID description: 'Resolves the public location identifier before fetching the resource. When `_elements` is provided, only the requested fields are returned. ' operationId: getLocation security: - ApiKeyHeader: [] - BearerAuth: [] parameters: - $ref: '#/components/parameters/LocationPublicId' - $ref: '#/components/parameters/SearchElements' responses: '200': description: Location resource content: application/fhir+json: schema: $ref: '#/components/schemas/Location' examples: default: value: resourceType: Location id: LOC-001 name: North Campus addressLine: Address 1 city: City state: State division: id: DIV-001 name: North Division zipCode: '33101' phoneNumber: 0987654321 fax: '1234567890' email: first@test.com sendEmail: false enabledCredentialing: true '401': $ref: '#/components/responses/InvalidOrMissingApiKey' '404': $ref: '#/components/responses/LocationWriteNotFound' '429': $ref: '#/components/responses/TooManyRequests' '500': $ref: '#/components/responses/InternalServerError' '502': $ref: '#/components/responses/LocationBadGateway' put: tags: - Locations summary: Update a location description: 'Resolves the public location identifier before updating the resource. ' operationId: updateLocation security: - ApiKeyHeader: [] - BearerAuth: [] parameters: - $ref: '#/components/parameters/LocationPublicId' requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/LocationUpdateRequest' examples: default: value: name: North Campus state: State city: City addressLine: Address 1 zipCode: '33101' phoneNumber: 0987654321 fax: '1234567890' email: first@test.com sendEmail: false enabledCredentialing: false division: reference: DIV-001 responses: '200': $ref: '#/components/responses/LocationUpdated' '401': $ref: '#/components/responses/InvalidOrMissingApiKey' '404': $ref: '#/components/responses/LocationWriteNotFound' '422': $ref: '#/components/responses/LocationValidationError' '429': $ref: '#/components/responses/TooManyRequests' '500': $ref: '#/components/responses/InternalServerError' '502': $ref: '#/components/responses/LocationBadGateway' patch: tags: - Locations summary: Update a location with PATCH description: 'Resolves the public location identifier before updating the resource with the same request body and validation rules as `PUT`. ' operationId: patchLocation security: - ApiKeyHeader: [] - BearerAuth: [] parameters: - $ref: '#/components/parameters/LocationPublicId' requestBody: required: true content: application/json: schema: $ref: '#/components/schemas/LocationUpdateRequest' examples: default: value: name: North Campus state: State city: City addressLine: Address 1 zipCode: '33101' phoneNumber: 0987654321 fax: '1234567890' email: first@test.com sendEmail: false enabledCredentialing: false division: reference: DIV-001 responses: '200': $ref: '#/components/responses/LocationUpdated' '401': $ref: '#/components/responses/InvalidOrMissingApiKey' '404': $ref: '#/components/responses/LocationNotFound' '422': $ref: '#/components/responses/LocationValidationError' '429': $ref: '#/components/responses/TooManyRequests' '500': $ref: '#/components/responses/InternalServerError' '502': $ref: '#/components/responses/LocationBadGateway' components: responses: LocationWriteNotFound: description: Location or linked division resource identifier could not be resolved for a write request content: application/fhir+json: schema: $ref: '#/components/schemas/OperationOutcome' examples: locationNotFound: value: resourceType: OperationOutcome issue: - severity: error code: not-found details: text: One or more location ids could not be resolved. divisionNotFound: value: resourceType: OperationOutcome issue: - severity: error code: not-found details: text: One or more division ids could not be resolved. ValidationError: description: Request validation failed content: application/fhir+json: schema: $ref: '#/components/schemas/OperationOutcome' examples: invalidIntegerQuery: value: resourceType: OperationOutcome issue: - severity: error code: invalid details: text: This value should be of type integer. expression: - _count invalidPageLowerBound: value: resourceType: OperationOutcome issue: - severity: error code: invalid details: text: Field '_page' must be greater than 0 expression: - _page invalidElementsType: value: resourceType: OperationOutcome issue: - severity: error code: invalid details: text: Field '_elements' must be a comma-separated string expression: - _elements missingName: value: resourceType: OperationOutcome issue: - severity: error code: invalid details: text: The Location.name field is required. expression: - name missingDivisionName: value: resourceType: OperationOutcome issue: - severity: error code: invalid details: text: The Division.name field is required. expression: - name missingDivisionLocations: value: resourceType: OperationOutcome issue: - severity: error code: invalid details: text: Field 'locations' is required and must contain at least one location ID string expression: - locations invalidLocations: value: resourceType: OperationOutcome issue: - severity: error code: invalid details: text: Field 'locations' must be an array of non-blank location ID strings expression: - locations[1] divisionNameTooLong: value: resourceType: OperationOutcome issue: - severity: error code: invalid details: text: Field 'name' must be 100 characters or fewer expression: - name duplicateDivisionName: value: resourceType: OperationOutcome issue: - severity: error code: invalid details: text: This division already exists for this company. expression: - name divisionLocationsNotFound: value: resourceType: OperationOutcome issue: - severity: error code: invalid details: text: All locations must exist and belong to the company. expression: - locations divisionLocationsAssigned: value: resourceType: OperationOutcome issue: - severity: error code: invalid details: text: Locations already assigned to another division must first belong to the Default division. expression: - locations emptyDivisionUpdatePayload: value: resourceType: OperationOutcome issue: - severity: error code: invalid details: text: At least one of 'name' or 'locations' must be provided. expression: - name - locations missingState: value: resourceType: OperationOutcome issue: - severity: error code: invalid details: text: Field 'state' is required and must be a non-blank string expression: - state invalidDivisionReference: value: resourceType: OperationOutcome issue: - severity: error code: invalid details: text: Field 'division.reference' must be a non-blank string expression: - division[reference] practitionerResourceType: value: resourceType: OperationOutcome issue: - severity: error code: invalid details: text: Field 'resourceType' must equal "Practitioner" expression: - resourceType practitionerBirthDateFormat: value: resourceType: OperationOutcome issue: - severity: error code: invalid details: text: Field 'birthDate' must use MM/DD/YYYY format expression: - birthDate practitionerMissingRequiredNestedField: value: resourceType: OperationOutcome issue: - severity: error code: invalid details: text: The Practitioner.telecom.email field is required. expression: - telecom.email practitionerInvalidScalar: value: resourceType: OperationOutcome issue: - severity: error code: invalid details: text: This value should be of type scalar. expression: - extension.user.location practitionerInvalidStatus: value: resourceType: OperationOutcome issue: - severity: error code: invalid details: text: 'Status must be one of: Active, Inactive, or Deactivated.' expression: - extension.user.status practitionerInvalidActiveStatusReason: value: resourceType: OperationOutcome issue: - severity: error code: invalid details: text: 'Status Reason must be one of: Contracted, Independent, Other for status Active.' expression: - extension.user.statusReason practitionerInvalidInactiveStatusReason: value: resourceType: OperationOutcome issue: - severity: error code: invalid details: text: 'Status Reason must be one of: On Leave, Prospect, Other for status Inactive.' expression: - extension.user.statusReason practitionerInvalidDeactivatedStatusReason: value: resourceType: OperationOutcome issue: - severity: error code: invalid details: text: 'Status Reason must be one of: Deceased, Resigned, Retired, Terminated, Other for status Deactivated.' expression: - extension.user.statusReason practitionerIncompatibleStatusReason: value: resourceType: OperationOutcome issue: - severity: error code: invalid details: text: Status Reason "Terminated" is not valid for status "Active". expression: - extension.user.statusReason practitionerMissingDeactivatedStatusReason: value: resourceType: OperationOutcome issue: - severity: error code: invalid details: text: Status Reason is required when status is Deactivated. expression: - extension.user.statusReason practitionerCreateMultipleIssues: value: resourceType: OperationOutcome issue: - severity: error code: invalid details: text: The Practitioner.name.given field is required. expression: - name.given - severity: error code: invalid details: text: The Practitioner.extension.employment.positionId field is required. expression: - extension.employment.positionId - severity: error code: invalid details: text: The Practitioner.extension.user.location field is required. expression: - extension.user.location practitionerUnsupportedWriteField: value: resourceType: OperationOutcome issue: - severity: error code: invalid details: text: Field 'extension.employment.employeeCategoryId' is not supported. expression: - extension.employment.employeeCategoryId practitionerInvalidDepartmentReference: value: resourceType: OperationOutcome issue: - severity: error code: invalid details: text: One or more department ids could not be resolved. expression: - extension.employment.departmentId practitionerInvalidLocationReference: value: resourceType: OperationOutcome issue: - severity: error code: invalid details: text: One or more location ids could not be resolved. expression: - extension.user.location LocationUpdated: description: Location updated successfully content: application/fhir+json: schema: $ref: '#/components/schemas/OperationOutcome' examples: default: value: resourceType: OperationOutcome issue: - severity: information code: informational details: text: Location updated successfully. id: LOC-001 LocationValidationError: description: Location request validation failed content: application/fhir+json: schema: $ref: '#/components/schemas/OperationOutcome' examples: missingName: $ref: '#/components/responses/ValidationError/content/application~1fhir+json/examples/missingName' missingState: $ref: '#/components/responses/ValidationError/content/application~1fhir+json/examples/missingState' invalidDivisionReference: $ref: '#/components/responses/ValidationError/content/application~1fhir+json/examples/invalidDivisionReference' BadRequest: description: Request rejected before validation or during request processing content: application/fhir+json: schema: $ref: '#/components/schemas/OperationOutcome' examples: invalidJson: value: resourceType: OperationOutcome issue: - severity: error code: invalid details: text: Request payload contains invalid "json" data. rejectedByBackingService: value: resourceType: OperationOutcome issue: - severity: error code: invalid details: text: The request could not be processed. LocationBadGateway: description: Location identifier or linked division could not be resolved at this time content: application/fhir+json: schema: $ref: '#/components/schemas/OperationOutcome' examples: resolverUnavailable: $ref: '#/components/responses/BadGateway/content/application~1fhir+json/examples/resolverUnavailable' divisionResolutionFailed: value: resourceType: OperationOutcome issue: - severity: error code: invalid details: text: Division reference resolution request failed. InvalidOrMissingApiKey: description: 'Missing credentials or invalid API key. ' content: application/fhir+json: schema: $ref: '#/components/schemas/OperationOutcome' examples: missingCredentials: value: resourceType: OperationOutcome issue: - severity: error code: login details: text: 'Missing or invalid authentication credentials. Provide an API key using the X-API-Key header or Authorization: Bearer .' invalidApiKey: value: resourceType: OperationOutcome issue: - severity: error code: login details: text: Invalid or revoked API key. SearchValidationError: description: Search request validation failed content: application/fhir+json: schema: $ref: '#/components/schemas/OperationOutcome' examples: invalidIntegerQuery: $ref: '#/components/responses/ValidationError/content/application~1fhir+json/examples/invalidIntegerQuery' invalidPageLowerBound: $ref: '#/components/responses/ValidationError/content/application~1fhir+json/examples/invalidPageLowerBound' invalidElementsType: $ref: '#/components/responses/ValidationError/content/application~1fhir+json/examples/invalidElementsType' LocationCreated: description: Location created successfully content: application/fhir+json: schema: $ref: '#/components/schemas/OperationOutcome' examples: default: value: resourceType: OperationOutcome issue: - severity: information code: informational details: text: Location created successfully. id: LOC-001 TooManyRequests: description: 'Rate limit exceeded. ' content: application/fhir+json: schema: $ref: '#/components/schemas/OperationOutcome' examples: default: value: resourceType: OperationOutcome issue: - severity: error code: throttled details: text: Rate limit exceeded. Please retry after a short delay. LocationNotFound: description: Location or linked division resource was not found content: application/fhir+json: schema: $ref: '#/components/schemas/OperationOutcome' examples: locationNotFound: value: resourceType: OperationOutcome issue: - severity: error code: not-found details: text: One or more location ids could not be resolved. divisionNotFound: value: resourceType: OperationOutcome issue: - severity: error code: not-found details: text: Division not found. DivisionWriteNotFound: description: Division resource identifier could not be resolved for a location write request content: application/fhir+json: schema: $ref: '#/components/schemas/OperationOutcome' examples: default: value: resourceType: OperationOutcome issue: - severity: error code: not-found details: text: One or more division ids could not be resolved. BadGateway: description: The requested resource identifier could not be resolved at this time content: application/fhir+json: schema: $ref: '#/components/schemas/OperationOutcome' examples: resolverUnavailable: value: resourceType: OperationOutcome issue: - severity: error code: invalid details: text: Public id resolution request failed. InternalServerError: description: Unexpected internal error content: application/fhir+json: schema: $ref: '#/components/schemas/OperationOutcome' examples: default: value: resourceType: OperationOutcome issue: - severity: error code: exception details: text: An internal server error occurred. Please try again later. schemas: Location: type: object additionalProperties: false required: - resourceType properties: resourceType: type: string const: Location id: type: string example: LOC-001 name: type: string example: North Campus addressLine: type: string example: 123 Main St city: type: string example: Miami state: type: string example: Florida zipCode: type: string example: '33101' phoneNumber: type: string example: 0987654321 fax: type: string example: '1234567890' email: type: string format: email example: first@test.com sendEmail: type: boolean example: false enabledCredentialing: type: boolean example: true division: type: object description: Division associated with the location. properties: id: type: string example: DIV-001 name: type: string example: North Division locations: type: array description: Optional child location identifiers when requested through `_elements`. items: type: string example: LOC-010 LocationCreateRequest: type: object description: 'Payload required to create a location. Both ''name'' and ''state'' are required. Unknown top-level properties are silently ignored by the server. ' required: - name - state properties: name: type: string minLength: 1 maxLength: 100 description: Required location name. Cannot be blank or whitespace-only. example: North Campus state: type: string minLength: 1 pattern: ^\S+.*$ description: Required state value. Cannot be blank or whitespace-only. example: Florida addressLine: type: string description: Location address line. example: 123 Main St city: type: string description: Location city. If provided, it cannot be blank or whitespace-only. example: Miami zipCode: type: string description: Location ZIP or postal code. example: '33101' phoneNumber: type: string pattern: ^[0-9]+$ maxLength: 10 description: Location phone number. Must contain only digits up to 10 characters. example: 0987654321 fax: type: string pattern: ^[0-9]+$ maxLength: 10 description: Location fax number. Must contain only digits up to 10 characters. example: '1234567890' email: type: string format: email description: Location email address. example: first@test.com sendEmail: type: boolean description: Whether to send email for this location. default: false enabledCredentialing: type: boolean description: Whether credentialing is enabled for this location. default: false division: type: object description: Optional division reference wrapper. properties: reference: type: string minLength: 1 description: Division ID reference. example: DIV-001 required: - reference LocationSearchBundle: type: object additionalProperties: false required: - resourceType - type - total - link - entry properties: resourceType: type: string const: Bundle type: type: string const: searchset total: type: integer minimum: 0 link: type: array items: $ref: '#/components/schemas/BundleLink' entry: type: array items: $ref: '#/components/schemas/LocationSearchEntry' LocationUpdateRequest: type: object description: 'Payload to update a location. ''name'' is required. ''state'' is optional when omitted, but cannot be null or blank if provided in the payload. Unknown top-level properties are silently ignored by the server. ' required: - name properties: name: type: string minLength: 1 maxLength: 100 description: Required location name. Cannot be blank or whitespace-only. example: North Campus state: type: string minLength: 1 pattern: ^\S+.*$ description: 'Optional state value. If provided, it cannot be null, blank, or whitespace-only. Omit the field completely if you do not wish to update this field. ' example: Florida addressLine: type: string description: Location address line. example: 123 Main St city: type: string description: Location city. If provided, it cannot be blank or whitespace-only. example: Miami zipCode: type: string description: Location ZIP or postal code. example: '33101' phoneNumber: type: string pattern: ^[0-9]+$ maxLength: 10 description: Location phone number. Must contain only digits up to 10 characters. example: 0987654321 fax: type: string pattern: ^[0-9]+$ maxLength: 10 description: Location fax number. Must contain only digits up to 10 characters. example: '1234567890' email: type: string format: email description: Location email address. example: first@test.com sendEmail: type: boolean description: Whether to send email for this location. default: false enabledCredentialing: type: boolean description: Whether credentialing is enabled for this location. default: false division: type: object description: Optional division reference wrapper. properties: reference: type: string minLength: 1 description: Division ID reference. example: DIV-001 required: - reference LocationSearchEntry: type: object additionalProperties: false required: - resource properties: resource: $ref: '#/components/schemas/Location' OperationOutcomeIssue: type: object additionalProperties: false required: - severity - code - details properties: severity: type: string enum: - fatal - error - warning - information code: type: string example: invalid details: $ref: '#/components/schemas/OperationOutcomeDetails' expression: type: array items: type: string OperationOutcome: type: object additionalProperties: false required: - resourceType - issue properties: resourceType: type: string const: OperationOutcome issue: type: array minItems: 1 items: $ref: '#/components/schemas/OperationOutcomeIssue' BundleLink: type: object additionalProperties: false required: - relation - url properties: relation: type: string enum: - self - first - previous - next - last url: type: string format: uri OperationOutcomeDetails: type: object additionalProperties: false required: - text properties: text: type: string id: type: string parameters: SearchPage: name: _page in: query required: false description: 1-based page number. Must be a positive integer. schema: type: integer minimum: 1 default: 1 SearchCount: name: _count in: query required: false description: Number of resources to return. Must be a positive integer. schema: type: integer minimum: 1 default: 20 SearchElements: name: _elements in: query required: false description: 'Comma-separated field selector. For location, division, position, department, and practitioner category searches, the implemented selectors are `id`, `name`, `division`, `locations`, and `clinical` as applicable. The default when `_elements` is omitted: locations default to `id,name`; divisions default to `id,name,locations`; positions default to `id,name,clinical`; departments default to `id,name`; practitioner categories default to `id,name`. For practitioners, nested selectors are allowed, including values such as `telecom.email`, `telecom.homePhone`, `address.city`, `extension.user.status`, `extension.user.statusReason`, `extension.user.userType`, or `extension.provider.npiNumber`. The public practitioner response uses `extension.user.status` and `extension.user.statusReason` for the visible status state; the legacy `employeeCategoryId` is not exposed. When `_elements` is omitted on practitioner search or get-by-id, the full public resource is returned. Whitespace around values is trimmed. Unknown selectors are preserved in pagination links but ignored when each resource is built. Pagination `link.url` values are returned as absolute URLs including scheme and host (for example, `/api/v1/...` resolves against the active server entry). ' schema: type: string example: id,name LocationPublicId: name: publicId in: path required: true description: Public location identifier. schema: type: string example: LOC-001 securitySchemes: ApiKeyHeader: type: apiKey in: header name: X-API-Key description: Preferred authentication header for protected endpoints. BearerAuth: type: http scheme: bearer bearerFormat: API key description: 'Alternative authentication scheme using `Authorization: Bearer `.'