Channels
curl --request GET \
--url https://api.example.com/v1/agent-channelsimport requests
url = "https://api.example.com/v1/agent-channels"
response = requests.get(url)
print(response.text)const options = {method: 'GET'};
fetch('https://api.example.com/v1/agent-channels', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.example.com/v1/agent-channels",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://api.example.com/v1/agent-channels"
req, _ := http.NewRequest("GET", url, nil)
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.get("https://api.example.com/v1/agent-channels")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.example.com/v1/agent-channels")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Get.new(url)
response = http.request(request)
puts response.read_bodyChannels
Channels
Connect external messaging channels to agents
GET
/
v1
/
agent-channels
Channels
curl --request GET \
--url https://api.example.com/v1/agent-channelsimport requests
url = "https://api.example.com/v1/agent-channels"
response = requests.get(url)
print(response.text)const options = {method: 'GET'};
fetch('https://api.example.com/v1/agent-channels', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.example.com/v1/agent-channels",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "GET",
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://api.example.com/v1/agent-channels"
req, _ := http.NewRequest("GET", url, nil)
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.get("https://api.example.com/v1/agent-channels")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.example.com/v1/agent-channels")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Get.new(url)
response = http.request(request)
puts response.read_bodyChannel endpoints use the orchestrator:
Use
Then register the webhook:
After storing the access token and app secret as Runtools secrets, connect the channel:
Register the webhook and copy the returned
Each WhatsApp Business phone number can be connected to one agent at a time.
Approve a pending request:
Approve a known conversation directly:
Revoke access:
When a linked product user messages the channel, Runtools runs the agent with Hosted Connect end-user scope. Missing Gmail, Stripe, Slack, or other customer data credentials return
Complete it after your product verifies the provider sender identity:
For trusted server-side imports, link directly:
List and revoke links:
https://api.runtools.ai/v1
List channels
curl "https://api.runtools.ai/v1/agent-channels?agent=gmail-agent" \
-H "X-API-Key: $RUNTOOLS_API_KEY"
provider=telegram or provider=whatsapp without agent to list connected provider accounts in the organization.
Connect Telegram
curl -X POST https://api.runtools.ai/v1/agent-channels \
-H "X-API-Key: $RUNTOOLS_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"agent": "gmail-agent",
"provider": "telegram",
"mode": "byok",
"botTokenSecretName": "TELEGRAM_GMAIL_AGENT_BOT_TOKEN"
}'
curl -X POST https://api.runtools.ai/v1/agent-channels/ch_123/register-webhook \
-H "X-API-Key: $RUNTOOLS_API_KEY"
Connect WhatsApp
Validate the Meta access token and phone number before saving secrets:curl -X POST https://api.runtools.ai/v1/agent-channels/whatsapp/validate-connection \
-H "X-API-Key: $RUNTOOLS_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"agent": "gmail-agent",
"accessToken": "'"$WHATSAPP_ACCESS_TOKEN"'",
"phoneNumberId": "123456789012345",
"businessAccountId": "987654321098765"
}'
curl -X POST https://api.runtools.ai/v1/agent-channels \
-H "X-API-Key: $RUNTOOLS_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"agent": "gmail-agent",
"provider": "whatsapp",
"mode": "byok",
"accessTokenSecretName": "WHATSAPP_GMAIL_AGENT_ACCESS_TOKEN",
"appSecretSecretName": "WHATSAPP_GMAIL_AGENT_APP_SECRET",
"phoneNumberId": "123456789012345",
"businessAccountId": "987654321098765"
}'
setup.webhookUrl and setup.verifyToken into Meta’s WhatsApp webhook settings:
curl -X POST https://api.runtools.ai/v1/agent-channels/ch_456/register-webhook \
-H "X-API-Key: $RUNTOOLS_API_KEY"
Privacy
curl https://api.runtools.ai/v1/agent-channels/ch_123/privacy \
-H "X-API-Key: $RUNTOOLS_API_KEY"
curl -X PATCH https://api.runtools.ai/v1/agent-channels/ch_123/privacy \
-H "X-API-Key: $RUNTOOLS_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"mode": "approved_only",
"allowPrivateChats": true,
"allowGroups": true,
"unauthorizedReply": "message"
}'
curl -X POST https://api.runtools.ai/v1/agent-channels/ch_123/privacy/requests/req_123/approve \
-H "X-API-Key: $RUNTOOLS_API_KEY"
curl -X POST https://api.runtools.ai/v1/agent-channels/ch_123/privacy/grants \
-H "X-API-Key: $RUNTOOLS_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"scope": "conversation",
"externalConversationId": "telegram:chat:123456789",
"conversationType": "private",
"conversationDisplayName": "Arthur (@ArtKara1)"
}'
curl -X DELETE https://api.runtools.ai/v1/agent-channels/ch_123/privacy/grants/grant_123 \
-H "X-API-Key: $RUNTOOLS_API_KEY"
Product channel bindings
Product channel bindings let a developer-owned Telegram, WhatsApp, Slack, Discord, iMessage, or SMS channel serve end users of a product built on Runtools. The channel credential stays builder-owned. The person chatting is resolved through a separate channel identity link:appId + productExternalUserId + agentChannelId + providerExternalUserId
connect_required; product channels never fall back to builder Connected Apps.
Bind a channel to a product app
curl -X POST https://api.runtools.ai/v1/apps/atlas/channel-bindings \
-H "X-API-Key: $RUNTOOLS_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"agentChannelId": "ch_123",
"identityPolicy": "link_required",
"groupPolicy": "sender_scoped",
"unknownSenderReply": "Open Atlas to link this chat: {linkUrl}",
"metadata": {
"linkUrl": "https://atlas.example.com/settings/channels/link"
}
}'
curl https://api.runtools.ai/v1/apps/atlas/channel-bindings \
-H "X-API-Key: $RUNTOOLS_API_KEY"
curl -X DELETE https://api.runtools.ai/v1/apps/atlas/channel-bindings/bind_123 \
-H "X-API-Key: $RUNTOOLS_API_KEY"
identityPolicy must be link_required. groupPolicy can be disabled or sender_scoped.
Link a channel sender to a product user
Create a link session from your product server:curl -X POST https://api.runtools.ai/v1/apps/atlas/channel-link-sessions \
-H "X-API-Key: $RUNTOOLS_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"agentChannelId": "ch_123",
"externalUserId": "user_123",
"returnUrl": "https://atlas.example.com/settings/channels"
}'
curl -X POST https://api.runtools.ai/v1/apps/atlas/channel-link-sessions/sess_123/complete \
-H "X-API-Key: $RUNTOOLS_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"token": "pcl_...",
"productExternalUserId": "user_123",
"providerExternalUserId": "telegram_user_456",
"externalConversationId": "telegram:chat:789",
"displayName": "Sam Carter",
"username": "sam"
}'
curl -X POST https://api.runtools.ai/v1/apps/atlas/channel-links \
-H "X-API-Key: $RUNTOOLS_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"agentChannelId": "ch_123",
"externalUserId": "telegram_user_456",
"productExternalUserId": "user_123",
"externalConversationId": "telegram:chat:789"
}'
curl "https://api.runtools.ai/v1/apps/atlas/channel-links?agentChannelId=ch_123&productExternalUserId=user_123" \
-H "X-API-Key: $RUNTOOLS_API_KEY"
curl -X POST https://api.runtools.ai/v1/apps/atlas/channel-links/link_123/revoke \
-H "X-API-Key: $RUNTOOLS_API_KEY"