Upload Contacts Guide

I attempted to use the below and I am getting blanks when the page opens up. This is on https://developer.genesys.cloud/routing/outbound/upload-contacts-guide.

// Add contact lists to the UI contactLists.entities.forEach((contactList) => { $('#contactLists').append($('') .attr('value', contactList.id) .text(contactList.name));

The entire code I am using is:

<style type='text/css'>
	.row {
		margin-top: 8px;
	}
</style>

<script src="https://code.jquery.com/jquery-3.3.1.min.js" integrity="sha256-FgpCb/KJQlLNfOu91ta32o/NMZxltwRo8QtmkMRdAu8=" crossorigin="anonymous"></script>
<script src="https://sdk-cdn.mypurecloud.com/javascript/157.0.0/purecloud-platform-client-v2.min.js"></script>

<script type='text/javascript'>
	// This client ID expects the redirect URL to be http://localhost:8080/
	const clientId = '945f7819-983d-4996-9595-e466fab0a604';
	//const redirectUri = window.location.href;
	const redirectUri = http://localhost:8080;

	// Set Genesys Cloud objects
	const platformClient = require('platformClient');
	const client = platformClient.ApiClient.instance;
	const outboundApi = new platformClient.OutboundApi();

	// Set Genesys Cloud settings
	client.setEnvironment('usw2.pure.cloud');
	client.setPersistSettings(false, 'test_app');

	$(document).ready(() => {
		// Handle file selection
		$('#file-input').change((event) => {
			uploadContacts(event.target.files[0]);
		});
		
		// Authenticate with Genesys Cloud
		client.loginImplicitGrant(clientId, redirectUri)
			.then(() => {
				console.log('Logged in');

				// Get a list of contact lists
				return outboundApi.getOutboundContactlists({ pageSize: 100 });
			})
			.then((contactLists) => {
				// Add contact lists to the UI
				contactLists.entities.forEach((contactList) => {
					$('#contactLists').append($('<option></option>')
						.attr('value', contactList.id)
						.text(contactList.name));
				});
			})
			.catch((err) => console.error(err));
		
	function uploadContacts(file) {
		const contactListId = $('#contactLists').find(':selected').val();
		console.log('contactListId: ' + contactListId);

		// Build form data object (POST request body)
		const data = new FormData();
		data.append('id', contactListId);
		data.append('file', file);
		data.append('fileType', 'contactlist');
		data.append('contact-id-name', 'Contact ID');

		// Build request
		const request = {
			url: 'https://apps.mypurecloud.com/uploads/v2/contactlist',
			headers: {
				Authorization: 'bearer ' + client.authData.accessToken
			},
			data: data,
			processData: false,
			contentType: false
		};

		// Upload contact list
		$.post(request)
			.then((data) => {
				console.log('UPLOAD SUCCESS');
				console.log(data);

				// Show success
				$('#upload-result').html($('<div>').addClass('alert alert-success').text('File uploaded successfully'));
			})
			.catch((err) => {
				let message = $('<span>').text('File uploaded failed!');
				if (err.status) {
					message.append($('<p>').text(`${err.status} ${err.statusText}`));
					message.append($('<p>').text(`Response body: ${err.responseText}`));
				}
				console.error(err);

				// Show failure
				$('#upload-result').html($('<div>').addClass('alert alert-danger').html(message));
			});
	}
</script>
Contact Lists
Choose file to upload

image

Are there any errors in the browser's JS console? There's a couple places in that script that will log errors to the console. If no errors, when you look in the browser's network tab do you see any contacts coming back from the request to GET /api/v2/outbound/contactlists?

Hey Tim. Thanks for the quick response.

As far as errors in the browser's JS console, it looks like there is something, which looks to be coming from the redirect Uri. See attached screenshot. That is the one line that I added, if you notice that:
//const redirectUri = window.location.href; (This was the old one, but wasn't working.)
const redirectUri = http://localhost:8080; (This is the new one, is working but not displaying the 3 contact lists.)

When doing the GET /api/v2/outbound/contactlists, below is what is displaying. This is correct, have 3 contact lists, API Test List, ECS Amb List and ECS Rad List:
{
"entities": [
{
"id": "117b7d61-425f-4b33-9f4f-1da09864018a",
"name": "API Test List",
"dateCreated": "2023-01-23T15:15:41.882Z",
"dateModified": "2023-01-23T20:36:20.583Z",
"version": 2,
"division": {
"id": "84d7943c-a4a4-42ad-bd51-f4793abce2a0",
"name": "JMG/SA",
"selfUri": "/api/v2/authorization/divisions/84d7943c-a4a4-42ad-bd51-f4793abce2a0"
},
"columnNames": [
"Days in WQ",
"MRN",
"Patient Name",
"To Dept",
"To Provider",
"Referring Dept",
"Zip Code",
"Order Procedure",
"Ordering Provider",
"Order Diagnosis",
"Primary Coverage",
"WQ Note",
"Start Date",
"Future Expected Date",
"Possible Appointment",
"Deferred Reason",
"Priority",
"MyJeffersonHealth Status",
"Status",
"Home Phone",
"Mobile Phone"
],
"phoneColumns": [
{
"columnName": "Mobile Phone",
"type": "Cell"
}
],
"emailColumns": [],
"previewModeColumnName": "",
"previewModeAcceptedValues": [],
"automaticTimeZoneMapping": true,
"zipCodeColumnName": "Zip Code",
"selfUri": "/api/v2/outbound/contactlists/117b7d61-425f-4b33-9f4f-1da09864018a"
},
{
"id": "33597c7d-f1f8-4a6c-9160-0dfa8b276879",
"name": "ECS Amb Test",
"dateCreated": "2023-01-23T13:31:07.837Z",
"dateModified": "2023-01-23T20:36:46.826Z",
"version": 2,
"division": {
"id": "84d7943c-a4a4-42ad-bd51-f4793abce2a0",
"name": "JMG/SA",
"selfUri": "/api/v2/authorization/divisions/84d7943c-a4a4-42ad-bd51-f4793abce2a0"
},
"columnNames": [
"Days in WQ",
"MRN",
"Patient Name",
"To Dept",
"To Provider",
"Referring Dept",
"Zip Code",
"Order Procedure",
"Ordering Provider",
"Order Diagnosis",
"Primary Coverage",
"WQ Note",
"Start Date",
"Future Expected Date",
"Possible Appointment",
"Deferred Reason",
"Priority",
"MyJeffersonHealth Status",
"Status",
"Home Phone",
"Mobile Phone"
],
"phoneColumns": [
{
"columnName": "Mobile Phone",
"type": "Cell"
}
],
"emailColumns": [],
"previewModeColumnName": "",
"previewModeAcceptedValues": [],
"attemptLimits": {
"id": "c4892e32-b514-4922-b66e-e6dc0c60e00f",
"name": "ECS Outbound",
"selfUri": "/api/v2/outbound/attemptlimits/c4892e32-b514-4922-b66e-e6dc0c60e00f"
},
"automaticTimeZoneMapping": true,
"zipCodeColumnName": "Zip Code",
"selfUri": "/api/v2/outbound/contactlists/33597c7d-f1f8-4a6c-9160-0dfa8b276879"
},
{
"id": "58ff9cd4-080e-4d63-bb18-9f6f4f00774a",
"name": "ECS Rad Test",
"dateCreated": "2023-01-23T13:31:47.807Z",
"dateModified": "2023-01-23T20:36:53.185Z",
"version": 2,
"division": {
"id": "84d7943c-a4a4-42ad-bd51-f4793abce2a0",
"name": "JMG/SA",
"selfUri": "/api/v2/authorization/divisions/84d7943c-a4a4-42ad-bd51-f4793abce2a0"
},
"columnNames": [
"Days in WQ",
"MRN",
"Patient Name",
"To Dept",
"To Provider",
"Referring Dept",
"Zip Code",
"Order Procedure",
"Ordering Provider",
"Order Diagnosis",
"Primary Coverage",
"WQ Note",
"Start Date",
"Future Expected Date",
"Possible Appointment",
"Deferred Reason",
"Priority",
"MyJeffersonHealth Status",
"Status",
"Home Phone",
"Mobile Phone"
],
"phoneColumns": [
{
"columnName": "Mobile Phone",
"type": "Cell"
}
],
"emailColumns": [],
"previewModeColumnName": "",
"previewModeAcceptedValues": [],
"attemptLimits": {
"id": "c4892e32-b514-4922-b66e-e6dc0c60e00f",
"name": "ECS Outbound",
"selfUri": "/api/v2/outbound/attemptlimits/c4892e32-b514-4922-b66e-e6dc0c60e00f"
},
"automaticTimeZoneMapping": true,
"selfUri": "/api/v2/outbound/contactlists/58ff9cd4-080e-4d63-bb18-9f6f4f00774a"
}
],
"pageSize": 25,
"pageNumber": 1,
"total": 3,
"firstUri": "/api/v2/outbound/contactlists?pageSize=25&pageNumber=1",
"lastUri": "/api/v2/outbound/contactlists?pageSize=25&pageNumber=1",
"selfUri": "/api/v2/outbound/contactlists?pageSize=25&pageNumber=1",
"pageCount": 1
}

Hello,

redirectUri expects a string in this code. So what you have typed is incorrect.
It must be:

const redirectUri = 'http://localhost:8080'

Your code also implies/requires 2 things:

You have created an OAuth Client (Implicit/Token Grant type) in your Genesys Cloud org, with Authorized Redirect Uri = http://localhost:8080 -> the one with clientId = '945f7819-983d-4996-9595-e466fab0a604'

Your html page for the Upload Contacts guide (your index.html) is hosted on a local webserver. I mean that opening a brower page with http://localhost:8080 will render your index.html page.

Regards,

Hey Jerome. Thank you for your response. Sorry, I was just following the directions and using the code from https://developer.genesys.cloud/routing/outbound/upload-contacts-guide. I'm looking to do this without hosting it on a local webserver. Is there a way this can be accomplished?

Yes and No.

The page must be available from your browser typing a url (and not a path to your local disk/computer).
So you could host it anywhere on internet (I mean a site that allows to host web pages) as long as it is available via http://..... or https://.....
Or you can use nodejs (or python or ...) to create a small script to do this locally using express npm or else.

Regards,

Sorry, a little new to this part of Genesys Cloud. Why must it be available from a browser by typing a URL?

Do you happen to have any information in order to create a script using phython or nodejs? Have other customers done this?
In reviewing the the upload contacts guide I was under the impression this was a complete guide. It might be a good idea to add this element to it so someone could do this end to end.

This is because you need an access token, obtained from an OAuth2 grant flow, in order to invoke the Genesys Cloud Platform API - see Platform API Client Authorization.
The code sample that you are using invokes an OAuth Implicit Grant flow -> his corresponds to the

client.loginImplicitGrant(clientId, redirectUri)

in the code.

"Do you happen to have any information in order to create a script using phython or nodejs? Have other customers done this?"
You can use the one available in this blueprint: https://developer.genesys.cloud/blueprints/analytics-detail-record-metrics-blueprint/#host-and-run-the-nodejs-app-server-locally
The nodejs code/files which take care of this are index.js and package.json in this directory: analytics-detail-record-metrics-blueprint/src at main · GenesysCloudBlueprints/analytics-detail-record-metrics-blueprint · GitHub

Regards,

Hello Jerome. Thank you for that. I've gotten it successfully integrated as an app, but still not getting the contact lists to populate. Man, what am I missing? It all looks correct, but still not getting anything.

<style type='text/css'>
	.row {
		margin-top: 8px;
	}
</style>

<script src="https://code.jquery.com/jquery-3.3.1.min.js" integrity="sha256-FgpCb/KJQlLNfOu91ta32o/NMZxltwRo8QtmkMRdAu8=" crossorigin="anonymous"></script>
<script src="https://sdk-cdn.mypurecloud.com/javascript/157.0.0/purecloud-platform-client-v2.min.js"></script>

<script type='text/javascript'>
	// This client ID expects the redirect URL to be http://localhost:8080/
	const clientId = '945f7819-983d-4996-9595-e466fab0a604';
	//const redirectUri = window.location.href;
	const redirectUri = http://localhost:3000;

	// Set Genesys Cloud objects
	const platformClient = require('platformClient');
	const client = platformClient.ApiClient.instance;
	const outboundApi = new platformClient.OutboundApi();

	// Set Genesys Cloud settings
	client.setEnvironment('usw2.pure.cloud');
	client.setPersistSettings(true, 'test_app');

	$(document).ready(() => {
		// Handle file selection
		$('#file-input').change((event) => {
			uploadContacts(event.target.files[0]);
		});

		// Authenticate with Genesys Cloud
		client.loginImplicitGrant(clientId, redirectUri)
			.then(() => {
				console.log('Logged in');

				// Get a list of contact lists
				return outboundApi.getOutboundContactlists({ pageSize: 100 });
				//return apiInstance.getOutboundContactlists({ pageSize: 100 });
			})
			.then((contactLists) => {
				// Add contact lists to the UI
				contactLists.entities.forEach((contactList) => {
					$('#contactLists').append($('<option></option>')
						.attr('value', contactList.id)
						.text(contactList.name));
				});
			})
			.catch((err) => console.error(err));
	});

	function uploadContacts(file) {
		const contactListId = $('#contactLists').find(':selected').val();
		console.log('contactListId: ' + contactListId);

		// Build form data object (POST request body)
		const data = new FormData();
		data.append('id', contactListId);
		data.append('file', file);
		data.append('fileType', 'contactlist');
		data.append('contact-id-name', 'Contact ID');

		// Build request
		const request = {
			url: 'https://apps.mypurecloud.com/uploads/v2/contactlist',
			headers: {
				Authorization: 'bearer ' + client.authData.accessToken
			},
			data: data,
			processData: false,
			contentType: false
		};

		// Upload contact list
		$.post(request)
			.then((data) => {
				console.log('UPLOAD SUCCESS');
				console.log(data);

				// Show success
				$('#upload-result').html($('<div>').addClass('alert alert-success').text('File uploaded successfully'));
			})
			.catch((err) => {
				let message = $('<span>').text('File uploaded failed!');
				if (err.status) {
					message.append($('<p>').text(`${err.status} ${err.statusText}`));
					message.append($('<p>').text(`Response body: ${err.responseText}`));
				}
				console.error(err);

				// Show failure
				$('#upload-result').html($('<div>').addClass('alert alert-danger').html(message));
			});
	}
</script>
Contact Lists
Choose file to upload

Not knowing what error shows up in console or network traces, I can't say with just a screenshot.
I would suggest that you start this page in a different tab (not as embedded app/frame) to make it easier to distinguish what comes from the page and what comes from Genesys Desktop.

So I assume you are now running a local nodejs web server on port 3000.

If yes, that means the OAuth Client with clientId = '945f7819-983d-4996-9595-e466fab0a604' must be defined in your Genesys Cloud configuration with grant type set to "Token Implicit Grant", with http://localhost:3000 defined in Authorized redirect URIs, and with at least "outbound" and "upload" defined in Scope.
The user (logged in Genesys Cloud Desktop and in this page) must have the necessary outbound permissions (permissions assigned to the user via Roles - which contains permissions - in Genesys Cloud configuration).
You must have some existing Contact Lists defined in your configuration (I mean in Genesys Cloud Desktop - Admin - Outbound - List Management - Contact Lists tab).

If that's the case, then I'd recommend to check what console logs or Network traces (in case there are some HTTP error responses) say when loading this page.

Regards,

I can confirm that I am running a nodejs web server on port 3000.

I can confirm that the OAuth Client with that clientid is set to the "Token Implicit Grant" with the http://localhost:3000 defined.

My user has all outbound permissions and I have 3 contact lists within List Management.

In opening this in a different tab, below is the error/info I recieve. If you need more, just let me know.

  1. Audit usage of navigator.userAgent, navigator.appVersion, and navigator.platform

  2. A page or script is accessing at least one of navigator.userAgent , navigator.appVersion , and navigator.platform . Starting in Chrome 101, the amount of information available in the User Agent string will be reduced.

To fix this issue, replace the usage of navigator.userAgent , navigator.appVersion , and navigator.platform with feature detection, progressive enhancement, or migrate to navigator.userAgentData .

Note that for performance reasons, only the first access to one of the properties is shown.

  1. AFFECTED RESOURCES
1. 1 source

  1. bundle.js:1
    • Learn more: User-Agent String Reduction
  1. 3

Indicate whether to send a cookie in a cross-site request by specifying its SameSite attribute

  1. Because a cookie’s SameSite attribute was not set or is invalid, it defaults to SameSite=Lax , which prevents the cookie from being sent in a cross-site request. This behavior protects user data from accidentally leaking to third parties and cross-site request forgery.

Resolve this issue by updating the attributes of the cookie:

* Specify `SameSite=None` and `Secure` if the cookie should be sent in cross-site requests. This enables third-party use.
* Specify `SameSite=Strict` or `SameSite=Lax` if the cookie should not be sent in cross-site requests.
  1. AFFECTED RESOURCES
1. 3 cookies

  1. |Name|Domain & Path|

| --- | --- |
|_ga|.mypurecloud.com/|
|_gcl_au|.mypurecloud.com/|
|_gid|.mypurecloud.com/|

2. 1 request

  1. purecloud-platform-client-v2.min.js
    • Learn more: SameSite cookies explained

Update on this. Looks like correcting one thing in the code has allowed the contact list to show, on both the separate tab and within the app. But, I am now getting the below error when attaching the file.

Looking into this further, I got past this and the upload shows completed without errors. When going to List Management though, it is showing the "Import Failed: One or more entries have a missing custom id value


This looks like it has been an issue before. Failed to import for the contact list because the custom id was invalid. @tim.smith looks like you had some replies. Anything ever come of that?

I don't know if the poster in that thread opened a case with Care and if there was any resolution if they did. If you believe you have the same issue as that thread, I would recommend opening a case with Care to investigate.

I don't believe I have access to open a Care ticket, but looks like it is the same. I am able to successfully upload it through the UI, but unable to when I use the API. Hmmm.

Hello,

Just found the reason why "Import Failed" is displayed on the calling list when importing from the code sample.

The problem is not in the code (at least when pointing to US East region - i.e. mypurecloud.com).
The issue for me was with the sample data - I have used the example-contacts.csv file in the github repo on tutorials.

Sample setup and modifications:

So what's the problem with Import Failed error.
The sample code is in fact written to leverage one optional capability, available with Contact List -> Unique Identifier Column -> see Step 6 on this article
That's what is set with: data.append('contact-id-name', 'Contact ID');
The Contact ID column, from the example-contacts.csv, is used as Unique Identifier Column.

But, as described in the Resource Center article:

Custom contact list ID columns cannot contain special characters, such as a backslash. Compose a unique identifier from alphanumeric characters, excluding spaces , backslashes , and these special characters that don’t work with URLs:
– . _ ~ : / ? # [ ] @ ! $ & ‘ ( ) * + , ; =

And the Contact ID's values, in example-contacts.csv, contain a "."

You can in fact replicate the same behavior when creating or updating the contact list in Genesys Desktop - under Advanced, set Contact ID as Unique identifier column.
The same error/warning will be displayed in the UI.

How to solve this?
There are different ways depending on what you want to achieve.

If you want to keep Contact ID as Unique Identifier Column.
Then you just need to modify the Contact ID values in your csv, so that these special characters are not used.
To leverage Unique Identifier Column feature, you would also need to specify "Contact ID" as Unique Identifier Column when creating the Calling List initially (Genesys Desktop UI). If you don't do this, the code sample will still work and not generate an error.

If you don't want to keep Contact ID as Unique Identifier Column (and you don't want to remove these special characters from Contact ID values), then, you just need to modify the index.html and comment/remove data.append('contact-id-name', 'Contact ID');

Hope this clarifies.

Regards,

Hey Jerome. That was what it was. As soon as I deleted that row, it imported. A big thanks for you and @tim.smith for the assistance!