Customising skill behaviors in OpenAPI documents
You can use additional extensions to modify certain behaviors within IBM Watson Orchestrate™ after establishing the foundation for your skill in the OpenAPI specification.
By editing the available annotations or OpenAPI configurations, you can modify the behavior of the following broader themes:
- Viewing skills in the UI
- Running skills
- Working with user input
- Dynamically add field values in the dropdown
- Working with skill output
- Returning user-friendly errors
- Sending data to your API
- Sample OpenAPI specification
Viewing skills in the UI
You can use the following options to customize how your skills are displayed to your users in the skill catalog:
- Define an application to house your skills
- Provide a name and description for your skills
- Provide an icon for your app
Define an application to house your skills
When you assign skills to an application, they are grouped with that application and they use a singular connection configuration for all the skills in that application. For your skills to work, you must have a singular application defined in an OpenAPI specification and the application ID must be unique to your tenant.
You can define an application using the following annotations:
-
You can use the annotation x-ibm-application-id to uniquely identify the application to which the skills in the OpenAPI document belong to.
-
The annotation x-ibm-application-name is the name of the application that the user sees in the user interface. The value must be human-readable and easily understandable.
You can see how the preceding annotations are rendered in Watson Orchestrate in the Figure 1: Viewing skills in the UI.
For your reference, the following snippet is an example:
-
openapi: 3.0.0 info: title: some-name version: 1.0.0 x-ibm-application-id: "salesforce" x-ibm-application-name: "Salesforce" {: codeblock} -
{ "openapi": "3.0.0", "info": { "title": "some-name", "version": "1.0.0", "x-ibm-application-id": "salesforce", "x-ibm-application-name": "Salesforce" } }
Provide a name and description for your skills
For your skills to display suitably, you must include values in the standard optional OpenAPI specification fields:
- Skill tile: Use the fields
summaryanddescriptionto display the skill in the skill catalog. You can find the summary of the skill displayed next to the icon on the tile and the description is displayed on the side view of a selected skill.
You can notice how the preceding annotations are rendered in Watson Orchestrate in the Figure 1: Viewing skills in the UI.
For your reference, the following snippet is an example that demonstrates sample content for the "Repeat a message" skill:
-
paths: /repeatMessage: description: "Repeat a message" post: summary: "Repeat a message" description: "Repeats a message that has already been sent." operationId: "repeatMessage" requestBody: content: application/json: schema: $ref: "#/components/schemas/repeatMessage_input" required: true -
{ "paths": { "/repeatMessage": { "description": "Repeat a message", "post": { "summary": "Repeat a message", "description": "Repeats a message that has already been sent.", "operationId": "repeatMessage", "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/repeatMessage_input" } } }, "required": true } } } } }
- Skill form: You can provide the JSON schema standard fields
titleanddescriptioninside your response and request schemas. These values appear as field names and helper text on the skill's form.
The following example demonstrates sample content from a Box form:
-
File: type: object properties: id: title: ID description: Box's unique string identifying this file. type: string parent_id: title: Parent ID description: The ID of the parent folder. type: string name: title: File name description: The file name, including the extension. type: string ... -
{ "File": { "type": "object", "properties": { "id": { "title": "ID", "description": "Box's unique string identifying this file.", "type": "string" }, "parent_id": { "title": "Parent ID", "description": "The ID of the parent folder.", "type": "string" }, "name": { "title": "File name", "description": "The file name, including the extension.", "type": "string" } } } }
For more information about content best practices in your UI, see UI content best practices.
Provide an icon for your application
You can use the annotation x-ibm-application-icon to add an icon for your application. The annotation can be specified at the info or operation levels in your OpenAPI specification. The icon specified
at the operation level takes precedence over the info level.
The annotation takes an HTML <svg> as its value, and is rendered at 48 x 48 pixels. You can create your <svg> using any image modification program, then save it as an SVG file, ensuring that
the dimensions are of 48 x 48 pixels.
For example, you can use Inkscape to create an <svg>, either through their GUI or command line tools.
For your reference, the following snippet is an example to do it on the MacOS command line:
brew install inkscape
inkscape -l --export-filename=<your_svg>.svg <your_png>.png
Note:
-
The command opens a GUI, click and choose the Embed option.
-
The conversion maintains the original size and the UI renders a scaled-down image. To conserve space in your OpenAPI it is recommended to produce a
48 x 48 pixelversion of your icon before conversion.
After you create and save your SVG, complete the following steps to add it to your application:
- Format your
svgto avoid the usage of any classes. For example, you can use the tool svgo to format<svg>using the following command.
npx svgo <your_svg>.svg -o <your_svg>.svg
Note: Make sure to install Node.js on your system.
- Run the following command to escape the quotations.
sed 's/"/\\"/g' <your_svg>.svg > <your_svg>-escaped.svg
You can also use an equivalent to escape the double quotation marks (") in the SVG and write to a new file.
- Remove any extraneous data and delete anything before or after the
<svg>tags. - Embed the image in your OpenAPI specification. Insert the contents of the SVG file into the OpenAPI in the field
x-ibm-application-icon. For JSON, you must format the data on a single line. You can do it using the tool yq4.
yq4 -i -o json ".info.x-ibm-application-icon = \"$(cat <your_svg>-escaped)\"" <your_OpenAPI>.json
| Tag | Required |
|---|---|
| x-ibm-application-icon | No |
You can see how the preceding annotation is rendered in Watson Orchestrate in the Figure 1: Viewing skills in the UI.
The following example demonstrates the usage of the info level:
-
openapi: 3.0.0 info: title: some-skill version: 1.0.0 x-ibm-application-icon: <svg width="48" height="48"><circle cx="50" cy="50" r="40" stroke="green" stroke-width="4" fill="yellow" /></svg> -
{ "openapi": "3.0.0", "info": { "title": "some-skill", "version": "1.0.0", "x-ibm-application-icon": "<svg width=\"48\" height=\"48\"><circle cx=\"50\" cy=\"50\" r=\"40\" stroke=\"green\" stroke-width=\"4\" fill=\"yellow\" /></svg>" } }
The following example demonstrates the usage of the operation level:
-
paths: /repeatMessage: description: "repeat a message" post: description: "repeat a message" operationId: "repeatMessage" x-ibm-application-icon: <svg width="48" height="48"><circle cx="50" cy="50" r="40" stroke="green" stroke-width="4" fill="yellow" /></svg> requestBody: content: application/json: schema: $ref: "#/components/schemas/repeatMessage_input" required: true -
{ "paths": { "/repeatMessage": { "description": "repeat a message", "post": { "description": "repeat a message", "operationId": "repeatMessage", "x-ibm-application-icon": "<svg width=\"48\" height=\"48\"><circle cx=\"50\" cy=\"50\" r=\"40\" stroke=\"green\" stroke-width=\"4\" fill=\"yellow\" /></svg>", "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/repeatMessage_input" } } }, "required": true } } } } }
Figure 1: Viewing skills in the UI

