Channel API
The inbound URL providers post to, and the endpoints for managing channels.
The inbound URL
POST https://service.ensembleapp.dev/channel/webhook/{channelId}This is the URL you give the provider. It is public, in the sense that it carries no platform credential: the provider authenticates with its own scheme, and the channel validates that before doing anything.
| Channel type | How a request is verified |
|---|---|
sms | Twilio request signature |
slack | Slack signing secret |
teams | The app's own credentials |
A request that fails validation is refused. A request that passes is queued and acknowledged at once, and the pipeline runs afterwards, which is covered in Configuring a channel.
The signature is checked against the credentials in the channel's configuration. If those are $ {secrets.X} references pointing at the wrong environment variable, every inbound message is
rejected and the provider's console is where you will see it, not ours. Check the channel's own
credentials first when nothing arrives.
Managing channels
GET /api/channels List this tenant's channels
GET /api/channels/{id} One channel
POST /api/channels Create one
PATCH /api/channels/{id} Update it
DELETE /api/channels/{id} Delete itAll of these take a platform JWT and are scoped to your tenant. PATCH is a partial
update, so a field you leave out keeps its stored value.
curl -X POST https://service.ensembleapp.dev/api/channels \
-H 'Authorization: Bearer <jwt>' \
-H 'Content-Type: application/json' \
-d '{
"name": "Support SMS",
"channelType": "sms",
"channelConfig": {
"provider": "twilio",
"accountSid": "${secrets.TWILIO_ACCOUNT_SID}",
"authToken": "${secrets.TWILIO_AUTH_TOKEN}",
"fromNumber": "+15550101"
},
"agentId": "<agent id>",
"agentVersion": "published",
"threadIdExpression": "${phoneNumber}",
"enabled": true
}'The channel's id is in the response, and it is what goes in the inbound URL you hand the provider.
Put credentials in as ${secrets.X} references rather than literal values. A channel
configuration is read back by the management endpoints, so a literal token is a token in every
response that lists channels. References resolve per message instead. See
Environments.
What to check when nothing happens
In the order that finds it fastest:
| Check | Because |
|---|---|
| Is the channel enabled? | A disabled channel accepts nothing and says nothing. |
| Does the provider report a delivery failure? | Signature validation happens before anything of ours runs, so a rejected request shows up at their end. |
| Do the configured credentials resolve? | A ${secrets.X} pointing at a variable that does not exist resolves to nothing, and nothing never validates. |
| Did the agent version resolve? | A pinned version that has since been archived leaves the pipeline with nothing to invoke. |
| Is a reject condition firing? | A reject answers the sender and never reaches the agent, which looks like the agent ignoring them. |
The reject condition is the one worth suspecting early. A condition written against a
toolResult field that is sometimes absent can evaluate true for reasons you did not
intend, and the sender gets the reject message rather than an answer.
Configuring a channel
The context tool, reject conditions, thread ids, data context and reply templates, and which variables each one can use.
Chat Widget
Embed a fully-featured chat into your React app in minutes. The chat connects to your Ensemble agent with real-time streaming and supports custom widgets.