Updating users in Zendesk via proxy

💡
Disclaimer: all code is provided as is without guarantee or responsibility for any actions or changes as a result of this code.

Requirements

I make use of a Cloudflare Worker account. This is free to create via:

Get started - Dashboard · Cloudflare Workers docs
Follow this guide to create a Workers application using the Cloudflare dashboard.

You'll also need a Zendesk API token, your Zendesk subdomain and an admin email.

Creating the worker

Go to the Cloudflare dashboard and click on Workers and Pages > Overview

Click the Create Worker button to create a new, free, worker. Give your worker a useful name like update_user_tags.

Follow the steps until you reach the successfully deployed screen.

Adding variables

Click Continue to project to setup the worker.

Our worker needs to authenticate to Zendesk.

Navigate to the Settings > Variables and click the Add Variable button.

Add three variables:

  1. subdomain and add your Zendesk subdomain
  2. email and add a valid email from a Zendesk Admin
  3. token and add a Zendesk API token

Press Deploy.

Adding the code

Below you'll find the code for this worker. I annotated it where possible but in essence we do:

  1. We get the ID and tag we want to set from the URL we're calling
  2. We handle CORS and other security requirements of your browser
  3. We get the user's current tags, since updating a tag would overwrite the existing tags
  4. We add our new tag to the current tags
  5. We update the user to set the new tags
  6. We return a 200 OK

Code

export default {
	async fetch(request, env, ctx) {
		const url = new URL(request.url);
		const id = url.searchParams.get('id');
		const tag = url.searchParams.get('tag');

      //Handle CORS
		const corsHeaders = {
			"Access-Control-Allow-Origin": "*",
			"Access-Control-Allow-Methods": "GET, HEAD, POST, OPTIONS",
			"Access-Control-Allow-Headers": "Content-Type",
			"content-type": "application/json"
		};

		if (request.method === "OPTIONS") {
			return new Response(null, {
				headers: {
					"Access-Control-Allow-Origin": "*",
					"Access-Control-Allow-Methods": "GET, HEAD, POST, OPTIONS",
					"Access-Control-Allow-Headers": "Content-Type",		
				}
			})
		}

		//Check for missing metadata
		if (!id || !tag) {
			return new Response('Missing id or tag', { headers: corsHeaders, status: 400 });
		}

		const api_url = `https://${env.subdomain}.zendesk.com/api/v2/users/${id}.json`;
		const auth = 'Basic ' + btoa(`${env.email}/token:${env.token}`);

		try {
          // Fetch the current tags so we don't overwrite them
          const getUserResponse = await fetch(api_url, {
            method: 'GET',
            headers: {
              'Content-Type': 'application/json',
              'Authorization': auth
            }
          });

          //Handle an issue with the tags, eg the user does not exist.
          if (!getUserResponse.ok) {
            const errorData = await getUserResponse.json();
            return new Response(JSON.stringify(errorData), { headers: corsHeaders, status: getUserResponse.status });
          }

          const userData = await getUserResponse.json();
          const currentTags = userData.user.tags;
    
          // Add the new tag to the existing tags
          const updatedTags = new Set(currentTags);
          updatedTags.add(tag);
    
          const putBody = JSON.stringify({
              user: {
                tags: Array.from(updatedTags)
              }
          });
    
          // Update the user's tags
          const updateUserResponse = await fetch(api_url, {
            method: 'PUT',
            headers: {
              'Content-Type': 'application/json',
              'Authorization': auth
            },
            body: putBody
          });
    
          if (updateUserResponse.ok) {
            return new Response('200 OK', { headers: corsHeaders, status: 200 });
          } else {
            const errorData = await updateUserResponse.json();
            return new Response(JSON.stringify(errorData), { headers: corsHeaders, status: updateUserResponse.status });
          }
        } catch (error) {
          return new Response(error.message, { headers: corsHeaders, status: 500 });
    }
  }
};

After deploying your variables in the previous step, click the Edit Code button top right.

You'll be greeted with the editor below. Replace the existing sample code with our Workers code above.

Press Deploy.

Testing

In the right side of the editor we can test our new Worker.

Edit the URL to https://update-user-tags.yourdomain.workers.dev?id=123456789&tag=example

In this URL id is the ID of the user we want to update, and tag is the tag we want to add. Note the yourdomain is whatever domain you picked for your workers instance.

Press send and you should get a 200 OK if all goes well, or an error message if not.

Refresh your user profile in Zendesk and you'll see the tag added.

Help Center

Final step is to combine this with the Help Center.

The code below does the following:

  1. Get the current user ID
  2. Check if we're looking at an article with a specific ID 987654321
  3. Make a call to our worker: https://update-user-tags.yourdomain.workers.dev?id=123456789&tag=example with example the tag we want to set for this article.

Code

  document.addEventListener('DOMContentLoaded', function () {
  // Function to get the current user info using Zendesk API
  function getCurrentUser() {
    return fetch('/api/v2/users/me.json', {
      method: 'GET',
      credentials: 'same-origin'
    })
    .then(response => {
      if (response.ok) {
        return response.json();
      } else {
        throw new Error('Failed to fetch user information');
      }
    })
    .then(data => {
      return data.user;
    })
    .catch(error => {
      throw new Error('User not logged in or unable to fetch user data');
    });
  }

  // Function to check if the current URL contains the specific article ID
  function checkArticleURL() {
    return window.location.href.includes('987654321');
  }

  // Function to make the API call
  function makeAPICall(userId) {
    const apiUrl = `https://example.workers.dev?id=${userId}&tag=example`;
    fetch(apiUrl, {
      method: 'GET'
    })
    .then(response => {
      if (response.ok) {
        console.log('API call success');
      } else {
        console.error('API call failed', response.statusText);
      }
    })
    .catch(error => {
      console.error('API call error', error);
    });
  }

  // Main function to check conditions and make API call
  function main() {
    getCurrentUser()
      .then(user => {
        if (checkArticleURL()) {
          makeAPICall(user.id);
        }
      })
      .catch(error => {
        console.error(error);
      });
  }

  // Run the main function
  main();
});

Code Editor

I added the code at the bottom of the article_page.hbs document, wrapped in a <script></script> tag