Skip to content

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 owner or admin role in the workspace (required to create the instance, store credentials, and sync tools)
  • curl available in your terminal
  • A Slack workspace and a Slack Bot Token (starts with xoxb-...)

Set your environment variables:

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

bash
curl "https://api.chainabit.com/api/v1/connectors?category=communication" \
  -H "Authorization: Bearer $TOKEN"
javascript
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));
python
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:

json
{
  "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:

bash
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"
  }'
javascript
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"
python
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:

json
{
  "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:

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

bash
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\"
    }
  }"
javascript
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); // true
python
import 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"])  # True

Response:

json
{
  "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:

bash
curl -s -X POST "https://api.chainabit.com/api/v1/connectors/instances/$INSTANCE_ID/test" \
  -H "Authorization: Bearer $TOKEN"
javascript
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)`);
python
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:

json
{
  "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:

bash
curl -s -X POST "https://api.chainabit.com/api/v1/connectors/instances/$INSTANCE_ID/sync-tools" \
  -H "Authorization: Bearer $TOKEN"
bash
curl -s "https://api.chainabit.com/api/v1/connectors/instances/$INSTANCE_ID/tools" \
  -H "Authorization: Bearer $TOKEN"

List tools response:

json
{
  "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:

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

bash
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
    }
  }'
javascript
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);
}
python
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:

json
{
  "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 ​

ErrorCauseFix
connector_tool_approval_required (403)The tool requires approval, for example send_messageAsk Chao to run it
No credentials configuredStep 3 was skipped or credentials were deletedRe-run Step 3
Tool is disabled on this instanceisActive: false on the toolPATCH the tool to re-enable it

Summary ​

In this tutorial you:

  1. Browsed connector definitions to confirm the slack key and its auth method
  2. Created a connector instance (starts as pending_auth)
  3. Stored an API key credential (write-only, encrypted at rest)
  4. Tested the connection to confirm the token is valid
  5. Synced and listed tools exposed by the Slack adapter
  6. Executed the read-only list_channels tool directly

Next Steps ​

Built with purpose.