Custom Commands Confused about "Custom Commands"?
Let us know how we can improve our documentation:
Confused about "Custom Commands"?
Let us know how we can improve our documentation:
- On This Page:
- Registering Custom Commands
- Command Fields
- Creating a Command
- List Commands
- Get a Command
- Edit a Command
- Remove a Command
- Configure a Channel Type
- Configure a Custom Action URL
- Webhook Requirements
- Verify Requests via X-Signature and X-Api-Key
- Request format
- Response format
- Discarding messages
- Rewriting messages
- Example code
OverviewCopied!Confused about "Overview"?
Let us know how we can improve our documentation:
Confused about "Overview"?
Let us know how we can improve our documentation:
By using Custom Commands, you can receive all messages sent using a specific slash command, eg. /ticket
, in your application. When configured, every slash command message happening in a Stream Chat application will propagate to an endpoint via an HTTP POST request.
1
2
3
4
const message = {
text: '/ticket suspicious transaction with id 1234'
};
const response = await channel.sendMessage(message);
Setting up your Custom Command consists of the following steps:
Registering your Custom Command
Configure a Channel Type
Configuring a Custom Action Handler URL
Implement a handler for your Custom Command
Registering Custom CommandsCopied!Confused about "Registering Custom Commands"?
Let us know how we can improve our documentation:
Confused about "Registering Custom Commands"?
Let us know how we can improve our documentation:
The API provides methods to create, list, get, update, and delete Custom Command definitions. These determine which commands are allowed to be used and how they're presented to the user by providing a description of the command.
Command FieldsCopied!Confused about "Command Fields"?
Let us know how we can improve our documentation:
Confused about "Command Fields"?
Let us know how we can improve our documentation:
name | type | description | default | optional |
---|---|---|---|---|
name | string | name of the command | - | ✓ |
description | string | description, shown in commands auto-completion | - | ✓ |
args | string | arguments help text, shown in commands auto-completion | - | ✓ |
set | string | set name used for grouping commands | - | ✓ |
Creating a CommandCopied!Confused about "Creating a Command"?
Let us know how we can improve our documentation:
Confused about "Creating a Command"?
Let us know how we can improve our documentation:
1
2
3
4
5
6
await client.createCommand({
name: "ticket",
description: "Create a support ticket",
args: "[description]",
set: "support_commands_set",
});
Please note that applications have a hard limit of 50 custom commands. If you need more than this please have a look at the Multi-tenant & Teams section.
List CommandsCopied!Confused about "List Commands"?
Let us know how we can improve our documentation:
Confused about "List Commands"?
Let us know how we can improve our documentation:
You can retrieve the list of all custom commands defined for your application.
1
await client.listCommands();
Get a CommandCopied!Confused about "Get a Command"?
Let us know how we can improve our documentation:
Confused about "Get a Command"?
Let us know how we can improve our documentation:
You can retrieve a custom command definition.
1
await client.getCommand("ticket");
Edit a CommandCopied!Confused about "Edit a Command"?
Let us know how we can improve our documentation:
Confused about "Edit a Command"?
Let us know how we can improve our documentation:
Custom command description, args & set can be changed. Only the fields that must change need to be provided, fields that are not provided to this API will remain unchanged.
1
2
3
await client.updateCommand("ticket", {
description: 'Create customer support tickets',
});
Remove a CommandCopied!Confused about "Remove a Command"?
Let us know how we can improve our documentation:
Confused about "Remove a Command"?
Let us know how we can improve our documentation:
You can remove a custom command definition.
1
await client.deleteCommand("ticket");
Configure a Channel TypeCopied!Confused about "Configure a Channel Type"?
Let us know how we can improve our documentation:
Confused about "Configure a Channel Type"?
Let us know how we can improve our documentation:
In order to be able to use this command in a channel, we’ll need to create, or update an existing, channel type to include the ticket
command.
1
2
3
4
await client.createChannelType({
name: "support-channel-type",
commands: ["ticket"],
});
Configure a Custom Action URLCopied!Confused about "Configure a Custom Action URL"?
Let us know how we can improve our documentation:
Confused about "Configure a Custom Action URL"?
Let us know how we can improve our documentation:
In order to use the defined custom commands, you will first have to set an endpoint URL in the App Settings.
1
2
3
await client.updateAppSettings({
custom_action_handler_url: "https://example.com/command_handler?type={type}",
});
{type}
variable substitution in the URL to pass on the name of the command that was triggered.Webhook RequirementsCopied!Confused about "Webhook Requirements"?
Let us know how we can improve our documentation:
Confused about "Webhook Requirements"?
Let us know how we can improve our documentation:
In order to use Custom Commands, the endpoint responding to the request must:
- Be reachable from public internet, tunneling services like Ngrok are supported
- Respond with a 200 HTTP code in less than 3 seconds
- Handle HTTP requests with POST body
- Able to parse JSON payloads
- Support HTTP/1.1
While not required, we recommend following these best-practices for production environments:
- Use HTTPS with a certificate from a trusted authority (eg. Let's Encrypt)
- Verify the "x-signature" header
- Support Keep-Alive
- Be highly available
- Offload the processing of the message (read, store, and forget)
Verify Requests via X-Signature and X-Api-KeyCopied!Confused about "Verify Requests via X-Signature and X-Api-Key"?
Let us know how we can improve our documentation:
Confused about "Verify Requests via X-Signature and X-Api-Key"?
Let us know how we can improve our documentation:
All HTTP requests can be verified as coming from Stream (and not tampered by a 3rd party) by analyzing the signature attached to the request. Every request includes an HTTP header called "x-signature" containing a cryptographic signature of the message. Your webhook endpoint can validate that payload and signature match.
1
2
// first argument is the request body as a string, second the signature header
const valid = client.verifyWebhook(req.rawBody, req.headers['x-signature']);
Request formatCopied!Confused about "Request format"?
Let us know how we can improve our documentation:
Confused about "Request format"?
Let us know how we can improve our documentation:
Your endpoint will receive a POST request with a JSON encoded body containing: message, user and form_data objects. The form_data object will contain values of the interactions initiated by either Attachment or MML actions.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
{
"message":{
"id":"",
"text":"/ticket suspicious transaction with id 1234",
"command": "ticket",
"args": "suspicious transaction with id 1234",
"html":"",
"type":"regular",
"attachments":[],
"latest_reactions":[],
"own_reactions":[],
"reaction_counts":null,
"reaction_scores":null,
"reply_count":0,
"mentioned_users":[],
"silent":false
},
"user":{
"id":"17f8ab2c-c7e7-4564-922b-e5450dbe4fe7",
"role":"user",
"banned":false,
"online":false
},
"form_data":{
"action":"submit",
"name": "John Doe",
"email": "john@doe.com"
}
}
Response formatCopied!Confused about "Response format"?
Let us know how we can improve our documentation:
Confused about "Response format"?
Let us know how we can improve our documentation:
If you intend to make any change to the message, you should return a JSON encoded response with the same message structure. Please note that not all message fields can be changed, the full list of fields that can be modified is available in the rewriting messages section.
Discarding messagesCopied!Confused about "Discarding messages"?
Let us know how we can improve our documentation:
Confused about "Discarding messages"?
Let us know how we can improve our documentation:
Your endpoint can decide to reject the command and return a user message. To do that the endpoint must return a regular message with type set to error.
1
2
3
4
5
6
{
"message":{
"type":"error",
"text":"invalid arguments for command /ticket"
}
}
Rewriting messagesCopied!Confused about "Rewriting messages"?
Let us know how we can improve our documentation:
Confused about "Rewriting messages"?
Let us know how we can improve our documentation:
You can also decide to modify the message, in that case you return the updated version of the message and it will overwrite the user input.
1
2
3
4
5
{
"message":{
"text":"Ticket #85736 has been created"
}
}
Interactions can be initiated either using Attachment actions:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
{
"message": {
"text": "Ticket #85736 has been created",
"attachments": [
{
"type": "text",
"actions": [
{
"name": "action",
"text": "Send",
"style": "primary",
"type": "button",
"value": "submit"
},
{
"name": "action",
"text": "Cancel",
"style": "default",
"type": "button",
"value": "cancel"
}
}
]
}
}
Or by using MML formatted messages:
1
2
3
4
5
{
"message": {
"text": "this message contains a MML message."
"mml": "<mml><text>Ticket #85736 has been created</text><button name=\"action\" value=\"submit\">Send</button></mml>",
}
Rewritable message fieldsCopied!Confused about "Rewritable message fields"?
Let us know how we can improve our documentation:
Confused about "Rewritable message fields"?
Let us know how we can improve our documentation:
Not all message fields can be rewritten by your hook handler, fields such as created_at or updated_at for instance are reserved and can only be set by Stream Chat APIs. Any non custom field that is not listed here will be ignored and not updated on the final message.
text
mml
i18n
show_in_channel
silent
type
attachments
any custom field
Example codeCopied!Confused about "Example code"?
Let us know how we can improve our documentation:
Confused about "Example code"?
Let us know how we can improve our documentation:
An example of how to handle incoming Custom Command requests can be found in this repo here.