logo

Are you need IT Support Engineer? Free Consultant

Building Custom Joule Capabilities: Message Types …

  • By sujay
  • 29/05/2026
  • 9 Views

Introduction

This post is part of the Joule Capability Development Blog Series. The first post covers setting up your development environment and building your first capability. This post builds on that foundation and dives into message types.

When building SAP Joule capabilities, the message: block in your function YAML controls how Joule renders the response. Choose the wrong type and a clean OData result becomes a wall of raw text. Choose the right one and the same data becomes a structured card, a list, or a rich UI5 Integration Card that feels like a native Fiori element.

Joule can also generate responses automatically using its LLM based on the data your function returns; see Gen AI Response Generation in the Joule Development Guide. The message: block gives you explicit control over the layout when the generated output is not structured enough for your use case.

The official Joule Development Guide documents the available message types, but it stops short of showing what each one looks like in practice or when to prefer one over another. This post fills that gap using the public Northwind OData service introduced in Building Custom Joule Capabilities: Getting Started. The examples here give a practical overview of each type and the decisions that go into choosing one over another.

Each message type has more fields and options than what fits in a single example, so treat this as a starting point rather than a complete reference. For the full SAP Help Documentation, see the Messages page of the Joule Development Guide. You can try the message types yourself using the digital assistants with their capabilities available in the respective folder of the GitHub Repo; see the Try It Yourself  section below.

One of the sample capabilities, Buttons, acts as a navigable launcher across all the others. Ask Joule to show different message types in action and you get one clickable postback button per message type. Clicking a button sends the right prompt back to Joule and triggers that capability without you having to type anything. You can read the sections in order or jump around in chat using the launcher.

Prerequisites

Complete Building Custom Joule Capabilities: Getting Started before starting here. You need the Joule CLI installed and a BTP destination named Northwind.

Message Types at a Glance

Type What Joule renders Best for
text Plain text, supports Markdown Free-form answers, errors, simple facts
card Title, subtitle, image, status, description Single-record preview with optional drill-down
buttons Summary text with clickable action buttons Guided next-step selection
quickReplies Inline suggestion chips below a message Short yes/no or option selection
carousel Horizontally scrollable set of cards Comparing a small set of records
list Vertically scrollable list of items Search results, multi-record display
media Image, video, or audio embed Embedding a product demo, keynote, or how-to video
client_data Silent payload for client-side logic Triggering navigation without visible output
ui5integrationCard Full UI5 Integration Card via manifest JSON Rich Fiori-style grouped field display
illustrated_message SAP illustration with title and description Success/error/empty state feedback
likert_scale Clickable scale with labeled options (1–7) In-chat surveys and satisfaction ratings

This post covers text, card, buttons, list, carousel, media, ui5integrationCard, illustrated_message, and likert_scale. quickReplies is included in the table for completeness but not demonstrated separately, as it behaves like a lighter-weight buttons message.

Plain Text Messages

The text type is the simplest message type available. It renders the content string directly in the chat window and optionally interprets Markdown. Use it when you need a free-form answer, an error message, or a brief fact where visual structure would add no value.

# northwind_product_text/functions/get_product_text.yaml (relevant section)
- type: message
  message:
    type: text
    markdown: true
    content: |
      - **Product:**  product.ProductName ?>
      - **Price:** $ product.UnitPrice ?>
      - **In Stock:**  product.UnitsInStock ?> units

Set markdown: true at the same level as content to render bold, italics, and lists. Add delay: to introduce a pause before the message appears, useful when chaining multiple messages. For error states, a text message with a clear explanation is more useful to the user than an empty card.

Message Type: Text

Example use cases: single values, error messages, displaying markdown, or any response where structured layout would distract rather than help.

Card Messages

The card type renders a structured tile with a title, subtitle, icon or image, status badge, and a Markdown-enabled description block. It is the visual equivalent of a single-record detail page condensed into a chat message.

