Connect and Execute a Tool with a Connector
In this tutorial you will install a Slack connector instance, store a bot token, sync the available tools, execute the read-only list_channels tool, and inspect the execution history. By the end you will have a working connector that AI agents in your workspace can invoke.
Prerequisites
- A Chainabit account with a valid access token
- The
owneroradminrole in the workspace (required to create the instance, store credentials, and sync tools) curlavailable in your terminal- A Slack workspace and a Slack Bot Token (starts with
xoxb-...)
Set your environment variables:
export BASE="https://api.chainabit.com/api/v1"
export TOKEN="your-access-token"
export SLACK_BOT_TOKEN="xoxb-..."Connector Workflow
Step 1: Browse Available Connector Definitions
List the available connector definitions to find the Slack connector key:
curl "https://api.chainabit.com/api/v1/connectors?category=communication" \
-H "Authorization: Bearer $TOKEN"const res = await fetch(`https://api.chainabit.com/api/v1/connectors?category=communication`, {
headers: { Authorization: `Bearer ${TOKEN}` },
});
const { data } = await res.json();
console.log(data.map((c) => c.key));import requests
res = requests.get(
f"{BASE}/connectors",
params={"category": "communication"},
headers={"Authorization": f"Bearer {TOKEN}"},
)
data = res.json()["data"]
print([c["key"] for c in data])Response excerpt:
{
"data": [
{
"key": "slack",
"displayName": "Slack",
"category": "communication",
"authMethod": "api_key",
"isActive": true
},
{
"key": "gmail",
"displayName": "Gmail",
"category": "communication",
"authMethod": "oauth2",
"isActive": true
}
]
}Note the authMethod field. Slack supports api_key (bot token), which means you can authenticate without a browser redirect. The key for the next step is "slack".
Step 2: Create a Connector Instance
An instance is a workspace-scoped installation of a connector. One workspace can have multiple Slack instances:
curl -s -X POST "https://api.chainabit.com/api/v1/connectors/instances" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"connectorKey": "slack",
"displayName": "Team Slack"
}'const res = await fetch(`https://api.chainabit.com/api/v1/connectors/instances`, {
method: 'POST',
headers: {
Authorization: `Bearer ${TOKEN}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
connectorKey: 'slack',
displayName: 'Team Slack',
}),
});
const { data } = await res.json();
console.log(data.id, data.status); // "inst_abc123", "pending_auth"res = requests.post(
f"{BASE}/connectors/instances",
headers={"Authorization": f"Bearer {TOKEN}"},
json={"connectorKey": "slack", "displayName": "Team Slack"},
)
data = res.json()["data"]
print(data["id"], data["status"]) # "inst_abc123", "pending_auth"Response:
{
"data": {
"id": "inst_abc123",
"connectorKey": "slack",
"displayName": "Team Slack",
"status": "pending_auth",
"enabled": true,
"config": {},
"lastHealthCheck": null,
"healthMessage": null,
"createdAt": "2026-03-23T10:00:00Z",
"updatedAt": "2026-03-23T10:00:00Z"
}
}The instance starts with status: "pending_auth". Save the instance ID:
export INSTANCE_ID="inst_abc123"Step 3: Store Credentials
Store the Slack bot token. Credentials are encrypted at rest and never returned in API responses:
curl -s -X POST "https://api.chainabit.com/api/v1/connectors/instances/$INSTANCE_ID/credentials" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d "{
\"credType\": \"api_key\",
\"credentials\": {
\"apiKey\": \"$SLACK_BOT_TOKEN\"
}
}"const res = await fetch(
`https://api.chainabit.com/api/v1/connectors/instances/${INSTANCE_ID}/credentials`,
{
method: 'POST',
headers: {
Authorization: `Bearer ${TOKEN}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
credType: 'api_key',
credentials: { apiKey: process.env.SLACK_BOT_TOKEN },
}),
},
);
const { data } = await res.json();
console.log(data.stored); // trueimport os
res = requests.post(
f"{BASE}/connectors/instances/{INSTANCE_ID}/credentials",
headers={"Authorization": f"Bearer {TOKEN}"},
json={
"credType": "api_key",
"credentials": {"apiKey": os.environ["SLACK_BOT_TOKEN"]},
},
)
data = res.json()["data"]
print(data["stored"]) # TrueResponse:
{
"data": {
"stored": true
}
}Credentials are write-only. You can check whether credentials are stored via
GET /connectors/instances/:id/credentials/status, but their values are never readable through the API.
Step 4: Test the Connection
Send a health-check to confirm the bot token works and Slack is reachable:
curl -s -X POST "https://api.chainabit.com/api/v1/connectors/instances/$INSTANCE_ID/test" \
-H "Authorization: Bearer $TOKEN"const res = await fetch(
`https://api.chainabit.com/api/v1/connectors/instances/${INSTANCE_ID}/test`,
{
method: 'POST',
headers: { Authorization: `Bearer ${TOKEN}` },
},
);
const { data } = await res.json();
if (!data.healthy) throw new Error(data.message);
console.log(`Connected (${data.latencyMs}ms)`);res = requests.post(
f"{BASE}/connectors/instances/{INSTANCE_ID}/test",
headers={"Authorization": f"Bearer {TOKEN}"},
)
data = res.json()["data"]
if not data["healthy"]:
raise RuntimeError(data["message"])
print(f"Connected ({data['latencyMs']}ms)")Response:
{
"data": {
"healthy": true,
"message": "Connection successful",
"latencyMs": 187
}
}If healthy is false, check that your bot token starts with xoxb- and that the bot is installed in your Slack workspace.
Step 5: Sync and List Tools
Pull the available tools from the Slack adapter, then list them:
curl -s -X POST "https://api.chainabit.com/api/v1/connectors/instances/$INSTANCE_ID/sync-tools" \
-H "Authorization: Bearer $TOKEN"curl -s "https://api.chainabit.com/api/v1/connectors/instances/$INSTANCE_ID/tools" \
-H "Authorization: Bearer $TOKEN"List tools response:
{
"data": [
{
"id": "tool_abc123",
"connectorKey": "slack",
"toolKey": "list_channels",
"displayName": "List Channels",
"description": "List all public and private channels the bot has access to.",
"inputSchema": {
"type": "object",
"properties": {
"limit": { "type": "number" },
"cursor": { "type": "string" },
"types": { "type": "string" }
}
},
"requiresApproval": false,
"isActive": true,
"source": "auto_discovered"
}
]
}Save the tool ID for the list_channels tool:
export TOOL_ID="tool_abc123"Step 6: Execute a Tool
Call list_channels to list the channels the bot can see. The input is validated against the tool's inputSchema:
curl -s -X POST "https://api.chainabit.com/api/v1/connectors/instances/$INSTANCE_ID/tools/$TOOL_ID/execute" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"input": {
"limit": 20
}
}'const res = await fetch(
`https://api.chainabit.com/api/v1/connectors/instances/${INSTANCE_ID}/tools/${TOOL_ID}/execute`,
{
method: 'POST',
headers: {
Authorization: `Bearer ${TOKEN}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
input: { limit: 20 },
}),
},
);
const { data } = await res.json();
if (data.success) {
console.log('Channels:', data.output.channels);
} else {
console.error('Tool error:', data.error);
}res = requests.post(
f"{BASE}/connectors/instances/{INSTANCE_ID}/tools/{TOOL_ID}/execute",
headers={"Authorization": f"Bearer {TOKEN}"},
json={"input": {"limit": 20}},
)
data = res.json()["data"]
if data["success"]:
print("Channels:", data["output"]["channels"])
else:
print("Tool error:", data["error"])Response on success:
{
"data": {
"success": true,
"output": {
"ok": true,
"channels": [
{ "id": "C01234567", "name": "general", "is_private": false }
]
},
"error": null,
"httpStatus": 200,
"durationMs": 342
}
}Tools that require approval
Tools that change data in Slack, such as send_message and upload_file, require approval. Direct execution refuses them with 403 and error code connector_tool_approval_required, and nothing is sent to Slack. Ask Chao to run them instead, which applies its approval rules before acting.
Common errors
| Error | Cause | Fix |
|---|---|---|
connector_tool_approval_required (403) | The tool requires approval, for example send_message | Ask Chao to run it |
No credentials configured | Step 3 was skipped or credentials were deleted | Re-run Step 3 |
Tool is disabled on this instance | isActive: false on the tool | PATCH the tool to re-enable it |
Summary
In this tutorial you:
- Browsed connector definitions to confirm the
slackkey and its auth method - Created a connector instance (starts as
pending_auth) - Stored an API key credential (write-only, encrypted at rest)
- Tested the connection to confirm the token is valid
- Synced and listed tools exposed by the Slack adapter
- Executed the read-only
list_channelstool directly
Next Steps
- Connectors API Reference for all available endpoints
- Connector Catalog to browse all supported connectors
- Chain Triggers to run chains (workflows) that invoke connector tools