# Custom Apps

Show a custom widget in the issue sidebar or account notebooks with information pulled from your own system or external sources.

<figure><img src="https://982123570-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F9hXBoCTTNw5flARlUVJf%2Fuploads%2FS1rpYgP8eQVgFdnWTPks%2Fimage.png?alt=media&#x26;token=96f638b3-2104-498b-8bdb-30883fa9939d" alt=""><figcaption></figcaption></figure>

## Setup <a href="#setup" id="setup"></a>

Visit the Settings Page and look for Custom Apps in the Connections section

{% hint style="warning" %}
You will need to have an API endpoint that can be called to fetch the data for the custom app. When configuring the app, the URL of the API endpoint will be a required field. To verify a custom app request came from Pylon, see [here](https://support.usepylon.com/articles/9112357769-how-do-i-verify-an-embedded-app-request-came-from-pylon).
{% endhint %}

1. **Create the App**\
   \
   Create a new custom app by clicking on the “Create App” button. Give your app a descriptive name, provide the endpoint URL of the API that will be called to fetch the data, and configure any custom headers that need to be sent with the request.<br>
2. **Connect the App**\
   \
   For security purposes, Pylon expects the API endpoint to complete a one-time handshake. Pylon will send a GET request to the endpoint with the following query params:

   | Param          | Value                              |
   | -------------- | ---------------------------------- |
   | `request_type` | `verify`                           |
   | `code`         | `0bcb6f583f0e3ca5f034fdd960d6105c` |

   The code will be a unique code that is generated for each handshake. The endpoint should respond with one of the following responses:

   1. Set the `Content-Type` header to `application/json` and respond with a JSON object containing the `code` value that was sent in the query params.

   ```
   HTTP 200 OK
   Content-type: application/json
   {"code":"0bcb6f583f0e3ca5f034fdd960d6105c"}
   ```

   2. Set the `Content-Type` header to `text/plain` and respond with the `code` value as the response body.

   ```
   HTTP 200 OK
   Content-type: text/plain
   0bcb6f583f0e3ca5f034fdd960d6105c
   ```

   \
   Once the API endpoint is configured to respond to the handshake, you can click the “Retry Connection” option in the app dropdown to send the handshake request. If the handshake does not succeed, the status will be `Unverified` and you will need to click the “Retry Connection” option to try again.

{% hint style="warning" %}
If the app is disconnected, the handshake will need to be completed again to reconnect the app.
{% endhint %}

3. **Load and render data from the API**

The final step is to configure the endpoint to return the data that you want to display in the issue sidebar. Pylon will send a GET request to the endpoint with the following query params:

| Param             | Value                                            |
| ----------------- | ------------------------------------------------ |
| `request_type`    | `fetch_data`                                     |
| `app_id`          | id of the custom app                             |
| `widget_id`       | id of the widget associated with the custom app  |
| `issue_id`        | id of the issue that the app is being loaded for |
| `organization_id` | id of the organization that the issue belongs to |
| `account_id`      | id of the account that the issue belongs to      |
| `requester_id`    | user id of the requester of the issue            |
| `requester_email` | email of the requester of the issue              |

The endpoint should respond with a JSON object containing the data that you want to display. For more information about the schema of the data, look at the [schema section](#response-schema) below.

{% hint style="warning" %}
You can use the IDs from the request to fetch any additional information you might need from our [API](https://docs.usepylon.com/pylon-docs/developer/api/api-reference).
{% endhint %}

## Components <a href="#components" id="components"></a>

The custom app can be built using the following components. If the component type is not recognized, the custom app will render an error state.

## [​](https://docs.usepylon.com/developer/custom-apps#card)Card <a href="#card" id="card"></a>

Render a related set of data together. Custom apps can render multiple cards. All components except cards can be used in the card component.

<figure><img src="https://mintlify.s3-us-west-1.amazonaws.com/pylon/images/embedded-app-card.png" alt=""><figcaption></figcaption></figure>

<details>

<summary>Config</summary>

```json
"components": [
  {
    "type": "card",
    "header": {
      "title": "Additional Info"
    },
    "components": [
      {
        "type":     "text",
        "label":    "Requester title",
        "value":    "POTUS",
        "icon_url": "https://upload.wikimedia.org/wikipedia/commons/3/36/Seal_of_the_President_of_the_United_States.svg"
      },
      {
        "type":  "text",
        "label": "Requester address",
        "value": "1600 Pennsylvania Avenue NW\nWashington, DC 20500\nUnited States of America"
      },
      {
        "type":  "date_time",
        "label": "Time of request",
        "value": "2024-07-15T12:34:56Z"
      },
      {
        "type":  "badge",
        "label": "Labels",
        "items": [
          {
            "value": "IMPORTANT",
            "color": "red"
          },
          {
            "value": "POTUS",
            "color": "blue"
          },
        ]
      },
      {
        "type":  "link",
        "label": "Home Page",
        "url":   "https://www.whitehouse.gov/"
      },
      {
        "type":        "button",
        "button_type": "link",
        "label":       "Contact",
        "url":         "https://www.whitehouse.gov/"
      }
    ],
  }
]
```

</details>

## Text

The text value can be a multiline string. Any text that overflows the container will be truncated with an ellipsis.

<br>

<figure><img src="https://mintlify.s3-us-west-1.amazonaws.com/pylon/images/embedded-app-text.png" alt=""><figcaption></figcaption></figure>

<details>

<summary>Config</summary>

```json
"components": [
  {
    "type":     "text",
    "label":    "Requester title",
    "value":    "POTUS",
    "icon_url": "https://upload.wikimedia.org/wikipedia/commons/3/36/Seal_of_the_President_of_the_United_States.svg"
  },
  {
    "type":  "text",
    "label": "Requester address",
    "value": "1600 Pennsylvania Avenue NW\nWashington, DC 20500\nUnited States of America"
  }
]
```

</details>

## [​](https://docs.usepylon.com/developer/custom-apps#date-time)Date Time <a href="#date-time" id="date-time"></a>

Date time value should be in ISO date time format [(RFC 3339)](https://datatracker.ietf.org/doc/html/rfc3339).

<br>

<figure><img src="https://mintlify.s3-us-west-1.amazonaws.com/pylon/images/embedded-app-datetime.png" alt=""><figcaption></figcaption></figure>

<details>

<summary>Config</summary>

```json
"components": [
  {
    "type":  "date_time",
    "label": "Sample Date Time",
    "value": "2024-07-15T12:34:56Z"
  },
  {
    "type":     "date_time",
    "label":    "Date time with icon",
    "value":    "2024-07-15T12:34:56Z",
    "icon_url": "https://upload.wikimedia.org/wikipedia/commons/3/36/Seal_of_the_President_of_the_United_States.svg"
  }
]
```

</details>

## [​](https://docs.usepylon.com/developer/custom-apps#badge)Badge <a href="#badge" id="badge"></a>

The badge component can have any number of badges. If the value of the badge is too long, the text in the badge will be truncated with an ellipsis.

<br>

<figure><img src="https://mintlify.s3-us-west-1.amazonaws.com/pylon/images/embedded-app-badge.png" alt=""><figcaption></figcaption></figure>

<details>

<summary>Config</summary>

```json
"components": [
  {
    "type":  "badge",
    "label": "Annotations",
    "items": [
      {
        "value": "Informative",
        "color": "blue"
      },
      {
        "value": "Warning",
        "color": "orange"
      },
      {
        "value": "Danger!",
        "color": "red"
      },
      {
        "value": "Lorem ipsum dolor sit amet, consectetur adipiscing elit",
        "color": "gray"
      },
      {
        "value": "Success :)",
        "color": "green"
      }
    ]
  }
]
```

</details>

## [​](https://docs.usepylon.com/developer/custom-apps#button)Button <a href="#button" id="button"></a>

The button component can be used to show a link, for example to an admin dashboard.

<br>

<figure><img src="https://mintlify.s3-us-west-1.amazonaws.com/pylon/images/embedded-app-button.png" alt=""><figcaption></figcaption></figure>

<details>

<summary>Config</summary>

```json
"components": [
  {
    "type":        "button",
    "button_type": "link",
    "label":       "Click Me",
    "url":         "http://google.com"
  }
]
```

</details>

[​](https://docs.usepylon.com/developer/custom-apps#link)Link\ <a href="#link" id="link"></a>
---------------------------------------------------------------------------------------------

<figure><img src="https://mintlify.s3-us-west-1.amazonaws.com/pylon/images/embedded-app-link.png" alt=""><figcaption></figcaption></figure>

<details>

<summary>Config</summary>

```json
"components": [
  {
    "type":  "link",
    "label": "Example Link",
    "url":   "http://example.com"
  },
  {
    "type":  "link",
    "label": "Long Link",
    "url":   "https://app.usepylon.com/issues?conversationID=abc"
  }
]
```

</details>

## [​](https://docs.usepylon.com/developer/custom-apps#divider)Divider <a href="#divider" id="divider"></a>

<figure><img src="https://mintlify.s3-us-west-1.amazonaws.com/pylon/images/embedded-app-divider.png" alt=""><figcaption></figcaption></figure>

<details>

<summary>Config</summary>

```json
"components": [
  {
    "type":  "divider"
  }
]
```

</details>

## Response Schema

## Version 1.0.0

<details>

<summary>Schema</summary>

```json
{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "title": "Custom App Data Schema",
  "description": "Data to be displayed in the issue sidebar.",
  "type": "object",
  "properties": {
    "version": {
      "type": "string",
      "description": "The version of the schema.",
      "example": "1.0.0"
    },
    "header": {
      "type": "object",
      "description": "Header information.",
      "properties": {
        "title": {
          "type": "string",
          "description": "Title of the header.",
          "example": "Sample Header"
        },
        "icon_url": {
          "type": "string",
          "description": "URL to the header icon.",
          "format": "uri",
          "example": "http://example.com/icon.png"
        }
      }
    },
    "components": {
      "type": "array",
      "description": "List of components.",
      "items": {
        "oneOf": [
          { "$ref": "#/definitions/cardComponent" },
          { "$ref": "#/definitions/textComponent" },
          { "$ref": "#/definitions/dateTimeComponent" },
          { "$ref": "#/definitions/badgeComponent" },
          { "$ref": "#/definitions/buttonComponent" },
          { "$ref": "#/definitions/linkComponent" },
          { "$ref": "#/definitions/dividerComponent" }
        ]
      }
    }
  },
  "required": ["version", "header", "components"],
  "definitions": {
    "cardComponent": {
      "type": "object",
      "properties": {
        "type": {
          "type": "string",
          "enum": ["card"],
          "description": "Component type.",
          "example": "card"
        },
        "header": {
          "type": "object",
          "description": "Header information.",
          "properties": {
            "title": {
              "type": "string",
              "description": "Title of the card header.",
              "example": "Sample Header"
            },
            "icon_url": {
              "type": "string",
              "description": "URL to the card header icon.",
              "format": "uri",
              "example": "http://example.com/icon.png"
            }
          }
        },
        "components": {
          "type": "array",
          "description": "List of components.",
          "items": {
            "oneOf": [
              { "$ref": "#/definitions/textComponent" },
              { "$ref": "#/definitions/dateTimeComponent" },
              { "$ref": "#/definitions/badgeComponent" },
              { "$ref": "#/definitions/buttonComponent" },
              { "$ref": "#/definitions/linkComponent" },
              { "$ref": "#/definitions/dividerComponent" }
            ]
          }
        }
      },
      "required": ["type", "components"]
    },
    "textComponent": {
      "type": "object",
      "properties": {
        "type": {
          "type": "string",
          "enum": ["text"],
          "description": "Component type.",
          "example": "text"
        },
        "label": {
          "type": "string",
          "description": "Label for the text component.",
          "example": "Sample Text"
        },
        "value": {
          "type": "string",
          "description": "Value for the text component.",
          "example": "This is a sample text"
        },
        "icon_url": {
          "type": "string",
          "description": "URL to the text icon.",
          "format": "uri",
          "example": "http://example.com/text-icon.png"
        }
      },
      "required": ["type", "label", "value"]
    },
    "dateTimeComponent": {
      "type": "object",
      "properties": {
        "type": {
          "type": "string",
          "enum": ["date_time"],
          "description": "Component type.",
          "example": "date_time"
        },
        "label": {
          "type": "string",
          "description": "Label for the date_time component.",
          "example": "Sample Date"
        },
        "value": {
          "type": "string",
          "description": "Value for the date_time component.",
          "format": "date-time",
          "example": "2024-07-15T12:34:56Z"
        },
        "icon_url": {
          "type": "string",
          "description": "URL to the date_time icon.",
          "format": "uri",
          "example": "http://example.com/date-icon.png"
        }
      },
      "required": ["type", "label", "value"]
    },
    "badgeComponent": {
      "type": "object",
      "properties": {
        "type": {
          "type": "string",
          "enum": ["badge"],
          "description": "Component type.",
          "example": "badge"
        },
        "label": {
          "type": "string",
          "description": "Label for the badge component.",
          "example": "Sample Badge"
        },
        "items": {
          "type": "array",
          "description": "Items for the badge component.",
          "items": {
            "type": "object",
            "properties": {
              "value": {
                "type": "string",
                "description": "Value of the badge item.",
                "example": "Badge 1"
              },
              "color": {
                "type": "string",
                "description": "Color of the badge item.",
                "example": "blue",
                "enum": ["blue", "green", "red", "orange", "gray"]
              }
            },
            "required": ["value", "color"]
          }
        }
      },
      "required": ["type", "label", "items"]
    },
    "buttonComponent": {
      "type": "object",
      "properties": {
        "type": {
          "type": "string",
          "enum": ["button"],
          "description": "Component type.",
          "example": "button"
        },
        "label": {
          "type": "string",
          "description": "Label for the button component.",
          "example": "Click Me"
        },
        "button_type": {
          "type": "string",
          "description": "Type of button.",
          "example": "link",
          "enum": ["link"]
        },
        "url": {
          "type": "string",
          "description": "URL for the button.",
          "format": "uri",
          "example": "http://example.com"
        }
      },
      "required": ["type", "label", "button_type", "url"]
    },
    "linkComponent": {
      "type": "object",
      "properties": {
        "type": {
          "type": "string",
          "enum": ["link"],
          "description": "Component type.",
          "example": "link"
        },
        "label": {
          "type": "string",
          "description": "Label for the link component.",
          "example": "Example Link"
        },
        "url": {
          "type": "string",
          "description": "URL for the link.",
          "format": "uri",
          "example": "http://example.com"
        }
      },
      "required": ["type", "label", "url"]
    },
    "dividerComponent": {
      "type": "object",
      "properties": {
        "type": {
          "type": "string",
          "enum": ["divider"],
          "description": "Component type.",
          "example": "divider"
        }
      },
      "required": ["type"]
    }
  }
}
```

</details>

<details>

<summary>Example</summary>

```json
{
  "version": "1.0.0",
  "header": {
    "title": "Sample Header",
    "icon_url": "http://example.com/icon.png"
  },
  "components": [
    {
      "type": "card",
      "components": [
        {
          "type": "text",
          "label": "Nested Text",
          "value": "This is nested text",
          "icon_url": "http://example.com/nested-text-icon.png"
        }
      ]
    },
    {
      "type": "text",
      "label": "Sample Text",
      "value": "This is a sample text",
      "icon_url": "http://example.com/text-icon.png"
    },
    {
      "type": "date_time",
      "label": "Sample Date",
      "value": "2024-07-15T12:34:56Z",
      "icon_url": "http://example.com/date-icon.png"
    },
    {
      "type": "badge",
      "label": "Sample Badge",
      "items": [
        {
          "value": "Badge 1",
          "color": "blue"
        },
        {
          "value": "Badge 2",
          "color": "green"
        }
      ]
    },
    {
      "type": "button",
      "label": "Click Me",
      "button_type": "link",
      "url": "http://example.com"
    },
    {
      "type": "link",
      "label": "Example Link",
      "url": "http://example.com"
    },
    {
      "type": "divider"
    }
  ]
}
```

</details>

## Usage

Embed your custom app within the issue view.

<figure><img src="https://982123570-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F9hXBoCTTNw5flARlUVJf%2Fuploads%2Fme1Axi0i9ydJgesCaP8Z%2FCleanShot%202024-10-05%20at%2011.57.26.png?alt=media&#x26;token=11f25886-40ea-4cc7-b2e1-b4f25d3dc089" alt=""><figcaption></figcaption></figure>