# northwind_product_card/functions/get_product_card.yaml (relevant section)
- type: message
  message:
    type: card
    content:
      title:  product.ProductName ?>
      subtitle: "Unit Price: $ product.UnitPrice ?>"
      imageUrl: "sap-icon://product"
      imageStyle: avatar
      description:
        value: " product.UnitsInStock ?> in stock ·  product.QuantityPerUnit ?>"
        markdown: false
      enableDetailView: false
      sections:
        - title: Stock Details
          attributes:
            - label: Units on Order
              value:  product.UnitsOnOrder ?>
            - label: Reorder Level
              value:  product.ReorderLevel ?>
            - label: Discontinued
              value: " product.Discontinued == true ? 'Yes' : 'No' ?>"

The sample above shows the core fields. Beyond those, the card type supports additional options: statusState accepts success, warning, error, and information, mapping to the standard SAP semantic colors (green, orange, red, blue). Set enableDetailView: true to make the card expandable. Add a buttons array under content to append action buttons directly below the card body, following the same structure described in the Buttons section.

Message Type: CardMessage Type: Card

When to use: any single-record result where the user benefits from seeing title, status, and a few key fields at a glance without leaving the chat.

List Messages

The list type renders a vertically scrollable container of items. Each item supports a title, subtitle, description, icon, and its own buttons. Use it for search results or any response that returns more than one record.

The list function accepts an optional category_id parameter. When provided it adds an OData $filter to the request, scoping results to a single category. When omitted it returns the first five products from the full catalog. This makes the same function reusable both as a standalone capability and as the target of a postback from the carousel described in the next section.

# northwind_product_list/functions/get_product_list.yaml
parameters:
  - name: category_id
    optional: true

action_groups:
  - condition: category_id != null
    actions:
      - type: api-request
        method: GET
        system_alias: NorthwindService
        path: "/Products?$filter=CategoryID%20eq%20 category_id ?>&$select=ProductName,UnitPrice,UnitsInStock,QuantityPerUnit,UnitsOnOrder,ReorderLevel,Discontinued&$top=5"
        result_variable: api_result
  - condition: category_id == null
    actions:
      - type: api-request
        method: GET
        system_alias: NorthwindService
        path: "/Products?$select=ProductName,UnitPrice,UnitsInStock,QuantityPerUnit,UnitsOnOrder,ReorderLevel,Discontinued&$top=5"
        result_variable: api_result

The message block uses Handlebars to iterate the result array:

  - condition: api_result.status_code == 200 && api_result.body.value != null && api_result.body.value.size() > 0
    actions:
      - type: set-variables
        variables:
          - name: products
            value:  api_result.body.value ?>
      - type: message
        scripting_type: handlebars
        message: >
          {
            "type": "list",
            "content": {
              "title": "Northwind Product Catalog",
              "subtitle": "Showing first 5 products",
              "enableDetailView": true,
              "elements": [
                {{#eachJoin products}}
                {
                  "title": "{{ProductName}}",
                  "subtitle": "${{UnitPrice}}",
                  "description": "{{UnitsInStock}} units in stock",
                  "imageUrl": "sap-icon://product",
                  "imageStyle": "avatar",
                  "sections": [
                    {
                      "title": "Stock Information",
                      "attributes": [
                        { "label": "Quantity per Unit", "value": "{{QuantityPerUnit}}" },
                        { "label": "Units on Order", "value": "{{UnitsOnOrder}}" },
                        { "label": "Reorder Level", "value": "{{ReorderLevel}}" }
                      ]
                    }
                  ]
                }
                {{/eachJoin}}
              ]
            }
          }

Handlebars is more advanced than SpEL, but it unlocks more powerful data manipulation. Most notably, it provides the ability to dynamically generate list elements from an array. The goal here is to take the products array returned by the OData API and turn each product into a list element, with its name as the title and its price as a detail attribute. Without Handlebars, you would need a fixed, hardcoded structure; with it, you can loop over however many products the API returns and build the elements array at runtime.

When scripting_type: handlebars is set, the entire message: block becomes a folded JSON string. The message type, content fields, and the elements array are all rendered by the Handlebars engine before Joule parses the result. {{#eachJoin products}} iterates the array and dynamically creates one list element per product. Use {{#eachJoin}} instead of the standard {{#each}} when building JSON arrays. It automatically handles comma separation between items, so you don't need the {{#unless @Last}},{{/unless}} workaround. Setting enableDetailView: true on the content makes each list item clickable, opening a detail panel on the right populated from the sections array on that element. Each section has a title and an attributes list of label/value pairs.

Message Type: ListMessage Type: List

When to use: search results, catalog browsing, or any multi-record response where the user needs to compare or scan across items.

Carousel Messages

The carousel type renders a horizontally scrollable row of cards. Unlike list, which stacks items vertically and supports expanded detail sections, carousel is optimized for quick visual comparison of a small set of entities. Each card has a title, subtitle, image, optional status, and buttons.

The sample capability fetches Northwind product categories and renders one card per category. Each card includes a postback button that, when clicked, sends a new utterance back to Joule to trigger the list capability filtered by that category's ID. This creates a two-step flow where you browse categories in the carousel, then drill into products for a selected category in a list.

# northwind_category_carousel/functions/get_categories_carousel.yaml
action_groups:
  - actions:
      - type: api-request
        method: GET
        system_alias: NorthwindService
        path: "/Categories?$select=CategoryID,CategoryName,Description"
        result_variable: api_result
  - condition: api_result.status_code == 200 && api_result.body.value != null && api_result.body.value.size() > 0
    actions:
      - type: set-variables
        variables:
          - name: categories
            value:  api_result.body.value ?>
      - type: message
        scripting_type: handlebars
        message: >
          {
            "type": "carousel",
            "delay": 0,
            "content": {
              "enableDetailView": false,
              "cards": [
                {{#eachJoin categories}}
                  {
                    "title": "{{CategoryName}}",
                    "subtitle": "{{Description}}",
                    "imageUrl": "sap-icon://product",
                    "buttons": [
                      {
                        "type": "postback",
                        "title": "Explore {{CategoryName}}",
                        "text": "Explore {{CategoryName}}",
                        "value": "List products in category {{CategoryID}}"
                      }
                    ]
                  }
                {{/eachJoin}}
              ]
            }
          }

The postback button's value field, "List products in category {{CategoryID}}", is the utterance that Joule receives when the user clicks “Explore Beverages”. The list capability's scenario description includes “list products in a specific category” and declares category_id as a slot, so Joule picks it up and extracts the numeric ID from that utterance. The list function then applies $filter=CategoryID%20eq%20 category_id ?> to the OData request.

Message Type: CarouselMessage Type: Carousel

When to use: a small, fixed set of entities where side-by-side comparison is useful and each item has a natural follow-on action, such as drilling into a filtered view.

Media Messages

The media type embeds an image or video directly in the chat window. For video, Joule supports YouTube, Vimeo, Daily Motion, SAP Video, and SAP TV Video URLs. For direct file links, supported formats are MP4, WEBM, OGV, and 3GP. All URLs must be publicly accessible.

The sample capability for this section is intentionally simple, with no API call, no parameters, and no slots. It demonstrates that a capability does not need to reach a backend service to be useful. The function sends a short text message followed by the video embed in a single unconditional action group.

# sapphire_2026_media/functions/get_video.yaml
action_groups:
  - actions:
      - type: message
        message:
          type: text
          content: "Here's the SAP SAPPHIRE 2026 keynote."
      - type: message
        message:
          type: media
          content: "https://www.youtube.com/watch?v=CocpyxAizwE"

The two message actions in the same action group send back-to-back. The text message appears first, giving the embed context before the video player renders below it. This chaining pattern applies to any message type combination: you can follow a card with a buttons message, or a text summary with a list of results, by placing multiple message actions in the same action group.

The content field takes the URL directly as a string without additional nesting required. For a YouTube URL, Joule renders an embedded player. The user can play the video without leaving the chat.

Message Type: MediaMessage Type: Media

When to use: product demo videos, how-to recordings, keynote highlights, or any response where a visual or audio asset communicates more than text alone.

Buttons Messages

The buttons type renders one or more clickable action buttons, optionally preceded by a summary text. It is the right choice when you want to give the user a clear set of next steps directly in the response, or when a single result can be explored in multiple ways.

This capability is the launcher for the rest of the post. Ask Joule to “show me different Joule message types in action” and it renders one postback button per message type. Clicking a button sends the right prompt back to Joule and triggers that capability without the user having to type anything.

# northwind_product_buttons/functions/show_message_type_launcher.yaml
action_groups:
  - actions:
      - type: message
        message:
          type: buttons
          content:
            title: "Pick a message type to see it in action:"
            buttons:
              - type: postback
                title: See as text
                value: "Show me product Chai as text"
              - type: postback
                title: See as card
                value: "Show me product Chai as a card"
              - type: postback
                title: Browse all products via list
                value: "Provide me a list of products"
              - type: postback
                title: See categories as carousel
                value: "Show me Northwind categories"
              - type: postback
                title: See products as integration card
                value: "Show me products as an integration card"
              - type: postback
                title: See as illustrated message
                value: "Show me product Chai as an illustrated message"
              - type: postback
                title: See as media (SAPPHIRE video)
                value: "What happened at SAPPHIRE 2026"
              - type: postback
                title: Rate Joule (Likert)
                value: "Rate my Joule experience"

The function has no parameters, no slots, and no API call. Chai is hardcoded in each postback value that requires a product name, so every downstream capability receives a valid input when the button is clicked.

Some button types available in Joule:

  • postback: sends the value string back to Joule as a new user message, continuing the conversation
  • navigation: opens a URL in the browser or navigates within the host application
  • clientdata: passes a silent payload to the host application without sending a visible message

Message Type: ButtonsMessage Type: Buttons

When to use: after delivering a result when the logical next action is predictable, or when a single entity can be viewed in multiple ways and you want to let the user choose without typing follow-up prompts.

Likert Scale Messages

The likert_scale type renders a row of labeled, clickable scale options directly in the chat. It is designed for in-chat surveys and feedback collection where you need a numeric rating on a defined scale. When the user clicks an option, the selected value is stored in a slot and becomes available to downstream steps in the same dialog function.

The supported range is 1 to 7 options. The compiler does not enforce contiguous values or prevent duplicates, which gives you flexibility to configure non-standard scales. In practice a simple 1-to-5 or 1-to-7 Likert scale is the most common use case.

The sample capability asks the user to rate how well Joule fits their needs, stores the response in a slot, and sends a personalised thank-you text message.

The scenario declares the slot so Joule knows to wait for the Likert response before advancing:

# northwind_product_likert/scenarios/joule_satisfaction.yaml
description: >-
  Capture the user's satisfaction with Joule. Use this when the user
  wants to rate their experience or answer a survey question about Joule.

slots:
  - name: joule_fit_for_purpose
    description: >-
      The user's rating of whether Joule does what they need, on a scale
      from 1 (Strongly disagree) to 5 (Strongly agree).

target:
  type: function
  name: scenario_joule_survey

The function sends the scale first, then branches on the captured slot value:

# northwind_product_likert/functions/scenario_joule_survey.yaml (relevant section)
action_groups:
  - condition: joule_fit_for_purpose == null
    actions:
      - type: message
        message:
          type: likert_scale
          content:
            prompt: "Joule does what I need it to do"
            options:
              - label: "Strongly disagree"
                value: 1
              - label: "Disagree"
                value: 2
              - label: "Neutral"
                value: 3
              - label: "Agree"
                value: 4
              - label: "Strongly agree"
                value: 5
  - condition: joule_fit_for_purpose != null
    actions:
      - type: message
        message:
          type: text
          content: "Thank you! You rated Joule's fit for purpose as  joule_fit_for_purpose ?> out of 5."

The first action group fires when the slot is empty, displaying the scale. Once the user clicks an option, the slot is populated and Joule re-enters the function, this time matching the second action group and sending the confirmation text.

A few practical notes:

  • The likert_scale message type is designed to work with slot filling. Declare the slot in the scenario so Joule waits for the user's selection before proceeding.
  • The prompt field is the question text displayed above the scale. Keep it short, one sentence.
  • options requires at least one entry and at most seven. Values do not need to be contiguous.
  • The selected value is stored as a number in the slot, so downstream SpEL comparisons use numeric operators, such as joule_fit_for_purpose >= 4.

Message Type: Likert ScaleMessage Type: Likert Scale

When to use: in-chat feedback forms, satisfaction surveys, or any scenario where you need a structured numeric rating from the user without leaving the chat.

UI Integration Cards

How it differs from a plain card message

The card message type is a fixed Joule UI component with a predefined layout. It has a title, subtitle, image, status, and description. You populate fields but you cannot change the layout itself.

The ui5integrationCard type embeds a full UI5 Integration Card driven by a JSON manifest. You control the card type, the column layout, field grouping, labels, and semantic coloring. The trade-off is verbosity: the manifest is considerably longer than a card message, and all data must come from the dialog function. The card manifest cannot reach out to a BTP destination directly.

Card types available

The UI5 Integration Card framework supports a range of card types, each suited to different data shapes and presentation needs:

Card type Best for
Object Single-record detail view with grouped label/value fields
Table Multi-record tabular display with columns, alignment, and state badges
List Compact vertically stacked items with icon, title, description
Analytical Charts: bar, line, donut, bullet, stacked bar
Timeline Chronological event sequences
Calendar Date-based scheduling views
Component Custom UI5 component embed (not supported in Joule)

The full catalog with live examples is in the UI5 Integration Card Explorer.

The Table card

The Table card is well suited for multi-record results. It renders columns with headers, supports right-alignment for numeric values, and displays semantic state badges with icons on any column using state, showStateIcon, and inverted.

The sample capability fetches up to 10 products with their category name by extending the Category entity, as well as the stock status, and renders them as a table with four columns: Product, Category, In Stock, and Status.

The key pattern is a two-step construction:

  1. Build the rows array with Handlebars, computing status and statusState per row inline using {{#if}} comparisons against UnitsInStock and Discontinued
  2. Inject the rows into the manifest via SpEL and send it as a message action
# northwind_product_integration_card/functions/get_product_integration_card.yaml
# ... (parameters and api-request action groups follow the same pattern as earlier examples)
  - condition: api_result.status_code == 200 && api_result.body.value != null && api_result.body.value.size() > 0
    actions:
      - type: set-variables
        variables:
          - name: products
            value:  api_result.body.value ?>
      - type: set-variables
        scripting_type: handlebars
        variables:
          - name: tableRows
            value: >
              [
                {{#eachJoin products}}
                {
                  "product": "{{ProductName}}",
                  "category": "{{Category.CategoryName}}",
                  "unitsInStock": "{{UnitsInStock}}",
                  "status": "{{#if Discontinued}}Discontinued{{else if (eq UnitsInStock 0)}}Out of Stock{{else if (lt UnitsInStock 10)}}Low Stock{{else}}In Stock{{/if}}",
                  "statusState": "{{#if Discontinued}}Error{{else if (eq UnitsInStock 0)}}Error{{else if (lt UnitsInStock 10)}}Warning{{else}}Success{{/if}}"
                }
                {{/eachJoin}}
              ]
      - type: message
        scripting_type: spel
        message: >
          {
            "type": "ui5integrationCard",
            "content": {
              "_version": "1.17.0",
              "sap.app": { "type": "card", "id": "northwind.product.tablecard" },
              "sap.card": {
                "type": "Table",
                "header": {
                  "title": "Northwind Products",
                  "subtitle": "Showing up to 10 products"
                },
                "content": {
                  "data": { "json":  tableRows ?> },
                  "maxItems": 10,
                  "row": {
                    "columns": [
                      { "title": "Product",   "value": "{product}",      "identifier": true },
                      { "title": "Category",  "value": "{category}" },
                      { "title": "In Stock",  "value": "{unitsInStock}", "hAlign": "End" },
                      {
                        "title": "Status",
                        "value": "{status}",
                        "state": "{statusState}",
                        "showStateIcon": true,
                        "inverted": true
                      }
                    ]
                  }
                }
              }
            }
          }

A few points worth noting:

  • $expand=Category fetches the related Category entity in a single OData call.
  • data.json placement: in Table cards, data sits inside content, not at the sap.card level. The row.columns then use {product}, {category} etc. as binding paths into the JSON array.
  • State badges: "state": "{statusState}" with "showStateIcon": true and "inverted": true produces the filled colored badge with icon seen in the UI5 Card Explorer examples. Valid state values are Success, Warning, Error, Information, and None.
  • Two-step construction: Handlebars builds tableRows as a JSON array string, then SpEL injects it into the manifest via tableRows ?>. This is necessary because the YAML schema validator rejects SpEL expressions in positions that expect a literal array.

Message Type: Ui Integration CardMessage Type: UI Integration Card

When to use: multi-record results where column layout, numeric alignment, or semantic state coloring adds clarity that a plain list message cannot provide. For a single-record detail view, the Object card type is a better fit. The full range of UI5 card types, Analytical charts, Timeline, Calendar, opens up significant display possibilities beyond what Joule's built-in message types support.

Key restrictions for ui5integrationCard in Joule:

  • Only static data bindings are supported. You cannot declare a BTP destination inside the manifest and have Joule fetch data from it at render time.
  • Action handlers defined in the manifest are not executed by Joule.
  • Component card types are not supported.
  • Filters and user-defined manifest parameters are not supported.

Illustrated Messages

The illustrated_message type renders an SAP illustration alongside a title, subtitle, and up to two call-to-action buttons. It is designed for the moments that bookend a transaction or search, such as a success confirmation, a not-found state, or a system error. Rather than showing a blank text response after an action, it gives the user a visual signal that something meaningful happened.

The sample capability looks up a Northwind product and branches into two states based on what the API returns:

  • Product found, in stock, and not discontinued: Success illustration
  • Product found but discontinued or out of stock: UnableToLoad illustration
  • Product not found at all: tntEmpty illustration
# northwind_product_illustrated/functions/get_product_illustrated.yaml (relevant section)
  - condition: api_result.status_code == 200 && api_result.body.value != null && api_result.body.value.size() > 0
    actions:
      - type: set-variables
        variables:
          - name: product
            value:  api_result.body.value[0] ?>
      - type: message
        message:
          type: illustrated_message
          content:
            illustration_name: " (product.Discontinued == true || product.UnitsInStock == 0) ? 'UnableToLoad' : 'TntSuccess' ?>"
            title: " product.ProductName ?>"
            subtitle: "$ product.UnitPrice ?> ·  product.UnitsInStock ?> units in stock"
            buttons:
              - title: Look up another product
                type: postback
                text: Look up another product
                value: "Show me another product as an illustrated message"
              - title: Browse catalog
                type: navigation
                navigation_target:
                  url: "https://services.odata.org/V3/Northwind/Northwind.svc/Products"
  - condition: api_result.status_code != 200 || api_result.body.value == null || api_result.body.value.size() == 0
    actions:
      - type: message
        message:
          type: illustrated_message
          content:
            illustration_name: NoEntries
            title: "Product not found"
            subtitle: "No product matching ' product_name ?>' was found in the Northwind catalog."
            buttons:
              - title: Try again
                type: postback
                text: Try again
                value: "Show me another product as an illustrated message"

The illustration_name is a SpEL expression that evaluates to UnableToLoad when the product is discontinued or out of stock, and TntSuccess otherwise. The not-found branch uses the separate NoEntries illustrated message.

The buttons array supports the same button types as the card and buttons message types: postback, navigation, phonenumber, and clientdata. The postback button's value field is the utterance Joule receives when the user clicks. Note it includes “as an illustrated message” to keep Joule routing to this capability rather than a different one for the next lookup.

A few practical notes:

Message Type: Illustrated MessageMessage Type: Illustrated Message

When to use: after a mutation (create, update, delete) to confirm the outcome, or when a search returns no results and you want to guide the user toward a recovery action rather than leaving them with a silent empty state.

Common Pitfalls

A few mistakes that are easy to make when building capabilities for the first time:

  • URL-encode dynamic values in OData paths. Spaces in a user-supplied slot value (e.g. “Chai Tea”) will break an OData filter if embedded directly. Add a set-variables step first with product_name.urlEncode() ?>, then reference the encoded variable in the path.
  • Use {{#eachJoin}} instead of {{#each}} when building JSON arrays in Handlebars. {{#eachJoin}} inserts commas between items automatically. {{#each}} does not, which produces invalid JSON.
  • In Table integration cards, data belongs inside content, not at the sap.card level. Placing it at the wrong level causes the card to render empty.
  • likert_scale stores the selected value as a number. Downstream SpEL comparisons must use numeric operators (>=, ==), not string comparisons.
  • Postback button value strings must match the target capability's scenario description. Joule uses the value as an utterance for intent matching. Include the keywords that route to the right capability.

UI Constraints and Limits

Each message type enforces hard limits on element counts and text lengths. Exceeding these silently truncates or hides content. The full tables covering element counts, character limits, full-screen widths, and markdown variants are in the UI Constraints and Limits page of the Joule Development Guide.

Try It Yourself

The complete sample files are in this GitHub folder. The assistant is structured as one Digital Assistant file referencing nine separate capability folders, one per message type. Each capability's scenario description includes a phrase like “as text”, “as a card”, or “as a list”. Joule then uses these during intent matching to route the incoming prompt to the correct capability. Once deployed, ask for any Northwind product with buttons to get a clickable launcher that triggers each message type without typing the prompts manually.

Deploy everything in one step:

cd Blog_01_joule_message_types
joule deploy -c -n "northwind_message_types"
joule launch "northwind_message_types"

Try each of these prompts to exercise all nine message types:

Message type Sample prompt
Text “Show me product Chai as text”
Card “Show me product Chai as a card”
Buttons “Show me different Joule message types in action”
List “Provide me a list of products”
Carousel “Show me Northwind categories”
UI Integration Card “Show me products as an integration card”
Illustrated Message “Show me product Chai as an illustrated message”
Likert Scale “Rate your Joule experience”
Media “Show me the SAPPHIRE 2026 keynote”

For the carousel, click the “Explore” button on any category card to trigger the list capability filtered by that category. The list capability also works standalone. “List products in category 1” will return only Beverages products directly.

For the illustrated message, try a product that exists (“Show me product Chai as an illustrated message”) to see the success state, and a name that matches nothing (“Show me product XYZ as an illustrated message”) to see the not-found empty state.

What is Next

In the next post we look at restricting which scenarios are visible to which users using IAS group membership and BTP role assignments. See Building Custom Joule Capabilities: Role-Based Scenario Access in Joule. More posts in this series are on the way.

Resources

Source link

Leave a Reply

Your email address will not be published. Required fields are marked *