> This page is for Developers.

> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.emnify.com/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.emnify.com/_mcp/server.

# Troubleshoot device connectivity in Zendesk Support

> Build a Zendesk app to help your team troubleshoot device connectivity using the emnify REST API

Create a Zendesk Support app in JavaScript to help your team troubleshoot customer device connectivity issues using the emnify REST API.
This app enables your team to:

* [View device and SIM details (including statuses)](#view-device-and-sim-details)
* [View the device's connectivity status](#view-the-devices-connectivity-status)
* [Refresh device statuses](#refresh-device-statuses)
* [Reset device connectivity](#reset-device-connectivity)

## Prerequisites

### Accounts and permissions

* A Zendesk account on a [Support Professional plan or higher](https://www.zendesk.com/pricing/support/).
  You also need to be an [Administrator](https://support.zendesk.com/hc/en-us/articles/4408883763866-Understanding-Zendesk-Support-user-roles#topic_evb_dbp_cc) or [Account owner](https://support.zendesk.com/hc/en-us/articles/4408883763866-Understanding-Zendesk-Support-user-roles#topic_fjb_ffq_cc).
* An [emnify account](/quickstart) with at least one device and assigned SIM.
* A valid application token from your emnify Workspace, created via the [emnify Portal](/portal/application-tokens) or the [emnify REST API](/developers/auth/application-tokens).
  > **Note**
  >
  > Only **Administrators** can create application tokens in the emnify Portal.
  > See [Roles and permissions](/portal/roles#user-management) for more information.

### Tools and software

* Basic JavaScript knowledge
  > **Info**
  >
  > All the necessary code is provided, but understanding JavaScript helps you customize the app.
* [Node.js 14.17.3 or later](https://nodejs.org/en/download/) installed.
* A web browser, such as Chrome or Firefox, that supports mixed HTTP and HTTPS content.
  This is required to run the app locally.
  > **Warning**
  >
  > Safari doesn't support mixed content and cannot run Zendesk apps locally.

Additionally, create a custom field for your SIM identifier in Zendesk Support as outlined in the next section.

## Create a custom ticket field for your SIM identifier

This app relies on a unique SIM identifier to retrieve data from the emnify REST API.
Create a custom field in Zendesk Support to store and manage this identifier.
Your team should be familiar with this field and use it regularly.
This identifier must be:

* Usable for filtering in the [List Endpoints API](/developers/api/endpoint/get-endpoints).
* Unique enough that it responds with a single device.

> **Info**
>
> Devices are referred to as "endpoints" in the emnify REST API.

For example, you can use:

* [eUICC Identifier (EID)](https://www.emnify.com/iot-glossary#eid)
* [Integrated Circuit Card Identifier (ICCID)](https://www.emnify.com/iot-glossary#iccid) with or without the final [Luhn checksum digit](https://www.emnify.com/iot-glossary#luhn-checksum-digit)
* [International Mobile Equipment Identity (IMEI)](https://www.emnify.com/iot-glossary#imei)
* A serial number or custom identifier that maps to the device name in emnify
  > **Warning**
  >
  > The emnify Portal doesn't allows for duplicate device names.
  > If you choose this option, you need to ensure that the device name is unique and follows a consistent format.
  > For example, `device-12345`, `D12345`, `12345`, etc.

> **Note**
>
> This guide uses the **ICCID** without the final Luhn checksum digit.

Here are the steps to create a custom field in Zendesk Support:

Follow the [Zendesk Help guide](https://support.zendesk.com/hc/en-us/articles/4408883152794-Adding-custom-ticket-fields-to-your-tickets-and-forms) to add a new custom ticket field named **ICCID**.
Select **Number** (without decimals) as the field type.

Add a **Display name** to clarify the field's purpose for your team.

> **Note**
>
> Optionally, add a **Description** for admins to provide more context.
> If the field is visible to customers, you can include a separate description for them.
>
> Example description for ICCID:
>
> ```txt wordWrap
> Integrated circuit card identifier (ICCID), a 19-digit code used to identify a SIM card. This value doesn't include the final Luhn check digit.
> ```

Complete the configuration and save the custom field by following the [Adding custom ticket fields to your tickets and forms Zendesk guide](https://support.zendesk.com/hc/en-us/articles/4408883152794-Adding-custom-ticket-fields-to-your-tickets-and-forms).

After saving, the field appears in the Admin Center under **Objects and rules** > **Tickets** > **Fields**.

> **Tip**
>
> Note down the **Field ID** for the custom field.
> You need this ID to [fetch the ICCID value](#pull-the-custom-field-value-from-ticket-data).

## Create a new Zendesk app

The Zendesk Apps Framework (ZAF) allows you to customize and extend your agent's interface.
For more information, see [Apps in the Zendesk developer documentation](https://developer.zendesk.com/documentation/apps/).

### Generate the starter files

Follow these steps to set up a new Zendesk app:

[Create the app files](https://developer.zendesk.com/documentation/apps/getting-started/zendesk-app-quick-start/#creating-the-app-files).
Use the following values when prompted:

* **Directory**: emnify-support-app
* **Author's name**: Your name
* **Author's email**: Your email address
* **Author's website**: Leave blank and press Enter
* **App name**: emnify Support App

After completing this step, the Zendesk Command Line Interface (ZCLI) generates starter files in the `emnify-support-app` folder.

[Check the app's location](https://developer.zendesk.com/documentation/apps/getting-started/zendesk-app-quick-start/#checking-the-app-location).

[Run the app locally](https://developer.zendesk.com/documentation/apps/getting-started/zendesk-app-quick-start/#running-the-app-locally) to test it in your Zendesk Support account before deployment.

### Clean up the default code

#### Review the HTML file

A Zendesk app runs inside an iframe.
Static and dynamic elements are defined in the `iframe.html` file located in `emnify-support-app/assets/iframe.html`.

Open the `iframe.html` file in a text editor.

Remove the `h2` element:

```html
<h2 class="u-semibold u-fs-xl">Hello, World!</h2>
```

Ensure the file includes the ZAF SDK library with the following script tag:

```html
<script src="https://static.zdassets.com/zendesk_app_framework_sdk/2.0/zaf_sdk.min.js"></script>
```

#### Set up the JavaScript file

Create a file named `main.js` in the `assets` folder.

Add this asynchronous function to `main.js` to initialize the ZAF JavaScript API client:

```javascript
(async function () {
  const client = ZAFClient.init();
})();
```

Add a line to resize the app UI:

```javascript {5}
(async function () {
  // Initialize the Zendesk app framework (ZAF) client
  const client = ZAFClient.init();
  // Resize the app UI
  client.invoke("resize", { width: "100%", height: "500px" });
})();
```

In the `iframe.html` file, delete the inline script initializing the ZAF client and replace it with:

```html
<script src="main.js"></script>
```

> **Check**
>
> Make sure this script tag is placed after the one for the ZAF SDK.

The body of `iframe.html` should now look as follows:

```html
<body>
  <script src="https://static.zdassets.com/zendesk_app_framework_sdk/2.0/zaf_sdk.min.js"></script>
  <script src="main.js"></script>
</body>
```

### Design the app's user interface

#### Add a footer

Add a footer with a **Report bugs** link:

In `iframe.html`, add the following footer block before the script tags:

```html
<footer>
  <a href="https://github.com/org/emnify-support-app/issues/new" target="_blank">Report bugs</a>
</footer>
```

Refresh the app in Zendesk to see the changes.

### Add styles

This guide uses [Bootstrap CSS](https://getbootstrap.com/docs/3.4/css/) for styling.

Create a new file named `main.css` in the `assets` folder.

Replace the Zendesk Garden style sheet link in `iframe.html` with:

```html
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css"
crossorigin="anonymous">
```

Add a link to `main.css`:

```html
<link rel="stylesheet" href="main.css">
```

Add the following styles to `main.css` for the app interface:

```css maxLines=15
/* General Body Styles */
body {
  color: #343a40;
  font-family: Arial, sans-serif;
  font-size: 14px;
  margin: 0;
  padding: 0.5rem;
}

/* Button Container */
.button-container {
  display: flex;
  justify-content: center; /* Center buttons horizontally */
  flex-wrap: wrap; /* Allow buttons to wrap on small screens */
  gap: 0.5rem; /* Increase spacing between buttons */
  margin: 1rem 0; /* Add more vertical spacing above and below the buttons */
}

/* Buttons */
.btn {
  font-size: 12px;
  padding: 0.25rem 0.5rem;
  border-radius: 0.25rem;
}

.btn-secondary {
  background-color: #6f42c1;
  border-color: #6f42c1;
  color: #fff;
}

.btn-secondary:hover {
  background-color: #5a379c;
  border-color: #5a379c;
}

.btn-danger {
  background-color: #e83e8c;
  border-color: #e83e8c;
  color: #fff;
}

.btn-danger:hover {
  background-color: #d63384;
  border-color: #d63384;
}

/* Status Text */
#reset-connectivity-status {
  margin-top: 0.5rem;
  font-size: 12px;
  text-align: center; /* Ensure text is centered under buttons */
  color: #6c757d;
  display: block; /* Ensure it appears as a block element */
}

/* Info Blocks */
.info-blocks {
  margin-top: 1rem;
  padding: 0.5rem;
  background-color: #ffffff;
  border: 1px solid #dee2e6;
  border-radius: 0.25rem;
  box-shadow: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075);
}

.info-blocks table {
  width: 100%;
  font-size: 12px;
}

.info-blocks td {
  padding: 0.15rem 0.1rem;
}

/* Footer */
footer {
  font-size: 10px;
  text-align: center;
  margin-top: 1rem;
}

footer a {
  color: #6f42c1;
  text-decoration: none;
}

footer a:hover {
  color: #5a379c;
}
```

### Change the logo

Refer to [Changing the logo](https://developer.zendesk.com/documentation/apps/build-an-app/build-your-first-support-app/part-2-designing-the-user-interface/#changing-the-logo) in the Zendesk developer documentation for details on replacing the default logo with your own.

## Set up authentication for the emnify REST API

> **For demonstration purposes only**
>
> The authentication token entered during app installation must be refreshed regularly to keep the app working.
> If the token becomes invalid, the app displays an error.
> To resolve this, [update the app settings](https://support.zendesk.com/hc/en-us/articles/4409155972378-Managing-your-installed-apps#topic_kly_zt4_ym) in the Zendesk Admin Center and reenter the `authToken`.
>
> This approach ensures that the authentication token isn't exposed to the browser, which is especially important for organizations where access to the emnify Portal is restricted to specific users.
> However, this is a temporary solution designed for demonstration purposes and isn't ideal for production use.
>
> For a robust implementation, consider [building a server-side Zendesk app](https://developer.zendesk.com/documentation/apps/build-an-app/building-a-server-side-app/part-1-core-concepts/) that uses the application token to generate and refresh the `auth_token` dynamically.
> If you want to read a guide for creating a server-side Zendesk apps and integrating with the emnify REST API, let the team know via [Canny](https://emnify.canny.io/).

To access the emnify REST API, you need to authenticate your app.
The first step is to use your application token and the [Retrieve Authentication Token](/developers/api/authentication/authenticate) call to generate a JSON Web Token (JWT) `auth_token` that authenticates further requests to the emnify REST API.

Follow [Authenticate with application tokens](/developers/auth/application-tokens) to generate your `auth_token`.

> **Note**
>
> This API path has a rate limit of 100 requests per IP in a 5-minute window.
> For more information, see [Rate limits](/developers/api-guidelines/rate-limits).

Save the `auth_token` for later use in your app.

### Define installation settings

Next, define the app's [installation settings](https://developer.zendesk.com/documentation/apps/getting-started/setting-up-new-apps/#defining-installation-settings) to use the `auth_token`:

Open the `assets/manifest.json` file.

Add the following line to allow your app to send requests to the emnify REST API:

```json
{
  ...
  "domainWhitelist": ["cdn.emnify.net"],
  ...
}
```

Define settings using the `parameters` property in the manifest file.
Add the following code:

```json
{
  ...
  "parameters": [
    {
      "name": "authToken",
      "type": "text",
      "required": true,
      "secure": true
    }
  ],
  ...
}
```

> **Info**
>
> For other supported properties, see [Setting properties](https://developer.zendesk.com/documentation/apps/getting-started/setting-up-new-apps/#setting-properties) in the Zendesk developer documentation.

### Use the authentication token in your app

The framework generates an HTML settings page based on the settings defined in the manifest file.
This is where an agent enters the `authToken` when [installing the app](#install-the-app-in-zendesk) or edit it later.

Then, you use the `{{setting.authToken}}` placeholder for the secure setting's value in your app's `request` call.

## View device and SIM details

To help your team understand the condition of a device and SIM card, you can configure your app to fetch their current statuses from the emnify REST API and display them in the app.

This section includes:

1. Creating the necessary templating to display the data.
2. Pulling the ICCID value from the ticket.
3. Using it to fetch the device and SIM details from the emnify REST API.

### Pull the custom field value from ticket data

You can use ZAF to access Zendesk resources from your app.
To retrieve the ICCID from the custom field you created earlier, use Zendesk's [Ticket API](https://developer.zendesk.com/api-reference/ticketing/tickets/tickets/).

Zendesk apps uses a JavaScript library called the [ZAF software development kit (SDK)](https://github.com/zendesk/zendesk_app_framework_sdk) to interact with the Zendesk API.
The SDK includes a `client` that provides an interface between your app and Zendesk Support, enabling various methods to interact with the Apps framework.

#### Key methods in the ZAF client

* **request**: Used to make HTTP requests to any REST API, including emnify's.
* **get**: Used to retrieve data from the framework API, such as custom field values.

#### Steps to extract the ICCID

Use the `client` to access the ticket's custom fields and extract the ICCID.

Open your `main.js` file.

At the top, define global variables for the device ID and ICCID:

```javascript
let deviceId;
let iccidFromCustomField;
```

> **Info**
>
> The `deviceId` is necessary for resetting the connectivity status later.
> If you're not using this feature, you can omit this variable.

Add a function to retrieve the ticket's custom fields and extract the ICCID:

```javascript
await client
  .get("ticket.customField:custom_field_ID")
  .then(data => {
    iccidFromCustomField =
      data["ticket.customField:custom_field_ID"];
});
```

> **Note**
>
> Replace `ID` in `custom_field_ID` with the **Field ID** of the custom field you created earlier.
> Find the ID in the Admin Center under **Objects and rules** > **Tickets** > **Fields** under **Field ID**.

### Create and insert a Handlebars template

Use [Handlebars.js](https://handlebarsjs.com/) to display the fetched details in a structured format.

#### Create a template to display the information

Import the Handlebars library in your app by adding the following script to `iframe.html` (before the script importing your `main.js` file):

```html
<script src="https://cdn.jsdelivr.net/npm/handlebars@4.3.3/dist/handlebars.min.js"></script>
```

Define templates in `script` tags with `type="text/x-handlebars-template"`.
The following is an example to create a table for device and SIM details:

```html {1} maxLines=10
<script id="device-details-template" type="text/x-handlebars-template">
  <strong>Device and SIM Details</strong>
  <table>
    <tr>
      <td>Name:</td>
      <td class="data">{{name}}</td>
    </tr>
    <tr>
      <td>Device ID:</td>
      <td class="data">{{device_id}}</td>
    </tr>
    <tr>
      <td>Device status:</td>
      <td class="data">{{device_status}}</td>
    </tr>
    <tr>
      <td>SIM ID:</td>
      <td class="data">{{sim_id}}</td>
    </tr>
    <tr>
      <td>SIM status:</td>
      <td class="data">{{sim_status}}</td>
    </tr>
    <tr>
      <td>Last updated:</td>
      <td class="data">{{last_updated}}</td>
    </tr>
  </table>
</script>
```

Add a `div` element with the ID `device-sim-details` in `iframe.html` after opening `body` tag:

```html
<div id="device-sim-details" class="info-blocks"></div>
```

> **Check**
>
> If you reload the app, nothing happens because you haven't inserted the template in the HTML yet.

#### Insert the template into your app

> **Info**
>
> Only one template can be rendered at a time.

Add the following function to your `main.js` file to render the template at runtime:

```javascript
const renderTemplate = (
  templateId, // ID of the script template in HTML
  context, // Data to populate the template
  targetId, // ID of the target element to render content
  additionalData = {}, // Additional properties for the template context
) => {
  const source = document.getElementById(templateId).innerHTML;
  const template = Handlebars.compile(source);
  const html = template({ ...context, ...additionalData });
  document.getElementById(targetId).innerHTML = html;
};
```

> **Note**
>
> This function is generic and can be reused to render any Handlebars template in this guide.

### Fetch device and SIM details

Next, identify which device the SIM card is linked to in the emnify platform using the [List Endpoints](/developers/api/endpoint/get-endpoints) API call.
Filter results using the `q` query parameter in the `<field>:<criteria>` format with the ICCID value.

Pass the ICCID to the `showDeviceDetails` and concatenate it to the request URL.

Start by defining the function before `renderTemplate`.
It should accept the ICCID as an input parameter because the ICCID uniquely identifies the SIM card and is used in the API request.

```javascript
const showDeviceDetails = async iccid => {
  // Implementation goes here!
};
```

Since this function makes a network request, it can fail for reasons like network issues or incorrect data.
Use a `try-catch` block to handle both success and failure scenarios.

```javascript
try {
  // Successful response handling
} catch (error) {
  // Error handling
}
```

Inside the `try` block, use the `client.request` method to call the emnify REST API.
Pass the ICCID as part of the query parameter to filter the results and include authentication headers.

* **URL**: The base URL for the emnify Endpoint API.
* **ICCID query**: `q=iccid:${iccid}` filters results to find a specific SIM.
* **Headers**: Include the `Authorization` header with the JWT token.

```javascript
const response = await client.request({
  url: `https://cdn.emnify.net/api/v1/endpoint?q=iccid:${iccid}`,
  type: "GET",
  headers: { Authorization: "Bearer {{setting.authToken}}" },
  secure: true,
  dataType: "json",
});
```

> **Error**
>
> You need to mark `secure` as **true** to ensure sensitive information isn't exposed in the browser.
> For more information, see [Zendesk's `secure` setting property documentation](https://developer.zendesk.com/documentation/apps/getting-started/setting-up-new-apps/#secure).

The API response contains the details you need, such as the device name, ID, SIM status, etc.
Extract these details from the response.

* **Response parsing**: The response is an array of devices.
  Use the first device (`response[0]`).
* **Store device ID**: Save the device ID globally for later use.
* **Format the data**: Create an object with the required fields to display in the template.

```javascript
const lastUpdatedDate = new Date(); // Capture the time of the fetch
const device = response[0]; // Get the first device from the API response

deviceId = device.id; // Store device ID globally

const deviceDetails = {
  name: device.name,
  device_id: device.id,
  device_status: device.status.description,
  sim_id: device.sim.id,
  sim_status: device.sim.status.description,
  last_updated: lastUpdatedDate,
};
```

Pass the `deviceDetails` object to a Handlebars template to render the information in the app.
Use the `renderTemplate` function you created earlier.

```javascript
renderTemplate(
  "device-details-template", // Template ID
  deviceDetails,             // Data to populate
  "device-sim-details"       // Target element ID
);
```

If the API request fails, display an error message using a dedicated Handlebars template (defined in the next section).
The error message can include the status and description to help debug.

```javascript
catch (error) {
  renderTemplate("error-template", error, "device-sim-details");
}
```

Finally, call `showDeviceDetails` in `main.js` with your global ICCID variable:

```javascript
showDeviceDetails(iccidFromCustomField);
```

Putting it all together, the function looks like this:

```javascript maxLines=13
const showDeviceDetails = async iccid => {
  try {
    // Make a GET request to the emnify REST API
    const response = await client.request({
      url: `https://cdn.emnify.net/api/v1/endpoint?q=iccid:${iccid}`,
      type: "GET",
      headers: { Authorization: "Bearer {{setting.authToken}}" },
      secure: true,
      dataType: "json",
    });

    // Process the response
    const lastUpdatedDate = new Date();
    const device = response[0];
    deviceId = device.id;

    const deviceDetails = {
      name: device.name,
      device_id: device.id,
      device_status: device.status.description,
      sim_id: device.sim.id,
      sim_status: device.sim.status.description,
      last_updated: lastUpdatedDate,
    };

    // Render the data into the template
    renderTemplate("device-details-template", deviceDetails, "device-sim-details");
  } catch (error) {
    // Handle errors by rendering the error template
    // Covered in the next section
    renderTemplate("error-template", error, "device-sim-details");
  }
};

showDeviceDetails(iccidFromCustomField);
```

### Add error handling

Insert this Handlebars template in `iframe.html` after your details template:

```html
<script id="error-template" type="text/x-handlebars-template">
  <p>{{status}}
    -
    {{statusText}}
    error. Please report a bug at the link below.</p>
</script>
```

> **Info**
>
> The JavaScript logic for error handling is already included in the `showDeviceDetails` function.

### Optional: Format the dates

You can format the date and time to display to be more human-readable.

Locate the following line in your `showDeviceDetails` function:

```javascript
const lastUpdatedDate = new Date();
```

Replace it with:

```javascript
const currentTime = new Date();
const lastUpdatedDate = formatDate(currentTime);
```

Add this function to your `main.js` and adjust the options as needed:

```javascript
const formatDate = dateString => {
const options = {
  weekday: "short",
  day: "2-digit",
  month: "short",
  year: "numeric",
  hour: "2-digit",
  minute: "2-digit",
  timeZoneName: "short",
};
return new Date(dateString).toLocaleString("en-US", options);
};
```

Here's an example date rendered with these options: `Thu, Jan 16, 2025, 12:56 PM GMT+1`

## View the device's connectivity status

Now that you have the device details and defined the `deviceId`, you can fetch the device's connectivity status from emnify using the [Endpoint Connectivity Status](/developers/api/endpoint/endpoint-connectivity-by-id-get) API call.

### Set up the Handlebars template

Before writing the JavaScript logic, you must define the Handlebars template in your HTML.
This template renders the connectivity data dynamically at runtime.

Add this script to your `iframe.html` file:

```html
<script id="connectivity-template" type="text/x-handlebars-template">
  <strong>Device Connectivity</strong>
  <table>
    <tr>
      <td>Status:</td>
      <td class="data">{{connectivity_status}}</td>
    </tr>
    <tr>
      <td>Last updated:</td>
      <td class="data">{{last_updated}}</td>
    </tr>
  </table>
</script>
```

Add a `div` element with the ID `device-connectivity-status` after the opening `body` tag:

```html
<div id="device-connectivity-status" class="info-blocks"></div>
```

### Fetch the device's connectivity status

Begin by defining the function in `main.js`.
It accepts `deviceId` as a parameter, which is the unique identifier for the device obtained from the emnify REST API.

```javascript
const showConnectivityStatus = async deviceId => {
  // Implementation goes here!
};
```

As with `showDeviceDetails`, network requests in this function may fail.
Use a `try-catch` block to handle both success and error scenarios.

```javascript
try {
  // Successful response handling
} catch (error) {
  // Error handling
}
```

Inside the `try` block, use `client.request` to call the emnify REST API for the device's connectivity status.

* **URL**: The base URL for the emnify Endpoint API entrypoint.
* **Headers**: Include the `Authorization` header with the JWT token.

```javascript
const response = await client.request({
  url: `https://cdn.emnify.net/api/v1/endpoint/${deviceId}/connectivity`,
  type: "GET",
  headers: { Authorization: "Bearer {{setting.authToken}}" },
  secure: true,
  dataType: "json",
});
```

The response contains the device's connectivity status.
Extract the status description and format the current date to show when the information was last updated.

* Use the `formatDate` function (defined earlier) to format the date.

```javascript
const currentTime = new Date();
const lastUpdatedDate = formatDate(currentTime);

const connectivityData = {
  connectivity_status: response.status.description,
  last_updated: lastUpdatedDate,
;
```

Pass the `connectivityData` object to the `renderTemplate` function to populate and display the data in the app.

```javascript
renderTemplate(
  "connectivity-template",   // ID of the Handlebars template
  connectivityData,          // Data object to populate the template
  "device-connectivity-status" // Target element ID
);
```

If the API request fails, render an error template.
This ensures that the agent sees an informative message if something goes wrong.

```javascript
catch (error) {
  renderTemplate("error-template", error, "device-connectivity-status");
}
```

Call `showConnectivityStatus` from within `showDeviceDetails` after retrieving the `deviceId`.
This ensures that the connectivity status is displayed as soon as the device details are fetched.

```javascript
const showDeviceDetails = async iccid => {
  try {
    ...
  showConnectivityStatus(deviceId);
  } catch (error) {
    ...
  }
```

Here's the completed `showConnectivityStatus` function:

```javascript maxLines=13
const showConnectivityStatus = async deviceId => {
  try {
    // Fetch connectivity details from the emnify API
    const response = await client.request({
      url: `https://cdn.emnify.net/api/v1/endpoint/${deviceId}/connectivity`,
      type: "GET",
      headers: { Authorization: "Bearer {{setting.authToken}}" },
      secure: true,
      dataType: "json",
    });

    // Format the current time
    const currentTime = new Date();
    const lastUpdatedDate = formatDate(currentTime);

    // Prepare data for the template
    const connectivityData = {
      connectivity_status: response.status.description,
      last_updated: lastUpdatedDate,
    };

    // Render the data using Handlebars
    renderTemplate(
      "connectivity-template",
      connectivityData,
      "device-connectivity-status",
    );
  } catch (error) {
    // Render an error message if the API call fails
    renderTemplate("error-template", error, "device-connectivity-status");
  }
};
```

## Refresh device statuses

This section explains how to add and implement buttons under each information block, allowing agents to manually refresh the device and connectivity statuses.

### Update the HTML \[#update-the-html-refresh-statuses]

Incorporate buttons into your HTML structure in `iframe.html` under each information block:

```html {5-9, 13-17}
<body>
  ...
  <!-- Connectivity Status Section -->
  <div class="info-blocks" id="device-connectivity-status"></div>
  <div class="button-container">
    <button id="refresh-connectivity" class="btn btn-secondary btn-sm">
      Refresh Connectivity Status
    </button>
  </div>

  <!-- Device and SIM Details Section -->
  <div class="info-blocks" id="device-sim-details"></div>
  <div class="button-container">
    <button id="refresh-device-details" class="btn btn-secondary btn-sm">
      Refresh Device and SIM Details
    </button>
  </div>
  ...
</body>
```

### Attach event listeners in JavaScript

Event listeners connect the buttons to the respective functions, ensuring that clicking them triggers data fetching and updates.
Add the following code to your `main.js` file:

```javascript
// Event listeners for agent actions
document
  .getElementById("refresh-connectivity")
  .addEventListener("click", () => showConnectivityStatus(deviceId));

document
  .getElementById("refresh-device-details")
  .addEventListener("click", () => {
    showDeviceDetails(iccidFromCustomField);
  });
```

How it works:

* Each button is uniquely identified using an id (`refresh-connectivity` and `refresh-device-details`).
* On clicking the "Refresh Connectivity Status" button, the `showConnectivityStatus` function is invoked using the stored `deviceId`.
* The "Refresh Device and SIM Details" button triggers the `showDeviceDetails` function, passing the ICCID (`iccidFromCustomField`).

## Reset device connectivity

This section explains how to add and implement a button under the **Device Connectivity Status** block, allowing agents to reset connectivity for a device using emnify's [Reset Endpoint Connectivity](/developers/api/endpoint/update-endpoint-connectivity-by-id) API call.
The button displays status updates during the process and refresh the connectivity status once the reset is complete.

### Update the HTML \[#update-the-html-reset-connectivity]

Add the "Reset Connectivity" button and a status `span` for feedback in your `iframe.html` file:

```html {8-11}
<body>
  ...
  <div class="info-blocks" id="device-connectivity-status"></div>
  <div class="button-container">
    <button id="refresh-connectivity" class="btn btn-secondary btn-sm">
      Refresh Connectivity Status
    </button>
    <button id="reset-connectivity" class="btn btn-danger btn-sm">
      Reset Connectivity
    </button>
    <span id="reset-connectivity-status" class="ml-2 text-muted"></span>
  </div>
  ...
</body>
```

### Implement the reset feature

Begin by defining the `resetConnectivity` function.
By the end, this function:

* Updates the UI to indicate the reset process has started.
* Sends a `PATCH` request to the connectivity reset API entrypoint.
* Handles the response and update the UI accordingly.
* Display the updated connectivity status after a delay.

```javascript
const resetConnectivity = async () => {
  // Implementation goes here!
};
```

Inside the function, access the status span element (`reset-connectivity-status`) to provide feedback to the agent.
Update the text to "Processing..." and style it with a warning class to visually indicate that the reset process is ongoing.

```javascript
const statusSpan = document.getElementById("reset-connectivity-status");
statusSpan.innerText = "Processing...";
statusSpan.classList.add("text-warning");
```

The emnify REST API requires a payload with `location` and `pdp_context`.
For this guide, set these values to `null`.
Serialize this data into JSON format using `JSON.stringify`.

```javascript
const payload = JSON.stringify({
  location: null,
  pdp_context: null,
});
```

Next, prepare the headers for the API request.
Include:

* An `Authorization` header with the bearer token for authentication.
* A `Content-Type` header specifying that the payload is JSON.

By storing these headers in a variable, you maintain compatibility with the Zendesk app framework.

```javascript
const headers = {
  Authorization: "Bearer {{setting.authToken}}",
  "Content-Type": "application/json",
};
```

Use the `client.request` method to send a `PATCH` request to the emnify REST API.
The `deviceId` is passed in the URL to specify the device to be reset.

```javascript
await client.request({
  url: `https://cdn.emnify.net/api/v1/endpoint/${deviceId}/connectivity`,
  type: "PATCH",
  headers: headers,
  data: payload,
  secure: true,
});
```

If the request is successful:

1. Update the status span with a success message, informing the agent that the reset process has started.
2. Advise the agent to keep the tab open while waiting for the updated status.
3. Use a `setTimeout` function to fetch the new connectivity status after two minutes.
   > **Warning**
   >
   > When reset connectivity is triggered, the device detaches from the network and waits for the modem to connect again.
   > Depending on the device and network conditions, this process can take a few seconds to a few minutes.
   > The connectivity status fetched may not be accurate unless you add a delay.

```javascript
statusSpan.innerText =
  "Connectivity reset. Updating status... Don't close this tab. The process may take a few minutes.";
setTimeout(() => {
  showConnectivityStatus(deviceId); // Refresh the connectivity status
  statusSpan.innerText = "Updated.";
  statusSpan.classList.remove("text-warning");
  statusSpan.classList.add("text-success");
}, 120000); // Delay for 2 minutes
```

To handle potential errors, the catch block needs to:

* Extract details from the error response (if available).
* Update the status span with a meaningful error message and apply a danger class to indicate failure.
* Log the error to the console for debugging purposes.

```javascript
} catch (error) {
  const errorDetails = error.responseJSON
    ? `${error.responseJSON.message} (Status: ${error.status})`
    : error.statusText || "Unknown error occurred";

  console.error("Error resetting connectivity:", error);
  statusSpan.innerText = `Failed to reset connectivity: ${errorDetails}`;
  statusSpan.classList.remove("text-warning");
  statusSpan.classList.add("text-danger");
}
```

Finally, attach the `resetConnectivity` function to the "Reset Connectivity" button using an event listener.
This ensures that the function is triggered when an agent clicks the button.

```javascript
document
  .getElementById("reset-connectivity")
  .addEventListener("click", resetConnectivity);
```

Here's how the complete `resetConnectivity` function looks:

```javascript maxLines=13
const resetConnectivity = async () => {
  const statusSpan = document.getElementById("reset-connectivity-status");
  statusSpan.innerText = "Processing...";
  statusSpan.classList.add("text-warning");

  try {
    const payload = JSON.stringify({
      location: null,
      pdp_context: null,
    });

    const headers = {
      Authorization: "Bearer {{setting.authToken}}",
      "Content-Type": "application/json",
    };

    await client.request({
      url: `https://cdn.emnify.net/api/v1/endpoint/${deviceId}/connectivity`,
      type: "PATCH",
      headers: headers,
      data: payload,
      secure: true,
    });

    statusSpan.innerText =
      "Connectivity reset. Updating status... Don't close this tab. The process may take a few minutes.";
    setTimeout(() => {
      showConnectivityStatus(deviceId);
      statusSpan.innerText = "Updated.";
      statusSpan.classList.remove("text-warning");
      statusSpan.classList.add("text-success");
    }, 60000);
  } catch (error) {
    const errorDetails = error.responseJSON
      ? `${error.responseJSON.message} (Status: ${error.status})`
      : error.statusText || "Unknown error occurred";

    console.error("Error resetting connectivity:", error);
    statusSpan.innerText = `Failed to reset connectivity: ${errorDetails}`;
    statusSpan.classList.remove("text-warning");
    statusSpan.classList.add("text-danger");
  }
};

document
  .getElementById("reset-connectivity")
  .addEventListener("click", resetConnectivity);
```

## Install the app in Zendesk

If you haven't already, [install ZCLI](https://developer.zendesk.com/documentation/apps/getting-started/using-zcli/#installing-and-updating-zcli) then follow the steps from the Zendesk developer documentation to [Install the private app in Zendesk Support](https://developer.zendesk.com/documentation/apps/build-an-app/build-your-first-support-app/part-5-installing-the-app-in-zendesk-support/).