Figure 1 illustrates how the use of these extensions is reflected in the Watson Orchestrate user interface.
| Item | Description |
|---|---|
| 1 | Define an application to house your skills |
| 2 | Provide a name and description for your skills |
| 3 | Provide an icon for your app. |
Running skills
You can use the following options to determine how your skills are used by your users:
Train the natural language model
You can use the natural language model to determine whether the user's utterance in the UI chat box matches a specific skill. Using the annotation x-ibm-nl-intent-examples you can supply example utterances to train the NL model.
If the annotation is not supplied, utterances are generated from the description and summary that are supplied for the skill. However, these utterances are basic and tend to produce a narrow model.
As shown in the following example, you can specify the extension at the operation level to enable the explicit use of natural language sentences. Ideally, you should provide around 20 to 30 utterances for your skill. You can supply less, but providing fewer than 10 limits the usability of your skill:
-
paths: /repeatMessage: description: "repeat a message" post: description: "repeat a message" operationId: "repeatMessage" x-ibm-nl-intent-examples: - "repeat a message" - "can you echo this message?" - "repeat what I say" - "can you repeat what I tell you?" - "repeat this message for me" - "repeat after me" - "say it back to me" - "play that again" - "repeat this message again" - "repeat this message" requestBody: content: application/json: schema: $ref: "#/components/schemas/repeatMessage_input" required: true -
{ "paths": { "/repeatMessage": { "description": "repeat a message", "post": { "description": "repeat a message", "operationId": "repeatMessage", "x-ibm-nl-intent-examples": [ "repeat a message", "can you echo this message?", "repeat what I say", "can you repeat what I tell you?", "repeat this message for me", "repeat after me", "say it back to me", "play that again", "repeat this message again", "repeat this message" ], "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/repeatMessage_input" } } }, "required": true } } } }}
utterance
The utterance is the text that is used to start the skill.
For example,
-
paths: /repeatMessage: description: "repeat a message" post: description: "repeat a message" operationId: "repeatMessage" -
{ "paths": { "/repeatMessage": { "description": "repeat a message", "post": { "description": "repeat a message", "operationId": "repeatMessage", } } } }
Skill input
You can use the following options to interact with how your user is adding content with their skill set:
Understand the input form
The form that is presented to the user when a skill is started is determined by the request body schema defined in the OpenAPI specification for that skill. The process of providing the necessary values is known as slot filling. You can use the following extension schema syntax to control how the form is presented to your user.
All of these fields are supplied inside the OpenAPI schema:
-
requestFeedback_input: type: object required: - addressee properties: addressee: type: string title: Addressee description: email address of the receiver/assignee x-ibm-show: true subject: type: string description: Subject to be reviewed -
{ "requestFeedback_input": { "type": "object", "required": [ "addressee" ], "properties": { "addressee": { "type": "string", "title": "Addressee", "description": "email address of the receiver/assignee", "x-ibm-show": "true" }, "subject": { "type": "string", "description": "Subject to be reviewed", } } } }
required
You can use the field required as standard syntax in an OpenAPI schema to indicate that the particular field must be supplied to start the API. Using required, you can ensure that the skill set gets the necessary
information. You can add the field required on a wrapping object or on a field.
A wrapping object for example:
-
type: object required: - addressee properties: addressee: type: string -
{ "type": "object", "required": [ "addressee" ], "properties": { "addressee": { "type": "string" } } }
On a field:
-
type: object properties: addressee: type: string required: true -
{ "type": "object", "properties": { "addressee": { "type": "string", "required": true } } }
description
The field description is standard syntax in an OpenAPI schema that is used for describing the use and purpose of a field. It is displayed to the user in the form:
-
type: object properties: addressee: type: string description: Provide the addressee for this email -
{ "type": "object", "properties": { "addressee": { "type": "string", "description": "Provide the addressee for this email" } } }
x-ibm-label
You can use the IBM extension x-ibm-label to supply a human-readable name for input parameters and request body.
For example,
-
type: object properties: addressee: type: string x-ibm-label : Addressee -
{ "type": "object", "properties": { "addressee": { "type": "string", "x-ibm-label": "Addressee" } } }
x-ibm-show
You can use the IBM extension x-ibm-show to show or hide an optional input in the generated form. By default, all of the fields are shown:
-
type: object properties: addressee: type: string x-ibm-show : true -
{ "type": "object", "properties": { "addressee": { "type": "string", "x-ibm-show": "true" } } }
x-ibm-disable
You can use the extension x-ibm-disable to disable a field in the form and make it uneditable. By default, all of the fields are enabled:
-
<pre><code> type: object properties: addressee: type: string x-ibm-disable : "true" -
{ "type": "object", "properties": { "addressee": { "type": "string", "x-ibm-disable": "true" } } }
x-ibm-list
The extension x-ibm-list controls how enumerations are displayed to the user. The following values are supported:
drop-down: Used for single selection.multiselect: Used for selecting multiple values.
For example,
-
type: object properties: status: type: string enum: - Success - Failure x-ibm-list : "dropdown" -
{ "type": "object", "properties": { "status": { "type": "string", "enum": [ "Success", "Failure" ], "x-ibm-list": "dropdown" } } }
x-ibm-prompt
The extension x-ibm-prompt specifies a human-readable question that can be used by a Watson to request input from a user during a conversation:
-
type: object properties: city: type: string x-ibm-prompt : "What is the city name?" -
{ "type": "object", "properties": { "city": { "type": "string", "x-ibm-prompt": "What is the city name?" } } }
x-ibm-important
You can use the extension x-ibm-important to promote optional fields from the initially hidden section of the form to the main section. Any field that is not required or important is initially hidden from the user in the form
that the skill set generates. By default, the value is false.
The syntax corresponds to that of the required attribute. For a wrapper object, use the following syntax:
On a wrapping object:
-
type: object x-ibm-important: - addressee properties: addressee: type: string -
{ "type": "object", "x-ibm-important": [ "addressee" ], "properties": { "addressee": { "type": "string" } } }
On a field:
-
type: object properties: addressee: type: string x-ibm-important: true -
{ "type": "object", "properties": { "addressee": { "type": "string", "x-ibm-important": true } } }
x-ibm-multiline
The extension x-ibm-multiline switches the text box used in the form for the input field from a single line to a multiline box:
-
type: object properties: addressee: type: string x-ibm-multiline: "true" -
{ "type": "object", "properties": { "addressee": { "type": "string", "x-ibm-multiline": "true" } } }
Figure 4: Understand the input form
Figure 4 illustrates how the use of extensions title, description, x-ibm-multiline and so on are reflected in the input form that is presented to your user.
Upload files
Many skills might potentially require users to upload files as part of the skill, for example, uploading a job description text file. To do so, the input form must display a suitable file upload widget to the user. The uploaded content can then be passed to the API.
The skill set provides a file upload widget to the user that takes in a single file or multiple files depending upon the schema in the OpenAPI specification and the annotation x-ibm-content.
Upload a single file
To create a single file upload, you must define a property with type: "string" and format: binary and use the x-ibm-content with an empty value:
-
content: application/json: schema: type: "object" properties: myFile: type: "string" format: "binary" x-ibm-content: value: "" -
{ "content": { "application/json": { "schema": { "type": "object", "properties": { "myFile": { "type": "string", "format": "binary", "x-ibm-content": { "value": "" } } } } } } }
This annotation presents a file upload widget to the user that accepts a single file and sends a JSON payload to the API with the content of the uploaded file as a base64 encoded string.
You can use multipart/form-data instead of application/json and the data is sent to the API as a multipart/form-data with the field name as the key, however, currently you might notice issues with the
content type if you send more than just the file content.
Figure 5: Upload a file
Figure 5 shows an example on uploading a file.
Upload a fixed number of files
Sending a fixed number of files is also possible, using the same method as the previous example. It creates a form with a file upload widget for each file upload defined in the OpenAPI specification:
-
content: application/json: schema: type: "object" properties: myFile1: type: "string" format: "binary" x-ibm-content: value: "" myFile2: type: "string" format: "binary" x-ibm-content: value: "" -
{ "content": { "application/json": { "schema": { "type": "object", "properties": { "myFile1": { "type": "string", "format": "binary", "x-ibm-content": { "value": "" } }, "myFile2": { "type": "string", "format": "binary", "x-ibm-content": { "value": "" } } } } } } }
The preceding example displays two file upload widgets to the user, one for myFile1 and the other for myFile2.
You can use multipart/form-data instead of application/json, however, currently you might have issue with the content type if you send more than just the file content.
Upload multiple files
You can create a multifile upload widget that lets users upload any number of files to the API. The following API specification defines the file upload inside an array object. You must also add the field fileName:
-
content: application/json: schema: type: object required: - attachments properties: redundant: type: boolean attachments: type: array items: type: object properties: fileName: type: string title: "File name" content: type: string format: binary x-ibm-content: value: "" -
{ "content": { "application/json": { "schema": { "type": "object", "required": [ "attachments" ], "properties": { "redundant": { "type": "boolean" }, "attachments": { "type": "array", "items": { "type": "object", "properties": { "fileName": { "type": "string", "title": "File name" }, "content": { "type": "string", "format": "binary", "x-ibm-content": { "value": "" } } } } } } } } } }
The field redundant is supplied as without it the skill set immediately supplies an empty array, which is a valid value for an array. The field redundant is not marked as required, hence it doesn't
show up in the form without expanding it but might be hidden further by using x-ibm-show.
Note: Currently, you can't upload multiple files with a multipart or form. This is due to the need to supply the extra field, which causes the multipart or JSON conflict noted in the preceding sections.
Dynamically add field values in the drop-down
The OpenAPI specification supports adding a drop-down list into your skill with field values that are dynamically generated from another skill that you have in your skill set. The values can be fetched from an API that stores the data. This is advantageous if the data set you want to include in the OpenAPI specification is large or if it varies over time.
The following image illustrates a custom skill that has a drop-down list as an input. To dynamically populate the drop-down, the Retrieve folders skill from the Dropbox app is used.

