Why Extract & Export XM Directory Mappings?
If you work with Qualtrics Workflows and XM Directory, the Update XM Directory Contacts task is one of the most powerful data integration tools available. It maps survey responses, embedded data and transaction metadata directly into your directory contacts.
Managing these mappings across multiple workflows is where things get painful:
- Manually documenting which source fields map to which directory fields is slow and error-prone
- Transaction data mappings involve extra complexity with the "Make Piped Text" setting
- Migrating workflows between environments requires exact mapping documentation
- Audit and compliance requirements demand comprehensive, reproducible records
This guide gives you a free browser-based script that extracts all your contact and transaction mappings in seconds and exports them as clean CSV files.
| Screen | What it captures | Output file |
|---|---|---|
| Part 1: Confirm import fields | Source Field → XM Directory contact fields | xm_contact_mappings.csv |
| Part 2: Map specific values | Source Field → Transaction fields + Make Piped Text status | xm_transaction_mappings.csv |
Part 1: Extract Contact Mappings
Watch the Demo
Open the Workflow Task
In Qualtrics, navigate to the Workflows tab in your project or the standalone Workflows page.
Open a workflow containing an Update XM Directory Contacts task and click on it to open the configuration wizard.
You should land on the "Confirm import fields" screen with two columns showing Source Field and XM Directory Field.
Open the Browser Console
Right-click directly on the mapping table on any row where you can see a Source Field value.
Select Inspect or Inspect Element from the context menu.
In the developer tools panel that appears, click the Console tab.
Copy and Run the Extraction Script
Click the Copy Script button, paste into the console, then press Enter.
/**
* Qualtrics XM Directory Mapping Extractor (Contacts + Transactions)
* Extracts Source Field to XM Directory Field mappings - v2.3
* "Update XM Directory Contacts" workflow task.
* Run this script on EACH screen (Part 1 and Part 2).
* Developed by Pirai AI — piraiai.com
* Last updated: March 6, 2026
*/
(function() {
console.log('=== Qualtrics XM Directory Mapping Extractor Started ===');
function extractContactMappings() {
const mappings = [];
// Find the contacts data mapper section (not transaction)
const contactsTable = document.querySelector('.contactsDataMapper table.dataMapperTable');
if (!contactsTable) {
console.log('No contacts mapping table found');
return mappings;
}
const rows = contactsTable.querySelectorAll('tbody tr._2Oqzg');
console.log(`\nFound ${rows.length} contact mapping rows`);
rows.forEach((row, index) => {
try {
let sourceField = '';
let xmDirectoryField = '';
// Extract Source Field (left column)
const sourceInput = row.querySelector('td.sourceFieldCell input.textInput');
if (sourceInput) {
sourceField = sourceInput.value.trim();
}
// Extract XM Directory Field (right column - combobox)
const destinationInput = row.querySelector('td:nth-child(2) input[role="combobox"]');
if (destinationInput) {
xmDirectoryField = destinationInput.value.trim();
}
if (sourceField || xmDirectoryField) {
mappings.push({
index: index + 1,
sourceField: sourceField,
xmDirectoryField: xmDirectoryField
});
}
} catch (error) {
console.warn(`Error processing contact row ${index}:`, error);
}
});
return mappings;
}
function extractTransactionMappings() {
const mappings = [];
// Find the transaction data mapper section
const transactionTable = document.querySelector('.transactionDataMapper table.dataMapperTable');
if (!transactionTable) {
console.log('No transaction mapping table found');
return mappings;
}
const rows = transactionTable.querySelectorAll('tbody tr._2Oqzg');
console.log(`\nFound ${rows.length} transaction mapping rows`);
rows.forEach((row, index) => {
try {
let sourceField = '';
let xmDirectoryField = '';
let makePipedText = false;
// Extract Source Field (left column)
const sourceInput = row.querySelector('td.sourceFieldCell input.textInput');
if (sourceInput) {
sourceField = sourceInput.value.trim();
}
// Extract XM Directory Field (middle column)
const secondColumn = row.querySelector('td:nth-child(2)');
if (secondColumn) {
// First check for label WITHOUT nested input (Transaction Date case)
const standaloneLabel = secondColumn.querySelector('label:not(:has(input))');
if (standaloneLabel) {
xmDirectoryField = standaloneLabel.textContent.trim();
} else {
// Check for label WITH nested input (custom fields case)
const labelWithInput = secondColumn.querySelector('label input[type="text"]');
if (labelWithInput) {
xmDirectoryField = labelWithInput.value.trim();
}
}
}
// Extract "Make piped text" checkbox status (third column)
const checkbox = row.querySelector('td:nth-child(3) input[type="checkbox"]');
if (checkbox) {
makePipedText = checkbox.checked;
}
if (sourceField || xmDirectoryField) {
mappings.push({
index: index + 1,
sourceField: sourceField,
xmDirectoryField: xmDirectoryField,
makePipedText: makePipedText ? 'Yes' : 'No'
});
}
} catch (error) {
console.warn(`Error processing transaction row ${index}:`, error);
}
});
return mappings;
}
function displayAsTable(mappings, type) {
console.log(`\n=== EXTRACTED ${type.toUpperCase()} MAPPINGS ===\n`);
console.table(mappings);
console.log(`\nTotal ${type} mappings: ${mappings.length}`);
}
function convertToCSV(mappings, includePipedText = false) {
if (mappings.length === 0) return '';
let headerRow = '"Index","Source Field","XM Directory Field"';
if (includePipedText) {
headerRow += ',"Make Piped Text"';
}
const dataRows = mappings.map(mapping => {
let row = [
`"${mapping.index}"`,
`"${mapping.sourceField.replace(/"/g, '""')}"`,
`"${mapping.xmDirectoryField.replace(/"/g, '""')}"`
];
if (includePipedText) {
row.push(`"${mapping.makePipedText}"`);
}
return row.join(',');
}).join('\n');
return headerRow + '\n' + dataRows;
}
function downloadCSV(csv, filename) {
const blob = new Blob([csv], { type: 'text/csv;charset=utf-8;' });
const link = document.createElement('a');
const url = URL.createObjectURL(blob);
link.setAttribute('href', url);
link.setAttribute('download', filename);
link.style.visibility = 'hidden';
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
URL.revokeObjectURL(url);
console.log(`✓ Downloaded: ${filename}`);
}
function copyToClipboard(text) {
const textarea = document.createElement('textarea');
textarea.value = text;
textarea.style.position = 'fixed';
textarea.style.opacity = '0';
document.body.appendChild(textarea);
textarea.select();
try {
document.execCommand('copy');
console.log('✓ CSV data copied to clipboard!');
return true;
} catch (err) {
console.error('Failed to copy to clipboard:', err);
return false;
} finally {
document.body.removeChild(textarea);
}
}
console.log('Starting extraction...\n');
// Extract both types
const contactMappings = extractContactMappings();
const transactionMappings = extractTransactionMappings();
if (contactMappings.length === 0 && transactionMappings.length === 0) {
console.error('❌ No mappings found! Make sure you are on the XM Directory mapping dialog.');
return;
}
// Display results
if (contactMappings.length > 0) {
displayAsTable(contactMappings, 'contact');
}
if (transactionMappings.length > 0) {
displayAsTable(transactionMappings, 'transaction');
}
// Prepare CSV data
const contactCSV = convertToCSV(contactMappings, false);
const transactionCSV = convertToCSV(transactionMappings, true);
// Make data available globally
window.xmContactMappings = contactMappings;
window.xmTransactionMappings = transactionMappings;
window.xmContactCSV = contactCSV;
window.xmTransactionCSV = transactionCSV;
// Contact mapping functions
window.downloadContactMappingsCSV = function(filename) {
downloadCSV(contactCSV, filename || 'xm_contact_mappings.csv');
};
window.copyContactMappingsCSV = function() {
return copyToClipboard(contactCSV);
};
// Transaction mapping functions
window.downloadTransactionMappingsCSV = function(filename) {
downloadCSV(transactionCSV, filename || 'xm_transaction_mappings.csv');
};
window.copyTransactionMappingsCSV = function() {
return copyToClipboard(transactionCSV);
};
// Combined functions
window.downloadAllMappingsCSV = function() {
if (contactMappings.length > 0) {
downloadCSV(contactCSV, 'xm_contact_mappings.csv');
}
if (transactionMappings.length > 0) {
downloadCSV(transactionCSV, 'xm_transaction_mappings.csv');
}
};
window.getContactJSON = function() {
return JSON.stringify(contactMappings, null, 2);
};
window.getTransactionJSON = function() {
return JSON.stringify(transactionMappings, null, 2);
};
console.log('\n=== ACTIONS AVAILABLE ===');
console.log('CONTACT MAPPINGS:');
console.log(' 1. Download CSV: downloadContactMappingsCSV()');
console.log(' 2. Copy CSV: copyContactMappingsCSV()');
console.log(' 3. View JSON: getContactJSON()');
console.log('\nTRANSACTION MAPPINGS:');
console.log(' 4. Download CSV: downloadTransactionMappingsCSV()');
console.log(' 5. Copy CSV: copyTransactionMappingsCSV()');
console.log(' 6. View JSON: getTransactionJSON()');
console.log('\nALL MAPPINGS:');
console.log(' 7. Download All: downloadAllMappingsCSV()');
console.log(' 8. View data: xmContactMappings / xmTransactionMappings');
console.log('\n✓ Extraction complete! Use the commands above to export the data.');
})();
Export Contact Mappings
You should see the contact mappings table printed in the console. Run this command to download:
downloadContactMappingsCSV()
// downloads xm_contact_mappings.csv
Keep the console and the Qualtrics dialog open, you need both for Part 2.
Click "Next" in Qualtrics
Click the Next button at the bottom of the contact mapping screen to advance to the transaction data screen. Stay inside the task dialog.
Part 2: Extract Transaction Mappings
Enable the Transaction Data Checkbox
On the "Map specific values (optional)" screen, check the Transaction data checkbox to reveal the transaction mapping table.
You should now see three columns: Source Field, XM Directory Field, and Make Piped Text.
Run the Same Script Again
Paste and run the exact same script from Step 3 into the console again. This time it will find the transaction mapping table and extract those rows automatically.
Export Transaction Mappings
Run this command to download your transaction mappings:
downloadTransactionMappingsCSV()
// downloads xm_transaction_mappings.csv
Or download both files at once if you prefer:
downloadAllMappingsCSV()
// downloads both CSV files in one command
Understanding Your Output
Contact Mappings (xm_contact_mappings.csv)
Extracted from the "Confirm import fields" screen. Shows which data source fields map to which XM Directory contact fields.
| Index | Source Field | XM Directory Field |
|---|---|---|
| 1 | ${e://Field/SampleField3} | First name |
| 2 | ${e://Field/SampleField4} | Last name |
| 3 | ${e://Field/SampleField5} | |
| 4 | ${e://Field/SampleField6} | Phone number |
| 5 | ${e://Field/SampleField2} | Contact ID |
Transaction Mappings (xm_transaction_mappings.csv)
Extracted from the "Map specific values" screen. Includes the Make Piped Text column, which is critical for downstream workflow logic.
| Index | Source Field | XM Directory Field | Make Piped Text |
|---|---|---|---|
| 1 | ${e://Field/purchase_date} | Transaction Date | No |
| 2 | ${e://Field/product_name} | product_name | Yes |
| 3 | ${e://Field/order_total} | order_value | Yes |
| 4 | ${e://Field/order_status} | status | No |
Why "Make Piped Text" matters
When a transaction field is marked Yes, it becomes available as ${tr://Field/field_name} in all subsequent workflow tasks including email personalisation, conditional branches, web service calls. If a downstream task is failing unexpectedly, check this column first. A field used in an email template that was not marked Make Piped Text will silently fail.
Troubleshooting
No mappings found
- Confirm you are inside the "Update XM Directory Contacts" task configuration with the mapping table fully visible
- Scroll through the table to ensure all rows have loaded before running the script
- Refresh the page, reopen the task, and try again
No transaction mappings found (most common issue)
- You ran the script on the contact screen only. Click Next, check the Transaction data checkbox, and run the script again
- Make sure the Transaction data checkbox is ticked before running on Part 2
- If your workflow has no transaction data configured, this is expected and not an error
Download not working
- Use the clipboard alternative:
copyContactMappingsCSV()then paste into Excel or Google Sheets - Check your browser is not blocking downloads from qualtrics.com
- Disable any pop-up blocker temporarily and retry
Script shows errors or runs but finds nothing
- Make sure you copied the entire script, it is long, use Ctrl+A inside the code block before copying
- Type
clear()in the console to reset it, then paste and run again - Try Chrome if you are using another browser
Advanced Usage
Pass a filename to keep your exports organised by workflow, environment and date:
downloadContactMappingsCSV('PostPurchase_Contacts_2026-03-06.csv')downloadTransactionMappingsCSV('PostPurchase_Transactions_2026-03-06.csv')
Recommended naming pattern: {WorkflowName}_{Type}_{YYYY-MM-DD}.csv
After extraction the data is stored as JavaScript arrays you can query directly:
xmTransactionMappings.filter(m => m.makePipedText === 'Yes')// Find a specific contact field
xmContactMappings.find(m => m.xmDirectoryField === 'Email')// Count totals
console.log(xmContactMappings.length, xmTransactionMappings.length)
If you need the data in JSON format for documentation pipelines, validation scripts or CI/CD workflows:
console.log(JSON.stringify(xmContactMappings, null, 2))// Download as JSON file
const b = new Blob([JSON.stringify(xmContactMappings, null, 2)], {type:'application/json'});const a = document.createElement('a');a.href = URL.createObjectURL(b);a.download = 'contact_mappings.json'; a.click();
A few habits that pay off when workflows change or environments are migrated:
- Extract before making any changes, which creates a clean rollback reference
- Use a consistent folder structure:
WorkflowDocs / 2026-Q1 / WorkflowName_Type_Date.csv - For environment migrations, extract from test first, verify all source fields exist in production, reconfigure, then extract again and compare to confirm accuracy
- Use Excel VLOOKUP to diff two extractions:
=IF(VLOOKUP(B2,Sheet2!B:C,2,FALSE)=C2,"Match","Different")
Need to convert surveys into Qualtrics?
This tool documents your XM Directory workflow mappings. If you also need to convert Word, PDF or Excel questionnaires into QSF files for Qualtrics import, Pirai AI handles that automatically across 20+ survey platforms with no manual scripting required.