Extract & Export the Qualtrics Update XM Directory Contacts Task Mappings

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:

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.

⚠️
This is a two-part process. Run the script twice

The XM Directory task has two separate mapping screens. You must run the extraction script once on each screen. Running it only once is the most common mistake and results in missing transaction mappings.

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

▶ Part 1: Contact Mappings
Part 2: Transaction Mappings
1

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.

2

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.

Why right-click on the table? This ensures the correct DOM elements are loaded in the inspector before the script runs, making extraction more reliable.
3

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.');
})();
4

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
You should see: ✓ Downloaded: xm_contact_mappings.csv

Keep the console and the Qualtrics dialog open, you need both for Part 2.

5

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

✓ Part 1: Contact Mappings Done
▶ Part 2: Transaction Mappings
6

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.

How to confirm you're on the right screen: Look for a "Transaction data" checkbox, a "Make piped text" column header, and a Save button at the bottom (not Next).
7

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.

Yes, the same script. It detects which screen you are on and extracts accordingly. The contact table and transaction table are in different parts of the page structure, which is why a second run is needed.
8

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
Done. You now have two CSV files with complete documentation of your XM Directory workflow mappings.

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.

IndexSource FieldXM Directory Field
1${e://Field/SampleField3}First name
2${e://Field/SampleField4}Last name
3${e://Field/SampleField5}Email
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.

IndexSource FieldXM Directory FieldMake Piped Text
1${e://Field/purchase_date}Transaction DateNo
2${e://Field/product_name}product_nameYes
3${e://Field/order_total}order_valueYes
4${e://Field/order_status}statusNo

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

Custom export filenames +

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

Filter and analyse data in the console +

After extraction the data is stored as JavaScript arrays you can query directly:

// Only fields marked for piped text
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)
Export as JSON for programmatic use +

If you need the data in JSON format for documentation pipelines, validation scripts or CI/CD workflows:

// View JSON in console
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();
Best practices for documentation and versioning +

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.