What you'll learn today

  1. How to create a Freshworks app from scratch The basic structure of the Freshworks app
  2. A few FDK commands that help you build and run the app.

What you'll need

  1. A Freshdesk trial account
  2. One of the modern web browsers amongst Google Chrome, Firefox, Edge, Safari. This tutorial uses Chrome.
  3. Install the latest Freshworks CLI
  4. A code editor like Visual studio code, Sublime, etc.
  5. Working knowledge of HTML, CSS, JavaScript, and Browser dev tools.

What you'll build

You're going to build an app that runs in the ticket sidebar. When a user clicks on the Say Hello Button, the app shows a notification and creates a ticket in Freshdesk.

App workflow

To give you a sneak peek, we'll cover

  1. The usage of fdk create command
  2. The front-end development of the application
  3. The use of the Ticket API to create a ticket in Freshdesk
  4. The use of the Interface Method to present notifications
  5. The implementation of Installation parameters and set values

Before we jump into the codebase, let's take a moment to extend our understanding by cloning, setting, and running the Say Hello app locally, this also helps you to get a clear idea of what the app does.

And this goes into three easy steps:

Grab the API Key:

Create a trial account for Freshdesk or use your existing account to get your API key. Go to "Profile settings," and then you'd be able to find your API key. Copy the API key to the clipboard.

Clone the project:

Open up your terminal and run the command to clone the project repository.

git clone git@github.com:freshworks-developers/say-hello.git

Alternatively, you can download the project repository zip, and extract it to a directory of your choice.

Run the application:

Open the project folder in your code editor and get into the terminal and execute the following commands

cd Freshdesk

fdk run

You will see the CLI output as follows:

Before viewing the app, let us open the installation page running at port :10001 of the localhost.

http://localhost:10001/custom_configs

The installation page is asking for two fields.

  1. Freshdesk subdomain
  2. Freshdesk API key

For instance, If your account URL is https://puppycat.freshdesk.com, the subdomain you can provide to the Installation page is puppycat. Paste the copied API key in the Freshdesk API key field, and hit Install.

You're just a step away from viewing the Say-Hello app. In Freshdesk, click on the Tickets button from the left sidebar and navigate to a ticket page. Now to view the app, append dev=true at the end of the URL and reload, or you can directly open it.

https://{your_subdomain}.freshdesk.com/tickets/1?dev=true

πŸ“˜ Note:

If you're unable to view the app, you must allow the browser to load insecure content.

  1. Open Freshdesk product URL (Eg.: sample.freshdesk.com)
  2. Click on the lock icon in the address bar in the front.
  3. Click on the Site settings option with a gear icon. It will redirect to Chrome's settings for this particular site.
  4. In the permissions listed, for Insecure content permission, mark it as Allow for this site. This is equivalent to usually giving permission to load the unsafe scripts from the shield icon that would appear in the browser address bar.
  5. Now, refresh the page and the apps will be loaded

There you go, now you can find the app running at the ticket_sidebar location.

Hurray, congratulations on successfully running the application; please do not worry if you can't. You should be able to do it by the end of the tutorial.

If you observe the above GIF closely, the arrow tagged ticket_sidebar points to the location of β€˜ticket_sidebar,' which is one among the locations to run the app in the Ticket Details Page https://subdomain.freshdesk.com/a/tickets/XX (Where XX is the number when the user selects any tickets on the list). Learn more about placeholders in the documentation.

You've successfully experienced how Freshworks Developers runs their app in local development. In the next step of this tutorial, we will look into how you create an app.

Great, now that you know what the Say-Hello app does, getting your hands into creating it from scratch will give you more clarity on how it works.

FDK provides a command to create apps from templates. We shall use that to avoid writing boilerplate code.

Open the terminal window, navigate to the directory you want to create your app, and type the following command. Note that this directory must be empty.

fdk create –-products freshdesk --template your_first_app

Alternatively, you can just run fdk create and use the CLI prompts to choose the product as "Freshdesk and then the template as your_first_app. Why don't you give this a try?

The command creates the template app with directories and files.

To take a close look at the project and the code walkthrough of this app, please read the your-first-app documentation, and you should be able to understand various files and their usages.

πŸ“ Activity (optional):

Now that we created the app with fdk create command, why not run and find how it looks?. Use the section "Setup and Run" to recollect.

