Ensemble Docs

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 typeHow a request is verified
smsTwilio request signature
slackSlack signing secret
teamsThe 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 it

All 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:

CheckBecause
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.

On this page