This tutorial walks a system administrator through every step needed before a data steward can open the MDM Web UI dashboard and start reviewing patient identity decisions. The MDM Web UI supports the Probabilistic MDM strategy, in which borderline matches are resolved by a human reviewer. For a shorter reference-style overview of the same setup, see the MDM UI Tutorial; this tutorial is a detailed step-by-step walkthrough. By the end, the following will be in place:
MATCH_AND_LINK mode for link management, which requires a separately purchased MDM license. Contact sales@smiledigitalhealth.com if you need to enable it. An MDM module explicitly configured with MATCH_ONLY mode does not require a license — see MDM for details.
http://localhost:9100 (or your configured admin port) using an account with the ROLE_SUPERUSER permission.The MDM engine runs asynchronously on top of Smile CDR's subscription infrastructure. Two settings on the FHIR Storage module must be enabled before the MDM module can operate.
Open the FHIR Storage module
In the Web Admin Console, go to Configuration → Module Config and open the persistence module.
Enable MDM and message subscriptions
Use the On this page sidebar to navigate to each section below, then set the values shown:
| Section in sidebar | Setting | Value |
|---|---|---|
| FHIR MDM Server | MDM Mode Enabled | Enabled |
| FHIR Subscription Persistence | Message Subscription Enabled | Enabled |
| FHIR Configuration | Seed Base Validation Resources | Enabled |
The Seed Base Validation Resources setting loads StructureDefinition and related FHIR conformance resources on startup. The MDM UI frontend requires these to be present.
Save and restart the persistence module.
The MDM module processes incoming resources and produces the links that data stewards review. If the match rules produce only MATCH and no POSSIBLE_MATCH outcomes, the data steward queue will always be empty. This step configures rules that route borderline cases to the dashboard.
Create the MDM module (if it does not already exist)
Go to Configuration → Module Config → Add Module and select MDM.
Set the consumer thread count
Set Consumer Thread Count to 2 (or 1 for a low-volume environment).
Set the Subscription dependency
Under Module Dependencies, select the Subscription Matching module.
Enter the MDM Rule Definition Script
Paste the following JSON into the MDM Rule Definition Script field. It matches automatically when both name and birth date agree, and routes cases where only the family name and birth date agree to the data steward queue as Possible Matches.
{
"version": "v1",
"mdmTypes": ["Patient"],
"candidateSearchParams": [
{
"resourceType": "Patient",
"searchParams": ["birthdate"]
},
{
"resourceType": "Patient",
"searchParams": ["identifier"]
}
],
"candidateFilterSearchParams": [],
"matchFields": [
{
"name": "family-name",
"resourceType": "Patient",
"resourcePath": "name.family",
"similarity": {
"algorithm": "JARO_WINKLER",
"matchThreshold": 0.88
}
},
{
"name": "given-name",
"resourceType": "Patient",
"resourcePath": "name.given",
"matcher": {
"algorithm": "DOUBLE_METAPHONE"
}
},
{
"name": "birthdate",
"resourceType": "Patient",
"resourcePath": "birthDate",
"matcher": {
"algorithm": "DATE"
}
}
],
"matchResultMap": {
"family-name,given-name,birthdate": "MATCH",
"family-name,birthdate": "POSSIBLE_MATCH"
}
}
The matchResultMap is what creates work for data stewards. The MATCH entry handles the confident cases automatically. The POSSIBLE_MATCH entry catches cases where the family name and birth date agree but the given name comparison fails — for example, when the given name is absent on one record or differs significantly. Each POSSIBLE_MATCH link becomes a task in the dashboard.
Enter a survivorship script
Survivorship rules control which field values are promoted to the Golden Record when multiple source records are linked, and which values are used when two Golden Records are merged. Without a survivorship script, the Golden Record is left empty after a merge.
Paste the following into the MDM Survivorship Script field:
function mdmApplySurvivorshipRules(targetRec, goldenRec, transactionContext) {
var helper = new MdmHelper(Fhir.getContext(), targetRec, goldenRec, transactionContext);
helper.replaceAll();
}
function mdmApplySurvivorshipRulesOnMergeGoldenResources(fromGoldenRec, toGoldenRec, transactionContext) {
var helper = new MdmHelper(Fhir.getContext(), fromGoldenRec, toGoldenRec, transactionContext);
helper.mergeAll();
}
The first function runs whenever a source record is linked to a Golden Record: it copies all demographic fields from the source record to the Golden Record. The second function runs when two Golden Records are merged by a data steward: it combines all fields from both records into the surviving one.
Save the MDM module and start it.
Verify that the module status icon turns green. If it stays red or yellow, check Logs → Module Logs for errors. The most common cause is a missing Subscription Matching dependency.
The MDM UI authenticates users through SMART/OIDC. A SMART Outbound Security module issues tokens, and a callback script within it assigns each user the appropriate MDM UI role at login time.
Create the module (if it does not already exist)
Go to Configuration → Module Config → Add Module and select SMART Outbound Security. Use the module ID smart_auth.
Set the listener and issuer
| Setting | Value |
|---|---|
| OpenID Connect (OIDC) → Issuer URL | http://localhost:9200/smartauth |
| HTTP Listener → Listener Port | 9200 |
| HTTP Listener → Context Path | /smartauth |
| HTTP Listener → Respect Forward Headers | Disabled |
| HTTP Listener → HTTPS Forwarding Assumed | Disabled |
http:// URLs for all OIDC endpoints, which is correct for a direct connection. With them on, the server treats every connection as HTTPS and generates https:// URLs regardless of the actual listener protocol — connecting directly to the server without a proxy in front will then produce an ERR_SSL_PROTOCOL_ERROR in the browser.
Disable enforced scope restrictions
Under SMART Authorization, disable Enforce Approved Scopes. This lets the MDM UI receive all the permissions it needs without requiring each scope to be pre-approved on the client.
Enable CORS
Under Cross-Origin Resource Sharing, enable CORS and set the allowed origin to the URL where the MDM UI will be served (for example, http://localhost:8080). Do not use * here because the OIDC logout flow requires an exact origin match.
Add the Post-Authorization Script
In the SMART Callback Script section, paste the following into the Post Authorization Script Text field. This script assigns the Data Steward or Admin role to a user based on their username at the time a token is issued.
function onTokenGenerating(theUserSession, theAuthorizationRequestDetails) {
var userName = theUserSession.username;
if (userName === 'DATA_STEWARD' || userName === 'ADMIN') {
theUserSession.addAuthority('FHIR_WRITE_ALL_OF_TYPE', 'AuditEvent');
theUserSession.addAuthority('FHIR_WRITE_ALL_OF_TYPE', 'Task');
theUserSession.addAuthority('ACCESS_ADMIN_JSON');
theUserSession.addAuthority('UPDATE_USER');
theUserSession.addAuthority('VIEW_USERS');
}
if (userName === 'DATA_STEWARD') {
theUserSession.addAuthority('ROLE_MDMUI_DATASTEWARD_FHIR');
theUserSession.addAuthority('VIEW_MODULE_CONFIG_FOR_MODULE', 'Master/mdm');
theAuthorizationRequestDetails.addAccessTokenClaim('user_role', 'data_steward');
} else if (userName === 'ADMIN') {
theUserSession.addAuthority('FHIR_EXTENDED_OPERATION_ON_SERVER', '$mdm-evaluate');
theUserSession.addAuthority('ROLE_MDMUI_ADMIN_FHIR');
theUserSession.addAuthority('MODULE_ADMIN_FOR_MODULE', 'Master/mdm');
theUserSession.addAuthority('VIEW_BATCH_JOBS');
theAuthorizationRequestDetails.addAccessTokenClaim('user_role', 'admin');
}
}
The ROLE_MDMUI_DATASTEWARD_FHIR permission bundle includes the individual MDM operation authorities needed to query links, update links, merge Golden Records, and mark pairs as not-duplicate. The ROLE_MDMUI_ADMIN_FHIR bundle includes all of those plus the ability to run $mdm-clear and $mdm-submit.
Save and restart the smart_auth module.
The MDM UI uses the JSON Admin API to fetch module configuration and metadata.
Create the module (if it does not already exist)
Go to Configuration → Module Config → Add Module and select JSON Admin API.
Configure it
| Setting | Value |
|---|---|
| HTTP Listener → Listener Port | 9000 |
| HTTP Listener → Context Path | /json-admin |
| Auth: OpenID Connect → OpenIdConnect Security | Enabled |
| Cross-Origin Resource Sharing → CORS Enabled | Enabled |
| Cross-Origin Resource Sharing → CORS Allowed Origins | http://localhost:8080 (or * for testing only) |
Set module dependencies
In the Module Dependencies section, set the OpenID Connect Authentication dropdown to smart_auth.
Save and restart the JSON Admin API module.
The MDM UI reads and writes FHIR resources — Patient records, AuditEvents, Tasks — through a dedicated FHIR REST endpoint that is protected by OIDC.
Create the module
Go to Configuration → Module Config → Add Module and select FHIR REST Endpoint (All FHIR Versions). Use the module ID fhir_endpoint.
Configure it
| Setting | Value |
|---|---|
| HTTP Listener → Listener Port | 8000 |
| HTTP Listener → Context Path | /fhir-request |
| FHIR REST Endpoint → Fixed Value for Endpoint Base URL | http://localhost:8000/fhir-request |
| Auth: OpenID Connect → OpenID Connect Security | Yes |
| Cross-Origin Resource Sharing → CORS Enabled | Enabled |
| CORS Allowed Origins | http://localhost:8080 (or * for testing only) |
Set module dependencies
Under Module Dependencies, add:
| Dependency type | Select |
|---|---|
| FHIR Storage Module | persistence (FHIR Storage R4 RDBMS) |
| OpenID Connect Authentication | smart_auth (SMART Outbound Security) |
Save and start the fhir_endpoint module.
The MDM UI is a browser application. An OIDC client record authorizes it to request tokens from the SMART auth module.
Open the OIDC client manager
In the Web Admin Console, go to Users & Authorization → OpenID Connect Clients and click Add Client.
Fill in the client details
| Field | Value |
|---|---|
| Client ID | MDM_UI |
| Client Name | MDM UI |
| Authorized Grant Types | Authorization Code, Refresh Token, JWT Bearer Token |
| Authorized Redirect URLs | http://localhost:8080/mdm-ui/ |
| Scopes | cdr_all_user_authorities launch/patient launch/practitioner offline_access openid profile |
The Authorized Redirect URL must match the URL of the MDM UI module exactly, including the trailing slash and any context path. If you change the MDM UI's port or context path in the next step, update this URL to match.
Click Create Client.
Create the module
Go to Configuration → Module Config → Add Module and select MDM UI.
Configure it
| Setting | Value |
|---|---|
| HTTP Listener → Listener Port | 8080 |
| HTTP Listener → Context Path | /mdm-ui/ |
| Organization Identifier | A system and value identifying your organization (for example, http://example.org/org-id\|my-org). A new Organization resource is created automatically if one does not exist with this identifier. |
| JSON Admin URL | http://localhost:9000/json-admin |
| OIDC Client ID | MDM_UI |
| Issuer URL | http://localhost:9200/smartauth |
| Redirect URL | http://localhost:8080/mdm-ui/ |
| Logout URL | http://localhost:9200/smartauth/logout?cb=none&revoke=token&revoke=token_refresh |
Set module dependencies
Under Module Dependencies, select all available dependencies. This typically includes the FHIR REST endpoint, the JSON Admin API, and the SMART Outbound Security module.
Save and start the MDM UI module.
The MDM UI is now accessible at http://localhost:8080/mdm-ui/.
Users logging into the MDM UI must have accounts in the local user store. The post-authorization script from Step 3 grants the correct MDM UI permissions at login time based on the username — so the account names must match the values checked in that script.
Open the User Manager
Go to Users & Authorization → User Management and click Add User.
Create the Data Steward account
| Field | Value |
|---|---|
| Username | DATA_STEWARD |
| Password | Choose a strong password and share it securely with the user. |
| Permissions | No explicit permissions are required here — the post-authorization script adds them when the user logs into the MDM UI. |
Click Save.
Create the Admin account (optional)
Repeat the above with username ADMIN if you also want an MDM UI administrator account that can run match evaluations and batch jobs from within the UI.
DATA_STEWARD and ADMIN match the values in the post-authorization script written in Step 3. If you use different usernames, update the script accordingly. For production deployments with many stewards, replace the per-username checks with group membership lookups so you do not need to modify the script each time you add or remove a user.
Check module status
In the Web Admin Console, go to Configuration → Module Config and confirm that all seven modules show a green status icon:
Log in as a data steward
Open http://localhost:8080/mdm-ui/ in a browser. You should be redirected to the SMART auth login page. Log in with the DATA_STEWARD credentials created in Step 8.
After login you should see the MDM dashboard with Possible Matches and Possible Duplicates sections. Both counts will be zero on a fresh installation because no patient data has been loaded yet.
Confirm the data steward's view
Verify that the user can see the review queues but does not see the configuration or administration panels — those are visible only to users with the ADMIN role.
Send a test patient pair
To verify the full pipeline end to end, create two Patient records whose names and birth dates trigger a POSSIBLE_MATCH under the rules in Step 2.
Patient 1:
{
"resourceType": "Patient",
"name": [{ "family": "Smith", "given": ["Jonathan"] }],
"birthDate": "1985-03-15"
}
Patient 2 (given name absent — should produce a Possible Match, not an automatic Match):
{
"resourceType": "Patient",
"name": [{ "family": "Smith" }],
"birthDate": "1985-03-15"
}
Wait a few seconds for asynchronous MDM processing, then reload the MDM UI dashboard. The Possible Matches count should increment to 1. The data steward can now open that task and review it.
In this tutorial you:
MATCH links and POSSIBLE_MATCH tasks for data steward review, and a survivorship script for Golden Record merges.ROLE_MDMUI_DATASTEWARD_FHIR and ROLE_MDMUI_ADMIN_FHIR permission bundles at login.JARO_WINKLER threshold and the candidateSearchParams to fit your patient population's data quality. See Advanced Rule Authoring for a systematic guide.