Hint: If you wonder where to find the app in Freshdesk, you can find the app location in the manifest.json

Right from here, let us divide the development flow and build things accordingly in specific sections.

In this section, let us refactor the code base and create what content to render in the UI. We'll mostly edit app/index.html

Add the Crayons scripts to the head section in app/index.html.

​​
<script
  type="module"
  src="https://unpkg.com/@freshworks/crayons/dist/crayons/crayons.esm.js"
></script>

<script
  nomodule
  src="https://unpkg.com/@freshworks/crayons/dist/crayons/crayons.js"
></script>

Replace the body section of the script file with:

<body>
  <div class="fw-widget-wrapper">
    <span class="text-with-margin" id="agentName"></span>
    <fw-button class="text-with-margin" id="btnSayHello" color="primary">
      Say Hello πŸ‘‹
    </fw-button>
    <p>to Freshdesk!</p>
    <fw-label
      class="text-with-margin"
      id="newTicketBanner"
      value=""
      color="blue"
    >
    </fw-label>
  </div>
</body>

You should be able to find the front end of the app changes to

This is how your index.html should look

<html>
  <head>
    <script src="https://static.freshdev.io/fdk/2.0/assets/fresh_client.js"></script>
    <link rel="stylesheet" type="text/css" href="styles/style.css" />
    <link
      rel="stylesheet"
      type="text/css"
      href="https://static.freshdev.io/fdk/2.0/assets/freshdesk.css"
    />
    <script
      type="module"
      src="https://unpkg.com/@freshworks/crayons/dist/crayons/crayons.esm.js"
    ></script>
    <script
      nomodule
      src="https://unpkg.com/@freshworks/crayons/dist/crayons/crayons.js"
    ></script>
  </head>

  <body>
    <div class="fw-widget-wrapper">
      <span class="text-with-margin" id="agentName"></span>
      <fw-button class="text-with-margin" id="btnSayHello" color="primary">
        Say Hello πŸ‘‹
      </fw-button>
      <p>to Freshdesk!</p>
      <fw-label
        class="text-with-margin"
        id="newTicketBanner"
        value=""
        color="blue"
      >
      </fw-label>
    </div>
  </body>
  <script src="scripts/app.js"></script>
</html>

When you click, "Say Hello," the app should create a ticket that wishes, and notifies the agent. In this section, we shall code the logic of the application together, and we will be working only in the app.js file inside the scripts folder (app/scripts/app.js).

We will attain this in a few steps:

  1. Fetch the Subdomain from the Installation parameters
  2. Make a POST request to the Tickets API endpoint to create a new ticket
  3. On successfully creating the Ticket, show the Notification and the Banner
  4. Handle the failure scenarios

To start together from the same point, open app.js and clear the code and paste this boilerplate code which helps us to initialize the application.

function onAppActivate() {
  client.data.get("loggedInUser").then(
    function (data) {
      window.agent = data.loggedInUser.contact.name;
      document.getElementById("agentName").textContent = `Hello ${agent},`;
      document
        .getElementById("btnSayHello")
        .addEventListener("fwClick", sayHello);
    },
    function (error) {
      console.error("Error: Failed to fetch loggedInUser details");
      console.error(error);
    }
  );
}

document.onreadystatechange = function () {
  if (document.readyState === "interactive") renderApp();

  function renderApp() {
    var onInit = app.initialized();

    onInit.then(getClient).catch(function (error) {
      console.error("Error: Failed to initialize the app");
      console.error(error);
    });

    function getClient(_client) {
      window.client = _client;
      client.events.on("app.activated", onAppActivate);
    }
  }
};

Now that we have the app initialized, let me introduce you to the sayHello function.

async function sayHello() {
  try {
    // Try creating a ticket
    await createTicket();

    // If successful...
    console.info("Successfully created ticket in Freshdesk");
    showNotification("success", "Successfully created a ticket to say hello");
    showBanner("Freshdesk talks in tickets, check for new ticket.");
  } catch (error) {
    // If failed...
    console.error("Error: Failed to create a ticket");
    console.error(error);
    showNotification("danger", "Failed to create a ticket.");
  }
}