x-ibm-ui-extension
The x-ibm-ui-extension extension defines an input as a drop-down list, it includes the skill that populates this list and the labels and values to gather if the skill has complex objects (nested objects):
"x-ibm-ui-extension":
{
"component": "dropdown",
"actions": [
{
"skill_id": "<skill_id>",
"type": "data",
"mappings":
{
"labels": "<link_to_object_property_name>",
"values": "<link_to_object_property_name>"
},
"params": {}
}
]
}
In the extension schema, you have the following keywords and sections:
-
component
It must bedropdown. -
actions
This section defines the skill id, the type asdata, and the mappings of the skill that you want to pull data to a drop-down list.
x-ibm-ui-extension extension works only if the skill defined in skill_id doesn't require inputs.skill_id
The skill_id is the ID of the skill. Get the title, version, and operationId properties to build the skill_id from the OpenAPI documents for custom skills, or refer to the
Skill IDs for built-in apps section for a list of skill-ids for built-in apps.
The IDs are constructed in the following format:
<info.title>__<info.version>__<paths[].path.operation.operationId>
Example:
Translator-API__1.0__translator.skill
Any spaces in each section are replaced with hyphens, and the sections are separated by two underscores. For example, the skill_id for "Translator API" would be Translator-API__1.0__translator.skill.
mappings
The mappings section specifies the values and labels properties, which define how the data that is gathered from a specific skill can populate a drop-down list:
-
valuesRequired property. The data sent to the API server when users select an option in the drop-down list. If you add only this property, the data also appears on the user interface (UI) to users as the drop-down labels.
-
labelsOptional property. The data that is shown to users when they open the drop-down list in the user interface (UI).
To populate the drop-down list, you must create a link in the values and labels properties to an object in the request or response body. For reference, see Linking to operations in the following topic.
Linking to operations
In both the values and labels properties, you must create a link to an object property in the request or response body. The link defines which body contents must populate the drop-down list.
The response from the API that you link can be a simple object or complex objects with arrays of many objects or arrays of simple types. For complex objects, you need to specify the mappings section, otherwise this section isn't
needed.
Create the link based on the hierarchy of object properties under the properties keyword. Link's structures might differ based on how an object is built:
# Link to a simple object
<object_property_name>
# Link to a simple object with multiple properties
<object_property_name>.properties.<object_property_name>
# Link to a complex object that nest an array
<object_property_name>.items
# Link to a complex object that nest an array of objects
<object_property_name>.items.properties.<object_property_name>
For the following example, consider the Translator and Languages APIs for a simple operation to translate some text based on a specific language.
The Translator API has the translator endpoint that defines an input as a
drop-down list for users to select which language they want to translate their texts. This endpoint links to the Languages API to dynamically generate a list of languages available to translate texts.
So, to define this input as a drop-down list, this endpoint uses the x-ibm-ui-extension extension and adds the mappings section to define which labels and values must populate the list:
"/app/translator":{
"post": {
"summary": "Translate a text",
"operationId": "translator.skill",
"requestBody": {
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"type": "string",
"x-ibm-ui-extension": {
"component": "dropdown",
"actions": [
{
"skill_id": "Languages__1.0.0__language.skill",
"type": "data",
"mappings": {
"labels": "language_options.items.properties.name",
"values": "language_options.items.properties.code"
}
The Languages API has the languages endpoint that returns an array of complex objects for language options as a response:
"/app/languages": {
"get": {
"summary": "Get a list of languages.",
"operationId": "language.skill",
"responses": {
"200": {
"description": "Success to get the list of languages.",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"language_options": {
"type": "array",
"items": {
"type": "object",
"properties": {
"name": {
"type": "string"
},
"code": {
"type": "string"
}
The following image illustrates the mappings section in the Translator API by showing how the link is created based on the response of the languages endpoint in the Languages API:

Example of a working skill with dynamic drop-down list
For your reference, the following files are a working sample of the OpenAPI. The first OpenAPI defines the Manage Job Requisition app, and the second one defines the Getter app.
The following code is a working sample of the Manage Job Requisition app in YAML and JSON. Use this code to create an OpenAPI file and add this app to Watson Orchestrate:
-
openapi: 3.0.1 info: description: Manage all job requisitions title: Manage Job Requisition version: 1.0.14 x-ibm-application-id: manage-job-requisitions-seq x-ibm-application-name: manage-job-requisitions-seq x-ibm-skill-type: imported x-ibm-application-icon: <svg id="Layer_2" data-name="Layer 2" xmlns="http://www.w3.org/2000/svg\" viewBox="0 0 512 512"><defs><style>.cls-1{fill:#fab922;}</style></defs><path class="cls-1" d="M483,172.22c1.83,50.75-18.23,90.41-45.2,127C392.65,360.47,335,410.1,282.27,464.39c-4.5,4.64-9.19,9.09-13.75,13.67-13.43,13.5-13.64,13.09-26.73-.27C207.25,442.55,172,408.06,137.66,372.6c-36.26-37.5-71.22-75.85-94-124C14.56,187.18,27.35,109.72,76,65c43.11-39.64,113.8-47.54,166.5-17.66,10.23,5.79,17.22,5.74,27.52-.34,61.56-36.34,159.28-18.13,196.9,59.3C477.54,128.14,484,151,483,172.22Zm-403.76,7.7c1.38,25.13,10.54,49.49,25.08,71.92C139.81,306.58,188,350,233.75,395.41c7.07,7,12.77,18.89,22.54,19.14,10.11.26,15.13-12.33,22.12-19.34,38.76-38.86,79.38-76.09,112.6-120.06,27.85-36.84,49.26-75.65,37.21-125.07-9.71-39.83-44.34-69.2-85.37-69.88-31.29-.52-56.33,12.18-75.92,35.92-7.63,9.25-13.37,10.45-21.65.33C233.48,102.06,219,90.36,200.75,85,136.28,66.17,79.32,109.93,79.26,179.92Z"/></svg> servers: - url: https://my_server.com paths: /getAllJobRequisitions: get: summary: All open job requisitions description: Get all Job Requisitions operationId: getJobRequisitions parameters: - required: false in: query name: location schema: type: string - required: false in: query name: jobProfile schema: type: string - name: status in: query description: Status of the JR required: true explode: true x-ibm-ui-extension: component: dropdown actions: - skill_id: getter__1.0.0__status type: data params: {} schema: type: string enum: - open - closed default: open responses: '200': description: Returns all job requisitions. content: application/json: schema: $ref: '#/components/schemas/JobRequistions' components: schemas: JobRequistions: type: object properties: instances: $ref: '#/components/schemas/job_requisitions' job_requisitions: type: array items: $ref: '#/components/schemas/Job_Requisition' Job_Requisition: type: object properties: jobReqId: type: string x-ibm-disable: 'true' jobStartDate: x-ibm-show: false type: string department: x-ibm-show: false type: string division: x-ibm-show: false type: string location: x-ibm-show: false title: Location type: string costOfHire: x-ibm-show: false type: string country: x-ibm-show: false type: string createdDateTime: x-ibm-show: false type: string currency: x-ibm-show: false type: string salRateType: x-ibm-show: false type: string salaryBase: x-ibm-show: false type: string candidateProgress: x-ibm-show: false type: string city: x-ibm-show: false type: string state: x-ibm-show: false type: string closedDateTime: x-ibm-show: false type: string jobDescription: title: Job Description x-ibm-multiline: true type: string hiringManager: title: Hiring Manager type: string jobProfile: title: Job Profile type: string workExperience: x-ibm-show: false type: string recruiter: title: Recruiter type: string competency_type: x-ibm-show: false type: string competency_description: type: string competency_id: x-ibm-show: false type: string status: x-ibm-show: false type: string securitySchemes: provider_basic_auth: type: http scheme: basic -
{ "openapi": "3.0.1", "info": { "description": "Manage all job requisitions", "title": "Manage Job Requisition", "version": "1.0.14", "x-ibm-application-id": "manage-job-requisitions-seq", "x-ibm-application-name": "manage-job-requisitions-seq", "x-ibm-skill-type": "imported", "x-ibm-application-icon": "<svg id=\"Layer_2\" data-name=\"Layer 2\" xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 512 512\"><defs><style>.cls-1{fill:#fab922;}</style></defs><path class=\"cls-1\" d=\"M483,172.22c1.83,50.75-18.23,90.41-45.2,127C392.65,360.47,335,410.1,282.27,464.39c-4.5,4.64-9.19,9.09-13.75,13.67-13.43,13.5-13.64,13.09-26.73-.27C207.25,442.55,172,408.06,137.66,372.6c-36.26-37.5-71.22-75.85-94-124C14.56,187.18,27.35,109.72,76,65c43.11-39.64,113.8-47.54,166.5-17.66,10.23,5.79,17.22,5.74,27.52-.34,61.56-36.34,159.28-18.13,196.9,59.3C477.54,128.14,484,151,483,172.22Zm-403.76,7.7c1.38,25.13,10.54,49.49,25.08,71.92C139.81,306.58,188,350,233.75,395.41c7.07,7,12.77,18.89,22.54,19.14,10.11.26,15.13-12.33,22.12-19.34,38.76-38.86,79.38-76.09,112.6-120.06,27.85-36.84,49.26-75.65,37.21-125.07-9.71-39.83-44.34-69.2-85.37-69.88-31.29-.52-56.33,12.18-75.92,35.92-7.63,9.25-13.37,10.45-21.65.33C233.48,102.06,219,90.36,200.75,85,136.28,66.17,79.32,109.93,79.26,179.92Z\"/></svg>" }, "servers": [ { "url": "https://my_server.com" } ], "paths": { "/getAllJobRequisitions": { "get": { "summary": "All open job requisitions", "description": "Get all Job Requisitions", "operationId": "getJobRequisitions", "parameters": [ { "required": false, "in": "query", "name": "location", "schema": { "type": "string" } }, { "required": false, "in": "query", "name": "jobProfile", "schema": { "type": "string" } }, { "name": "status", "in": "query", "description": "Status of the JR", "required": true, "explode": true, "x-ibm-ui-extension": { "component": "dropdown", "actions": [ { "skill_id": "getter__1.0.0__status", "type": "data", "params": {} } ] }, "schema": { "type": "string", "enum": [ "open", "closed" ], "default": "open" } } ], "responses": { "200": { "description": "Returns all job requisitions.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/JobRequistions" } } } } } } } }, "components": { "schemas": { "JobRequistions": { "type": "object", "properties": { "instances": { "$ref": "#/components/schemas/job_requisitions" } } }, "job_requisitions": { "type": "array", "items": { "$ref": "#/components/schemas/Job_Requisition" } }, "Job_Requisition": { "type": "object", "properties": { "jobReqId": { "type": "string", "x-ibm-disable": "true" }, "jobStartDate": { "x-ibm-show": false, "type": "string" }, "department": { "x-ibm-show": false, "type": "string" }, "division": { "x-ibm-show": false, "type": "string" }, "location": { "x-ibm-show": false, "title": "Location", "type": "string" }, "costOfHire": { "x-ibm-show": false, "type": "string" }, "country": { "x-ibm-show": false, "type": "string" }, "createdDateTime": { "x-ibm-show": false, "type": "string" }, "currency": { "x-ibm-show": false, "type": "string" }, "salRateType": { "x-ibm-show": false, "type": "string" }, "salaryBase": { "x-ibm-show": false, "type": "string" }, "candidateProgress": { "x-ibm-show": false, "type": "string" }, "city": { "x-ibm-show": false, "type": "string" }, "state": { "x-ibm-show": false, "type": "string" }, "closedDateTime": { "x-ibm-show": false, "type": "string" }, "jobDescription": { "title": "Job Description", "x-ibm-multiline": true, "type": "string" }, "hiringManager": { "title": "Hiring Manager", "type": "string" }, "jobProfile": { "title": "Job Profile", "type": "string" }, "workExperience": { "x-ibm-show": false, "type": "string" }, "recruiter": { "title": "Recruiter", "type": "string" }, "competency_type": { "x-ibm-show": false, "type": "string" }, "competency_description": { "type": "string" }, "competency_id": { "x-ibm-show": false, "type": "string" }, "status": { "x-ibm-show": false, "type": "string" } } } }, "securitySchemes": { "provider_basic_auth": { "type": "http", "scheme": "basic" } } } }
- The Manage Job Requisition app is unauthenticated. You can use any credentials and it works.
- The skill in the Manage Job Requisition app doesn't display a dynamically generated list in the following cases:
- If you do not add the Getter app and its skill to the skill set.
- If it is not connected within the skill set.
- If it does not return an array. In this case, the skill reverts to anything specified in the OpenAPI spec. In the example, you can see that it displays an enum with options, "Open" or "Closed".
- The field
enumunderschemais optional. The values underenumare displayed when the server from where you fetch the data is down.
The following code is a working sample of the Getter app in YAML and JSON. Use this code to create an OpenAPI file and add this app to Watson Orchestrate:
-
openapi: 3.0.0 info: x-ibm-application-name: getter x-ibm-application-id: getter x-ibm-skill-type: imported title: Getter description: Sample skill for a drop-down list. version: 1.0.0 servers: - url: https://my_server.com paths: /getDropDownData: get: summary: Retrieve issue strings operationId: status responses: '200': description: A list of issue strings content: application/json: schema: type: array items: type: string example: - string1 - string2 - string3 components: securitySchemes: provider_basic_auth: type: http scheme: basic -
{ "openapi": "3.0.0", "info": { "x-ibm-application-name": "getter", "x-ibm-application-id": "getter", "x-ibm-skill-type": "imported", "title": "Getter", "description": "Sample skill for a drop-down list.", "version": "1.0.0" }, "servers": [ { "url": "https://my_server.com" } ], "paths": { "/getDropDownData": { "get": { "summary": "Retrieve issue strings", "operationId": "status", "responses": { "200": { "description": "A list of issue strings", "content": { "application/json": { "schema": { "type": "array", "items": { "type": "string" } }, "example": [ "string1", "string2", "string3" ] } } } } } } }, "components": { "securitySchemes": { "provider_basic_auth": { "type": "http", "scheme": "basic" } } } }
- The Getter app is unauthenticated. You can use any credentials and it works.
- The current implementation expects the Getter app to return only an array of strings as a response.
Now, you can create and add your own skills into the skill catalog and include them in the skill set to use this functionality.
Skill output
Use the following options to manage the content of your skills output:
Understand the output form
The form that is presented to your user when a skill is completed is determined by the response schema defined by the OpenAPI specification for that skill. You can use the following extensions schema syntax to control how the output form is presented to your user.
All of these fields are supplied inside the OpenAPI schema:
-
requestFeedback_output: type: object required: - addressee properties: addressee: type: string title: Addressee description: email address of the receiver/assignee x-ibm-show: true subject: type: string description: Subject to be reviewed -
{ "requestFeedback_output": { "type": "object", "required": [ "addressee" ], "properties": { "addressee": { "type": "string", "title": "Addressee", "description": "email address of the receiver/assignee", "x-ibm-show": "true" }, "subject": { "type": "string", "description": "Subject to be reviewed", } } } }
title
The field title is standard syntax in an OpenAPI schema for supplying a human-readable name for a parameter. This title is displayed in the form in place of the field name:
-
type: object properties: addressee: type: string title: Addressee -
{ "type": "object", "properties": { "addressee": { "type": "string", "title": "Addressee" } } }
description
The field description is standard syntax in an OpenAPI schema for describing the use and purpose of a field. It is displayed to your user in the form:
-
type: object properties: addressee: type: string description: Provide the addressee for this email -
{ "type": "object", "properties": { "addressee": { "type": "string", "description": "Provide the addressee for this email" } } }
x-ibm-show
The extension x-ibm-show shows or hides output in the generated form. By default, all of the fields are shown:
-
type: object properties: addressee: type: string x-ibm-show : true -
{ "type": "object", "properties": { "addressee": { "type": "string", "x-ibm-show": "true" } } }
x-ibm-multiline
The extension x-ibm-multiline switches the text box used in the form for the input field from a single-line box to a multiline box.
-
type: object properties: addressee: type: string x-ibm-multiline: "true" -
{ "type": "object", "properties": { "addressee": { "type": "string", "x-ibm-multiline": "true" } } }
Figure 6: Understand the output form
Figure 6 illustrates how the use of extensions title, description, x-ibm-multiline and so on are reflected in the output form that is presented to your user.
Generate output tables
When the API returns a single array of objects, the output is presented as a table. Each item in the array is an entry in the table and the table headers are the fields inside the objects in that array.
Returning user-friendly errors
The skill set can call out an external service and that service can return an error. The details of the error must be returned to your user to help them understand what went wrong.
Writing effective error messages
Everyone can benefit from writing effective error messages that are clear and concise. The error messages you write must also clearly convey the essence of the error to the user without over-complicating the reason. A good error message communicates the root cause and also something that could lead your users to a resolution.
Following are some best practices while you write error messages:
- The message must convey the reason why the task did not complete. For example, "This pet doesn't exist".
- The message must be simple enough for your user to understand. For example, writing "The key doesn't exist in our database" requires an understanding of the software implementation that "This pet doesn't exist" does not.
- The error messages must not reveal details about the backend implementation of your system. For example, rather than returning a stack trace to your user, return as much relevant information as you can and the transaction ID so that it can be shared with technical support.
- It can be beneficial to use alternative errors when trying to protect your APIs. For example, a
404error in place of a403error indicates that the API does not exist instead of revealing that your user doesn't have permission to access it. This might discourage attacks.
Returning a dynamic error message
To output a dynamic error message, return suitably structured JSON containing the details of the error:
-
paths: /mypath: get: responses: Error400: content: application/json: schema: $ref: "#/components/schemas/Errors" Error401: content: application/json: schema: $ref: "#/components/schemas/Errors" Error4XX: content: application/json: schema: $ref: "#/components/schemas/Errors" Error5XX: content: application/json: schema: $ref: "#/components/schemas/Errors" components: schemas: Errors: type: "object" description: "The Error object contains errors array that lists all of the errors that have occurred" required: - errors properties: errors: type: "array" items: $ref: "#/components/schemas/ErrorItem" ErrorItem: description: "Each error must have a code and a message" type: "object" required: - code - message additionalProperties: true, properties: code: description: "Error Code" type: "string" message: description: "Human readable message for this error" type: "string" -
{ "paths": { "/mypath": { "get": { "responses": { "Error400": { "content": null, "application/json": { "schema": null, "$ref": "#/components/schemas/Errors" } }, "Error401": { "content": null, "application/json": { "schema": null, "$ref": "#/components/schemas/Errors" } }, "Error4XX": { "content": null, "application/json": { "schema": null, "$ref": "#/components/schemas/Errors" } }, "Error5XX": { "content": null, "application/json": { "schema": null, "$ref": "#/components/schemas/Errors" } } } } } }, "components": { "schemas": { "Errors": { "type": "object", "description": "The Error object contains errors array that lists all of the errors that have occurred", "required": [ "errors" ], "properties": { "errors": { "type": "array", "items": { "$ref": "#/components/schemas/ErrorItem" } } } }, "ErrorItem": { "description": "Each error must have a code and a message", "type": "object", "required": [ "code", "message" ], "additionalProperties": "true,", "properties": { "code": { "description": "Error Code", "type": "string" }, "message": { "description": "Human readable message for this error", "type": "string" } } } } } }
In current scenario, as long as the object returned by the server matches the previously defined schema, the skill set can get the contents of the error objects and display the information to your user. You only need to include values for
code and message in your ErrorItem field. You can also add more fields to help diagnosing the APIs outside of Watson Orchestrate.
Skill runtime
Sending additional headers to the API
Sometimes it is necessary to send additional data from Watson Orchestrate to the API through headers. For example, if the user's name and email are to be used by the API to do some additional authorization on the API's server.
The headers can be controlled at the info level, by providing the header key that can be used to pass the information.
-
openapi: 3.0.0 info: title: some-skill version: 1.0.0 x-ibm-skill-headers: caller-id: "X-CUSTOM-HEADER-NAME-1" caller-name: "X-CUSTOM-HEADER-NAME-2" -
{ "openapi": "3.0.0", "info": { "title": "some-skill", "version": "1.0.0", "x-ibm-skill-headers": { "caller-id": "X-CUSTOM-HEADER-NAME-1", "caller-name": "X-CUSTOM-HEADER-NAME-2" } } }
-
caller-id: Refers to the user's email address. -
caller-name: Refers to the user's name (First name and last name).
It is sent with the API request in the X-CUSTOM-HEADER-NAME-1 and X-CUSTOM-HEADER-NAME-2 headers.
Sample OpenAPI specification
You can download the following sample OpenAPI specification file to know how the extensions are structured and defined in an OpenAPI: