Handling interactivity in Adaptive Cards

Last updated: 2024-01-31Contributors
Edit this page

Types of interactivity for an adaptive card

In addition to supporting the full array of display elements that aid in the rendering and presentation of a card, RingCentral currently supports the following types of interactivity, corresponding to each of the supported Actions in Adaptive Cards. These include:

  • Action.OpenUrl - when invoked, RingCentral will open the designated URL in an external web browser
  • Action.ShowCard - when invoked, RingCentral will display the designated Adaptive Card
  • Action.Submit - when invoked, RingCentral will collect the data entered into affiliated input elements and transmit them to the client
  • Action.ToggleVisbility - when invoked, RingCentral will show and/or hide designated target elements

Only the Action.Submit action requires a developer to implement handlers and webhooks in order to receive and process submitted data. All other actions define their behavior natively within the card itself.

Handling a form submission

If your application posts cards that utilize the Action.Submit action, or in other words, if the card contains input elements and allows users to submit a form, then your app will need to implement the following sequence.

Design the card

Your first step is to design the card if you have not already done so. The example below will be based upon the following card, simulating and add-in that a user would install into their personal chat to prompt them to enter a call log entry whenever they completed a call.

Excluded from this example, is how this message below is triggered. Presumably when the add-in is installed, the add-in would subscribe to an Extension Telephony Session event so that it can be notified when a call with the installing user has ended. Upon receiving that event, the add-in would then post the message below.

To make editing and loading this adaptive card a little easier, we will place the contents of this card into its own dedicated file called callNotesCard.json.

{
    "type": "AdaptiveCard",
    "$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
    "version": "1.3",
    "body": [
        {
            "type": "TextBlock",
            "size": "Medium",
            "weight": "Bolder",
            "text": "Log your call"
        },
        {
            "type": "Input.Text",
            "placeholder": "Enter call log notes",
            "label": "You just completed a phone call with ${callerPhoneNumber}, please enter your call log entry.",
            "isMultiline": true,
            "id": "call-log"
        }
    ],
    "actions": [
        {
            "type": "Action.Submit",
            "title": "Save"
        }
    ]
}

Post the card

To post the card, the app would utilize either the REST API to post the card. In the code excerpt below, the contents of the card are read off of the filesystem and processed through a templating system to replace variables found in the card template with values passed in at runtime.

async function post_card( group ) {
    try {
        var cardData = {
            'callerPhoneNumber' : '510-555-2823'
        }
        const template = new Template(cardTemplate);
        const card = template.expand({ $root: cardData });
        var resp = await platform.post('/restapi/v1.0/glip/chats/'+group+'/adaptive-cards',
                                       card);
        var jsonObj = await resp.json()
        console.log( JSON.stringify(jsonObj) )
    } catch (e) {
        console.log(e)
    }
}

The above card when posted to RingCentral, will appear as shown below:

Basic form submission

Receive interactive message webhook

When the user clicks the submit button, RingCentral will transmit the form data that the user entered, and deliver it as a webhook to the URL designated in the "Outbound webhook URL" field associated with your app, under the "Interactive messages" feature.

Here is a sample webhook you might receive for the adaptive card above.

{
    'uuid': 'abcdefg',
    'timestamp': '2016-03-10T18:07:52.534Z',
    'type': 'button_submit',
    'appId': 'abcdefg-123443-ghijklmnop',
    'user': {
        'id': 'abcdefg-1234',
        'firstName': 'Jim',
        'lastName': 'Halpert',
        'accountId': '123456789'
    },
    'conversation': {
        'id': 'abcdefg-1234',
        'type': 'group',
        'public': true,
        'name': 'Top paper sales'
    },
    'post': {
        'id': 'abcdefg-1234',
        'creationTime': '2016-03-10T18:07:52.534Z',
        'lastModifiedTime': '2016-03-10T18:07:52.534Z',
    },
    'data': {
        'call-log' : 'Spoke to Christian from Lackawanna Country. He might be interested in 20 reams.'
    }
}

Interactive messages and generic incoming webhooks

Incoming webhooks created inside the RingCentral desktop client are unable to support interactive messages, specifically cards that utilize the Action.Submit element because those webhooks are not associated with an Outbound Webhook URL. To build and test the Action.Submit element, please post the message from a bot or add-in.

Verify the sender of the event

To ensure RingCentral is the originator of the event, and no one else might be impersonating the sender, we recommend people verify the uthenticity of the event by comparing the contents of the X-Glip-Signature header with a SHA1 hash of the request body.

const SHARED_SECRET = "abcdefghijklmnopqrstuvwxyz"
api.post('/hook', function (req, res) {
 var signature = req.get('X-Glip-Signature');
 var bodyCrypted = require('crypto')
     .createHmac(
         'sha1',
         SHARED_SECRET
     )
     .update(JSON.stringify(req.body))
     .digest()
     .toString('hex');

 if (bodyCrypted !== signature) {
     res.status(401).send();
     return;
 }

 console.log('Webhook received')
 // code continues
})

Post a response

After processing the event, your application must respond to acknowledge receipt of the event, and optionally transmit an error message back to RingCentral to display to the end user.

const SHARED_SECRET = "abcdefghijklmnopqrstuvwxyz"
api.post('/hook', function (req, res) {
 var signature = req.get('X-Glip-Signature', 'sha1=')
 var bodyCrypted = require('crypto')
  .createHmac('sha1', SHARED_SECRET)
  .update(JSON.stringify(req.body))
  .digest('hex')
 if (bodyCrypted !== signature) {
  res.status(401).send()
  return
 }
 console.log('Webhook received')
 var error = process_event(req.body)
 if (error) {
   res.status(200).send({
     "type": "message",
     "text": "An error occurred: " + error
   })
 } else {
   res.status(200).send()
   return
 }
})

(Optionally) Update the card

Once the event has been completely processed, you may have the need to update the contents of the card to reflect a change in status. This can be done by use the readGlipPost and the updateGlipPost operations to post a new version of the card.