The sayHello function is the complete outline of the application logic, starting with createTicket function, it tries to create a ticket, and as it succeeds it logs in the browser console, and invokes ShowNotification, showBanner functions to indicate the same. Similarly, in any case, if it fails, the logs and notification from the catch block will help us debug. Add the sayHello function to the app.js.

For ticket creation, let us now look into the createTicket function and understand the requirements to perform an API call.

async function createTicket() {
  // Send request
  await client.request.post(ticketURL, options);
}

The single line code with the comment send request is what creates the Ticket. It is a POST request to an API endpoint with the required data. So, it takes two arguments

  1. ticketURL - API Endpoint for creating a ticket
  2. Options - Data of Headers, and Body to perform the request

The Freshdesk's API Endpoint to create a ticket is https://domain.freshdesk.com/api/v2/tickets we must need to replace the domain with your Freshdesk subdomain dynamically with the Installation Parameters values, we shall soon understand and define Installation Parameters in the Configuration sections of this tutorial.

async function createTicket() {
  const ticketURL =
    "https://<%= iparam.freshdesk_subdomain %>.freshdesk.com/api/v2/tickets";

  // Send request
  await client.request.post(ticketURL, options);
}

Here we are setting the value of ticketURL constant with API Endpoint, as the subdomain in the URL has to be dynamically updated from the installation parameters, we are using the templates way of fetching the iparams values, and there's also another way to do this, explore more about retrieving the iparams in the documentation.

Now to create a ticket via API, we must add the Headers and Body to the POST request. The Authorization header gets the API from the Installation parameters page, the Body of the request needs.

async function createTicket() {
  const ticketURL =
    "https://<%= iparam.freshdesk_subdomain %>.freshdesk.com/api/v2/tickets";

  const ticketDetails = {
    email: "puppycat@email.com",
    subject: `Hey ${agent} πŸ‘‹, First HELLO always inspires!`,
    priority: 1,
    description: "πŸ‘‹, Weirdest Hello World ever πŸ‘€!!",
    status: 2,
  };

  const options = {
    headers: {
      Authorization: "Basic <%= encode(iparam.freshdesk_api_key) %>",
      "Content-Type": "application/json",
    },
    body: JSON.stringify(ticketDetails),
    method: "POST",
  };

  // Send request
  await client.request.post(ticketURL, options);
}

With this, the createTicket function is ready.

Finally, to help us understand the success or failure status of the application, there are two more functions as utility, they are showNotificaiton and showBanner

function showNotification(type, message) {
  return client.interface.trigger("showNotify", {
    type: type,
    message: message,
  });
}

function showBanner(value) {
  document.getElementById("newTicketBanner").value = value;
}

The showNotification function uses Interface Method to present notifications, and the showBanner updates the value of a text banner component with a success message.

This is how the final app.js should look

/**
 * Show a notification toast with the given type and message
 *
 * @param {String} type - type of the notification
 * @param {String} message - content to be shown in the notification
 **/
async function sayHello() {
  try {
    // Try creating a ticket
    await createTicket();

    // If successful...
    console.info("Successfully created ticket in Freshdesk");
    showNotification("success", "Successfully created a ticket to say hello");
    showBanner("Freshdesk talks in tickets, check for new ticket.");
  } catch (error) {
    // If failed...
    console.error("Error: Failed to create a ticket");
    console.error(error);
    showNotification("danger", "Failed to create a ticket.");
  }
}

async function createTicket() {
  const ticketURL =
    "https://<%= iparam.freshdesk_subdomain %>.freshdesk.com/api/v2/tickets";

  const ticketDetails = {
    email: "puppycat@email.com",
    subject: `Hey ${agent} πŸ‘‹, First HELLO always inspires!`,
    priority: 1,
    description: "πŸ‘‹, Weirdest Hello World ever πŸ‘€!!",
    status: 2,
  };

  const options = {
    headers: {
      Authorization: "Basic <%= encode(iparam.freshdesk_api_key) %>",
      "Content-Type": "application/json",
    },
    body: JSON.stringify(ticketDetails),
    method: "POST",
  };

  // Send request
  await client.request.post(ticketURL, options);
}

function showNotification(type, message) {
  return client.interface.trigger("showNotify", {
    type: type,
    message: message,
  });
}

function showBanner(value) {
  document.getElementById("newTicketBanner").value = value;
}

function onAppActivate() {
  client.data.get("loggedInUser").then(
    function (data) {
      window.agent = data.loggedInUser.contact.name;
      document.getElementById("agentName").textContent = `Hello ${agent},`;
      document
        .getElementById("btnSayHello")
        .addEventListener("fwClick", sayHello);
    },
    function (error) {
      console.error("Error: Failed to fetch loggedInUser details");
      console.error(error);
    }
  );
}

document.onreadystatechange = function () {
  if (document.readyState === "interactive") renderApp();

  function renderApp() {
    var onInit = app.initialized();

    onInit.then(getClient).catch(function (error) {
      console.error("Error: Failed to initialize the app");
      console.error(error);
    });

    function getClient(_client) {
      window.client = _client;
      client.events.on("app.activated", onAppActivate);
    }
  }
};

Installation Parameters

The Freshworks Developer Kit enables you to define and use parameters whose values app users can set when they install an app. These parameters are termed as installation parameters or iparams, and there exist two different ways to define or set the iparams, learn more about these configurations in the documentation

As of the project, we are performing an API call to create a ticket in which the app needs two important values it can set before making the call.

  1. Subdomain name
  2. API Key

Now we shall define the iparams.json file in a way it collects these values. From the project, navigate to the config folder, open iparams.json, and add the following JSON object.

{
  "freshdesk_subdomain": {
    "display_name": "Freshdesk subdomain",
    "description": "Please enter your Freshdesk subdomain.",
    "type": "domain",
    "type_attributes": {
      "product": "freshdesk"
    },
    "required": true
  },
  "freshdesk_api_key": {
    "display_name": "Freshdesk API key",
    "description": "Please enter your Freshdesk API key",
    "type": "api_key",
    "secure": true,
    "required": true,
    "type_attributes": {
      "product": "freshdesk"
    }
  }
}

App Manifest

The manifest.json file contains information such as the platform version used by a product, locations on the product UI where an app can be rendered (for front-end apps), dependent files used by the app, SMI functions that can be invoked from an app's front-end component, events and the corresponding callbacks (for serverless apps), the Node.js and FDK versions used to build, test, validate, and pack the app, and third-party domains with which the app can interact. This section details the various parameters of the manifest.json file. Learn more about app Manifest at the developer documentation

From the root folder, open manifest.json and add whitelisted domains. As we are making API calls to Freshdesk API, let us add the Freshdesk domain to the list as follows. Note, using * as a sub-domain whitelists all the sites with freshdesk as a domain name.

  "whitelisted-domains": [
    "https://*.freshdesk.com"
  ],

The final manifest.json should look like this:

{
  "platform-version": "2.2",
  "product": {
    "freshdesk": {
      "location": {
        "ticket_sidebar": {
          "url": "index.html",
          "icon": "styles/images/wave.png"
        }
      }
    }
  },
  "whitelisted-domains": ["https://*.freshdesk.com"]
}

CongratulationsπŸŽ‰ on making it so far. You're one step away from finding your app running on the screen, and it is a single-liner.

fdk run

We will test the application by running it and recollect that this is the same process we did in the "Setup and Run" section.

As the above command executes, open the Installation page at http://localhost:10001/custom_configs and set the value of subdomain, API key fields. Now open any of the tickets in Freshdesk and append ?dev=true to the URL, or you can just open https://{your_domain}.freshdesk.com/tickets/1?dev=true.

Ta-da, you will be able to find the app running at the ticket_sidebar.

Freshworks Apps across different products are very similar with some changes, and the fact that if you're able to develop an app for one product, it is very much possible you can build apps for other products of Freshworks too.

To give you an example, take a took at the project structure of Say-Hello app for Freshdesk and FreshService

Irrespective of Freshworks products, the app structure is almost similar, except the differences come in product features, functionality, and behavior level. If you wish to try building the same app for FreshService, jump on to the tutorial: Say-Hello for Freshserive.

⭐ As we also used Freshdesk's Interface Method to create notifications, learn more from the Introduction to Interface methods tutorial.

⭐ Refer to our Sample apps repositories demoing various platform features and capabilities.

Resources:

Be sure to check out the Introductory guide to Freshworks App Development Explore more tutorials at freshworks.dev/tutorials

Help:

Our developer community at https://community.developers.freshworks.com is where you can create topics, make discussions and get a good push when you get stuck.