Create a Panel
Learn how to create Panel Apps for specific pages in Coupa.
Introduction
Panel Apps allow customers to display data from external sources within a UI panel on a given Coupa page. This data can be context-specific and can be automatically or manually refreshed. For example, when a Supplier page loads in Coupa, a Panel App on that page can automatically get data from an external source via API that pertains to that particular Supplier and display the data in Coupa.
Create a Panel App by going to Setup > Platform > IFrames and Panels and click the Create button and then the option: Panel. The key to creating and configuring a Panel App is through it's Descriptor, a JSON format set of parameters for your App.
Here's an example Panel App on the Coupa Homepage.
You can create a Panel App by going to Setup > Platform > Installed Apps . Click the Create button and select Create New Panel App .
Requirements
Starting in R29, Panel Apps require at least TLS v1.2 to make any outbound connections to 3rd party APIs.
Panel App basics
Panel Apps are built using an app descriptor that uses JSON format. The descriptor consists of two main properties: data and blocks. Data is processed using JQ v1.6 .
Property | Description |
---|---|
slot
|
Specifies the page type where the panel will be rendered. Current options are:
Only one slot can be specified per panel app. To have the app appear on multiple pages, create the app again with the desired |
|
The data attribute describes how to fetch the desired data that is rendered in the panel. A JSON object that describes what data to fetch to render the panel. The values in this object will be URLs that Coupa will send a request to in order to retrieve data. The responses from all of these data requests will be merged into one Hash, aliased under the key name given to them in this object.
|
context
|
An API call to the NY Times article search for recent articles about a supplier would look like this:
Contact Coupa for an updated list of what contextual data is available for each slot. |
Timeout |
Added at the beginning of the JSON body allows to configure the timeout for the panel's API response. An example block would be:
|
Parameterize api calls (properties) |
An array of properties that a user/administrator has to furnish after enabling your panel app. If your app/API requires a unique API key, or login/password for each Coupa customer that enables it, here's how you provide that option. An example block would be:
You then reference your properties in the data blocks like this:
When a Coupa admin enables a new panel app, they will be prompted to fill in all “properties” fields specified in your app descriptor. |
launch
|
Adds a Launch Button that can be used when a Panel App does not need to be displayed immediately. Rather than the Panel App automatically reaching out to the 3rd party and rendering the data, it only reaches out if the end user manually clicks the Launch Button. This JSON object includes
How to use
|
allow_refresh
|
Adds a Refresh Button that can be used when data on the page changes and affects the data rendered in the Panel App. This is handy for your Cart Panel App when a Req line is updated. The user can click Refresh Button to update the data in the Panel App. This is boolean option is used to display a refresh button on the Panel App in the app descriptor: "allow_refresh": true How to use
When clicked, the button refreshes the contents on that app. This field should be added on the same level as “slot”, “data”, “launch” etc, but not in blocks. The refresh button is shown at the top of panel application. |
blocks
|
The blocks attribute describes how to render data in the panel. It is an array of JSON objects ("blocks") of various types that each describe a separate visualization. Blocks have access to the data that was previously sent and use it to create visualizations. |
error_blocks
|
The error blocks attribute works similarly to the blocks attribute but is used to create error messages when there's a problem getting or rendering the data. |
Fetching Data
We will allow apps to make up to 5 separate HTTP calls in order to fetch data for the app. These will be specified using the data attribute in the descriptor. API calls should return either JSON.
Panel App block types
Panel App renders that data on an existing Coupa page using different types of UI elements known as blocks . Coupa supports the following block types:
- Rich text (including images)
- Fields (essentially key-value pairs)
- Number
- Money
- Pie chart
- Line/Bar/Column graph
- Launch
- Refresh
- Row
- Dynamic Table
App configuration
Attribute | Description |
---|---|
|
Identifies the type of block. For example, |
|
This JSON object will have different properties depending on strategy. JQ
Processing data with JQ For example, the following JQ script could be used to extract several specific pieces of data and repackage them in another JSON object:
This would result in the following JSON object being produced:
More information: |
|
Different blocks types may have other properties specific to that block type. |
Panel App block types
Text block
Attribute | Description |
---|---|
|
Must equal |
template
|
The text block type uses a Liquid Markdown template. The template is first parsed as Liquid to interpolate the data that's returned from the requests. Then, the template is parsed again as Markdown to generate the HTML. Markdown provides a safe method of generating HTML, and for security reasons, Coupa blocks inline HTML in the template. There's no character limit other than the max limit of the data column that stores the block's configuration. Markdown allows for headers, inline emphasis formatting, lists, images, links, and blockquotes. Coupa also supports syntax highlighting and tables. For detailed info about working with Liquid and Markdown, see Liquid template language and Mastering Markdown . |
data
|
The data strategy must produce a single JSON object. All of the keys in the object will be available to the Liquid template as variables.
|
Currently a text block is the best/only way to display data in a table using the kramdown table format.
Fields block
Property | Description |
---|---|
|
Must equal |
data
|
The data strategy must produce an array of JSON objects with label and value attributes. When rendering, the label will be used as the field label and the value will be used as the field value.
|
title
|
An optional title that will appear above the list of fields. |
Bar/Line block
Property | Description |
---|---|
|
Must equal |
data
|
The data strategy must produce an array of arrays. Unlike the Table Block, the data for bar/line graphs will be column-oriented. Each array represents a single series of data that will be displayed. The first element is the name of the series, then the remaining elements are the points in the series.
|
axis
|
Allows configuration of the labels on the x and y axes. |
title
|
An optional title that will appear above the graph. |
description
|
An options description that will appear below the graph in smaller text. |
Pie block
Property | Description |
---|---|
|
Must equal pie |
data
|
The data strategy must produce a column-oriented array of arrays as described in the Bar/Line Block section.
|
title
|
An optional title that will appear above the chart. |
description
|
An options description that will appear below the chart in smaller text. |
Money block
Property | Description |
---|---|
|
Must equal money |
data
|
The data strategy must produce a JSON object with the following structure:
|
Title
|
An optional title that will appear above the number. |
Description
|
An optional description that will appear below the number in smaller text. |
Numbers block
Property | Description |
---|---|
|
Must equal numbers |
Data
|
The data strategy must produce a single JSON integer or Float.
|
Decimal
|
The number of decimals to display after the decimal point. Defaults to 0. |
Title
|
An optional title that will appear above the number. |
Description
|
An optional description that will appear below the number in smaller text.Example Panel Apps |
Custom fields
Accessing custom fields on an in-context object follows this general format:
context.<object>.custom_fields.<field_name>
Where
<field_name>
is the Field Name prompt when creating a custom field:
Since we're looking at the Supplier object and have a field, Phone number, you'd reference it like this:
context.supplier.custom_fields.phone_number
Slot | Custom field accessor |
---|---|
contracts.show | context.contract.custom_fields.<field_name> |
projects.show | context.project.custom_fields.<field_name> |
quotes/requests.show | context.quote_request.custom_fields.<field_name> |
requisition_headers.edit | context.requisition_header.custom_fields.<field_name> |
suppliers.show | context.supplier.custom_fields.<field_name> |
Custom fields aren’t applicable for slots that don’t reference a specific object, like:
user.home
expenses.index
API details
Authentication
Coupa supports the following authentication types:
Method | Example |
---|---|
Standard API key |
|
Basic Auth |
|
Request for short lived token using “before_data” request |
|
API URLs
- URLs must be HTTPS
- URLs must be accessible by Coupa servers (i.e. a HEAD request must succeed)
- URLs must successfully parse as valid Liquid templates. This is to enable interpolation
API request details
- When interpolating data into the URLs using Liquid, the result must be a valid URL
- Response code must be 200 or 204
- Remote server must respond within a timeout period of 5 seconds
- Response Content-Type must be
application/json
Buttons
You can configure a Launch Button (
launch
)and a Refresh Button (
allow_refresh
) for your panel apps. While the functionality looks and feels like a block, technically buttons are not blocks. You can find details on buttons in the Panel App basics section of this article. for more info.
Panel App context
You can build Panel Apps for the following pages:
- Homepage:
/
and/user/home
- Suppliers page:
/suppliers/:id/record
- Contracts page:
/contracts/show/:id
- Cart:
/requisition_headers/{id}/edit
- Projects home page:
projects.show
- Sourcing event Settings page:
quotes/requests.show
Each Panel App type has its own respective context payload. Below are the fields that will be provided in the Panel Apps API payload, which are used as context to match and find data.
Page | Available fields |
---|---|
All panel apps |
user_instance
(i.e. [domain name].coupahost.com) is included in the payload. |
Homepage |
id
,
email
,
fullname
,
login
,
employee_number
|
Invoice show/edit |
|
Supplier page |
custom_fields: |
Contract page |
id
,
parent_contract_id
,
name
,
number
,
version
,
supplier_id
,
start_date
,
status
|
Cart | The Cart Panel App payload will contain the same fields as the Requisition header API payload |
Projects | All Project system fields and custom fields |
Sourcing event |
|
Panel App examples
New York Times article search
This app adds the recent news about the supplier from the New York Times on the supplier's page. You'll need to get an NYT API key to make this one work.
This is the code that drives the app.
{
"properties": {
"api_key": {
"type": "string"
}
},
"slot": "suppliers.show",
"before_data": {},
"data": {
"nyt_data": {
"uri": "https://api.nytimes.com/svc/search/v2/articlesearch.json?q={context.supplier.name}&api-key={properties.api_key}"
}
},
"blocks": [
{
"type": "text",
"data": {
"type": "jq",
"jq": "{ \"docs\": .nyt_data | .response | .docs }"
},
"text": "{:height=\"100px\"}"
},
{
"type": "text",
"data": {
"type": "jq",
"jq": "{ \"docs\": .nyt_data | .response | .docs }"
},
"text": "1. [{{ docs[0].headline.main }}]({{ docs[0].web_url }}) \n2. [{{ docs[1].headline.main }}]({{ docs[1].web_url }})\n3. [{{ docs[2].headline.main }}]({{ docs[2].web_url }})\n4. [{{ docs[3].headline.main }}]({{ docs[2].web_url }})\n5. [{{ docs[2].headline.main }}]({{ docs[4].web_url }})\n"
}
]
}
Weather Panel App
This example app shows the graphical representation of temperature aspects of a location. You'll need to get an API key to make this one work.
This is the code that drives the app.
{
"properties": {},
"slot": "suppliers.show",
"data": {
"chicago_data": {
"method": "get",
"uri": "https://api.openweathermap.org/data/2.5/weather?q=Chicago&appid=155057bc8e3dde82a6482e76bddf9c10&units=imperial"
}
},
"blocks": [
{
"type": "bar",
"title": "Here's the weather in Chicago",
"data": {
"type": "jq",
"jq": "[{name: \"Temp\", data: [.chicago_data.main.temp]}, {name: \"Wind Speed\", data: [.chicago_data.wind.speed]}, {name: \"Humidity\", data: [.chicago_data.main.humidity]}]"
},
"axes": {
"xAxis": {
"labels": {
"enabled": false
}
},
"yAxis": {
"title": {
"text": ""
},
"max": 100
}
}
},
{
"type": "line",
"title": "Here's the weather in Chicago",
"data": {
"type": "jq",
"jq": "[{name: \"Minimum, Current, max temp for Chicago\", data: [[.chicago_data.main.temp_min], [.chicago_data.main.temp], [.chicago_data.main.temp_max]] }, {name: \"Fake data not from API\", data: [44, 52, 130, 87, 74, 88] }]"
},
"axes": {
"xAxis": {
"labels": {
"enabled": true
},
"categories": [
"Jan",
"Feb",
"Mar",
"Apr",
"May",
"June"
]
},
"yAxis": {
"title": {
"text": ""
},
"max": 200
}
}
}
]
}
Panel App API example
Below is the App descriptor used for an automated test. We stub out the API response to return exactly this:
[
{
"data_title":"Title",
"data_number":42,
"second_score":88
}
]
{
# Here is where you'd setup client specific fields. Like login/password. The customer would be prompted to fill in
# those values once, when activating the application
"properties": {},
# This is the page where the app gets displayed. See the "slot" property for possible values.
"slot": "suppliers.show",
# Some APIs require a bearer token, or a 2-step process, this is where you handle that.
"before_data": {},
"data": {
"coupa_data": {
# You can add context specific values to this API call, {{context.contract.supplier_name}} for example
"uri": "http://fake.test",
# This works intuitively, just specify your API headers here, Bearer token, params, etc.
"headers": {}
}
},
"blocks": [
{
"type": "number",
# This is hard-coded title for this block, cant use api data, or contextual data here
"title": "Title for number block",
# "coupa_data" was the API response above, .[0] returns the stuff in the {}, ".data_number" gets the value,
# in this case "42"
"data": {
"type": "jq",
"jq": ".coupa_data | .[0] | .data_number"
}
},
# So this block renders a specially formatted number block, with the number "42" in large bold font,
# and a text label/title of "Title for number block"
{
# Fields block has some of the strictest requirements for data, It displays all data in key -> value pairs
"type": "fields",
"data": {
"type": "jq",
"jq": ".coupa_data | .[] | to_entries | map({ label: .key, value: .value })"
}
},
{
# For this block, we are just going to pull the hash out from the [] and use the values inside it
"type": "text",
"data": {
"type": "jq",
"jq": " .coupa_data | .[0]"
},
# Text blocks gives you a lot of flexibility for displaying data from the API response in context.
# The text can be formatted just like markup, so you can create links, tables, render/resize images, etc.
"text": "{{ data_number }} is the data_number"
},
{
# Setting up the bar graph is pretty tricky, maybe work with us if you want to go this route.
# But this example creates a bar graph with values/labels bar sizes based on the values in the API response
"type": "bar",
"title": "Bar graph with fake data",
"data": {
"type": "jq",
"jq": "[{name: \"Overall Score\", data: [.coupa_data | .[0].data_number]},
{name: \"Environment score\", data: [.coupa_data | .[0].second_score]}]"
},
"axes": {"xAxis": {"labels": { "enabled": false}}, "yAxis": {"title": { "text": ""}, "max": 100} }
}
]
}
Test in the partner's instance
To test this in Coupa, you will need to be on version R25.0 or higher. In the end, your App will be a part of the App Directory and not a custom Panel App, but using these steps you can test on your own before Coupa officially adds the App to the App Directory.
- Go to Setup
- Under Platform, click 'Installed Apps'
- Click the 'Create' button, then select 'Create New Panel App'
- Create the App
- Add a name
- In the descriptor section, include the code listed above
Once complete, if no errors a Panel App will be displayed on each Supplier page.
Download an app from the Coupa App Marketplace
Visit the Coupa App Marketplace to download a Panel and follow the installation instructions provided. For more information about viewing installed apps, see Coupa's App Marketplace.