The web connector lets external users connect to your app through a web form, for example a contact form.


We will use the contact form above to demonstrate how the web connector works.

Connect an HTML form to a Conclude app in two steps

Check out the live demo on CodePen. This is a complete web form implementation in about 150 lines of HTML, CSS and Javascript combined. Feel free to use and modify this code for your own web forms (MIT license).

Step 1: Create a contact form app in Conclude

First create a new Slack channel called #ext-contact, where you will process incoming messages from the contact form.

Then install a Conclude app with a web connector that can receive input from the web form: /c app install contact-form #ext-contact

Step 2: Connect the external web form to the app

Form data is sent to the app by setting the URL of the form action in the HTML code:

  <form id="conclude-form" method="POST" action="">

This URL must refer to your app, where is a unique web connector ID that identifies your app. It looks something like this: 6j09qa6sr0x186lx4k4qlkgbk9xnmg8

Run the command /c app web-connector #ext-contact to show the web connector ID. If the app does not have a web connector ID, this command will generate one for you.

The web connector ID is <your unique ID>

Insert the ID into the action URL in the HTML code (replace … with the ID). The ID can also be found in the app’s source code. Type /c app edit #ext-contact to open the external editor.

That’s all it takes to connect a web form to a Conclude app.

Handling web connector responses

The web connector contains two return paths; on_success when the app received the information, or on_error when an error occurred.

  "web_connector": {
     "id": "<unique ID>",
     "on_success": "",
     "on_error": "${error}",

The javascript code checks the return status, and displays a message to the user (see the JS section of the CodePen for the actual code).

The ${error} variable expands to one of these:

  • duplicate_request_error: The exact same form was already submitted in the last 5 minutes.
  • rate_limit_error: Too many forms have been submitted in the last minute.
  • domain_error: The form was submitted from an unknown domain.
  • recaptcha_error: Google reCAPTCHA v3 thinks a bot submitted the form.
  • disabled_error: The web connector has been disabled.
  • internal_error: An internal error occurred.

These error codes are described below.

Mapping form fields to attributes

Conclude apps have attributes named title, body and so forth, while the web form might use different attribute names.

You can create an attribute map to translate external web form attribute names to internal attribute names. In some cases you may want to set a default value in case the external attribute has no value.

  <input type="text" name="subject" id="a1" placeholder="Enter a subject">
  <textarea name="message" id="a2" placeholder="Enter a message"></textarea>

The HTML contains a form attribute named subject and one named message. These external attributes can be mapped to internal attributes with the attribute_map below:

  "web_connector": {
     "id": "<unique ID>",
     "attribute_map": [
           "internal": "title",
           "default_value": "Message from ${first-name} ${last-name}: ${subject}"
           "external": "message",
           "internal": "body",
           "default_value": "<no message>"

Here we map the web form attribute message to the app attribute body, and assign a default value in case this attribute is empty. Instead of mapping the web form attribute subject to the app attribute title, we compose a new title consisting of several web form attributes.

Protecting your app

Opening up your app to external users also opens it up to bots that potentially can flood your app with spam and junk. Conclude offers several ways to deal with this.

  "rate_limit": "6/min",
  "web_connector": {
     "id": "<unique ID>",
     "domain": "",

You can set rate_limit to limit the maximum number of submitted forms per minute. The app will return the error code rate_limit_error if the rate limit is exceeded.

You can set domain to tell the web connector to only forward requests from specific domains. Any attempt to submit a web form from any other domain will result in a domain_error.

The best way to protect your app is to implement Google reCAPTCHA, which can identify and block automated access to your web form (also known as bots).

Using Google reCAPTCHA

Conclude interoperates with reCAPTCHA version 3, which works without any direct user interaction. In order to use reCAPTCHA, you will first need to get your own keys.

First, get your reCAPTCHA v3 keys on You will get a public site key, and a private key that is secret.

Use the public site key in the HTML and the javascript

Uncomment line 4 in the HTML code:

  <script src=""></script>

Replace reCAPTCHA_site_key with your public site key.

You will also need to set the public site key on line 1 in the javascript code:

  const reCaptchaSiteKey = 'reCAPTCHA_site_key';
Register the secret private key with the app

The private key should not be shared with unauthorized people. Conclude can store such private app keys in a protected place with strict access control; app secrets.

Open the app secrets with /c app secrets #ext-contact and register the private reCAPTCHA key.

Your contact-form app will now return the error recaptcha_error if Google reCAPTCHA believes the submitted form came from a bot.

Add reviewers

You can set reviewers to review an incoming request before creating an activity channel and inviting team members. Reviewers act as a filter that can accept or reject incoming requests, or modify the actual requests. The review feature also lets you triage requests.

Create a new Slack channel #ext-contact-review where incoming requests arrive to be reviewed before they are sent to the team.

Finally, add these two lines to your app:

  "review.members": "#ext-contact-review",
  "review.alert": "#ext-contact-review",

This sets the members of the #ext-contact-review channel as reviewers, and will send an alert to the channel when a new request comes in. The reviewers can click a button to accept or reject the request, or modify it. When a reviewer accepts the request it will be sent to the #ext-contact channel as before.

Supercharge your team!

Bridge the collaboration gap with Conclude apps.