# Send Create a Botpress AI agent with the ADK using OpenClaw to your agent
Hand the extracted package to your coding agent with a concrete install brief instead of figuring it out manually.
## Fast path
- Download the package from Yavira.
- Extract it into a folder your agent can access.
- Paste one of the prompts below and point your agent at the extracted folder.
## Suggested prompts
### New install

```text
I downloaded a skill package from Yavira. Read SKILL.md from the extracted folder and install it by following the included instructions. Then review README.md for any prerequisites, environment setup, or post-install checks. Tell me what you changed and call out any manual steps you could not complete.
```
### Upgrade existing

```text
I downloaded an updated skill package from Yavira. Read SKILL.md from the extracted folder, compare it with my current installation, and upgrade it while preserving any custom configuration unless the package docs explicitly say otherwise. Then review README.md for any prerequisites, environment setup, or post-install checks. Summarize what changed and any follow-up checks I should run.
```
## Machine-readable fields
```json
{
  "schemaVersion": "1.0",
  "item": {
    "slug": "botpress-adk",
    "name": "Create a Botpress AI agent with the ADK using OpenClaw",
    "source": "tencent",
    "type": "skill",
    "category": "AI 智能",
    "sourceUrl": "https://clawhub.ai/yueranlu/botpress-adk",
    "canonicalUrl": "https://clawhub.ai/yueranlu/botpress-adk",
    "targetPlatform": "OpenClaw"
  },
  "install": {
    "downloadUrl": "/downloads/botpress-adk",
    "sourceDownloadUrl": "https://wry-manatee-359.convex.site/api/v1/download?slug=botpress-adk",
    "sourcePlatform": "tencent",
    "targetPlatform": "OpenClaw",
    "packageFormat": "ZIP package",
    "primaryDoc": "SKILL.md",
    "includedAssets": [
      "README.md",
      "SKILL.md"
    ],
    "downloadMode": "redirect",
    "sourceHealth": {
      "source": "tencent",
      "slug": "botpress-adk",
      "status": "healthy",
      "reason": "direct_download_ok",
      "recommendedAction": "download",
      "checkedAt": "2026-04-30T22:19:54.502Z",
      "expiresAt": "2026-05-07T22:19:54.502Z",
      "httpStatus": 200,
      "finalUrl": "https://wry-manatee-359.convex.site/api/v1/download?slug=botpress-adk",
      "contentType": "application/zip",
      "probeMethod": "head",
      "details": {
        "probeUrl": "https://wry-manatee-359.convex.site/api/v1/download?slug=botpress-adk",
        "contentDisposition": "attachment; filename=\"botpress-adk-1.0.1.zip\"",
        "redirectLocation": null,
        "bodySnippet": null,
        "slug": "botpress-adk"
      },
      "scope": "item",
      "summary": "Item download looks usable.",
      "detail": "Yavira can redirect you to the upstream package for this item.",
      "primaryActionLabel": "Download for OpenClaw",
      "primaryActionHref": "/downloads/botpress-adk"
    },
    "validation": {
      "installChecklist": [
        "Use the Yavira download entry.",
        "Review SKILL.md after the package is downloaded.",
        "Confirm the extracted package contains the expected setup assets."
      ],
      "postInstallChecks": [
        "Confirm the extracted package includes the expected docs or setup files.",
        "Validate the skill or prompts are available in your target agent workspace.",
        "Capture any manual follow-up steps the agent could not complete."
      ]
    }
  },
  "links": {
    "detailUrl": "https://openagent3.xyz/skills/botpress-adk",
    "downloadUrl": "https://openagent3.xyz/downloads/botpress-adk",
    "agentUrl": "https://openagent3.xyz/skills/botpress-adk/agent",
    "manifestUrl": "https://openagent3.xyz/skills/botpress-adk/agent.json",
    "briefUrl": "https://openagent3.xyz/skills/botpress-adk/agent.md"
  }
}
```
## Documentation

### Botpress ADK Development Guide

A comprehensive guide for building AI bots with the Botpress Agent Development Kit (ADK).

### When to Use

User asks to build a Botpress bot or chatbot
User mentions ADK, Agent Development Kit, or Botpress
User wants to create actions, tools, workflows, conversations, tables, triggers, or knowledge bases
User needs help with adk CLI commands (init, dev, deploy, link)
User has ADK-related errors or needs troubleshooting
User asks about bot configuration, state management, or integrations

### Quick Reference

The ADK is a convention-based TypeScript framework where file structure maps directly to bot behavior.

Your role: Guide users through the entire bot development lifecycle - from project setup to deployment. Use the patterns and code examples in this skill to write correct, working ADK code.

Key principle: In ADK, where you put files matters. Each component type has a specific src/ subdirectory, and files are auto-discovered based on location.

### How to Use This Skill

This skill is your primary reference for building Botpress bots. When a user asks you to build something with the ADK:

Identify what they need - Is it a new bot, a feature (action, tool, workflow), data storage (table), or event handling (trigger)?
Check the correct directory - Each component type goes in a specific src/ subdirectory
Use the patterns below - Follow the code examples exactly, they represent the correct ADK conventions
Run adk --help - For CLI commands not covered here, or adk <command> --help for specific help

Decision Guide - What Component to Create:

User Wants To...Create ThisLocationHandle user messagesConversationsrc/conversations/Add a function the AI can callToolsrc/tools/Add reusable business logicActionsrc/actions/Run background/scheduled tasksWorkflowsrc/workflows/Store structured dataTablesrc/tables/React to events (user created, etc.)Triggersrc/triggers/Give AI access to docs/dataKnowledge Basesrc/knowledge/Connect external service (Slack, etc.)Integrationadk add <name>

If the information in this skill isn't enough, fetch the corresponding GitHub reference file (links provided in each section) for more detailed specifications.

### Important: ADK is AI-Native

The ADK does NOT use traditional chatbot patterns. Don't create intents, entities, or dialog flows.

Instead of:

Defining intents (greet, orderPizza, checkStatus)
Training entity extraction (@pizzaSize, @toppings)
Manually routing to intent handlers

ADK uses:

execute() - The AI understands user intent naturally from instructions
Tools - AI autonomously decides when to call your functions
zai.extract() - Schema-based structured data extraction
Knowledge bases - RAG for grounding responses in your docs

Docs: https://www.botpress.com/docs/adk/
GitHub: https://github.com/botpress/skills/tree/master/skills/adk

### Prerequisites & Installation

Before using the ADK, ensure the user has:

Botpress Account - Create at https://app.botpress.cloud
Node.js v22.0.0+ - Check with node --version
Package Manager - bun (recommended), pnpm, yarn, or npm

Install the ADK CLI:

macOS & Linux:

curl -fsSL https://github.com/botpress/adk/releases/latest/download/install.sh | bash

Windows (PowerShell):

powershell -c "irm https://github.com/botpress/adk/releases/latest/download/install.ps1 | iex"

Verify installation:

adk --version

If installation fails, check https://github.com/botpress/adk/releases for manual download options.

Docs: https://www.botpress.com/docs/adk/quickstart
GitHub: https://raw.githubusercontent.com/botpress/skills/master/skills/adk/references/cli.md

### Quick Start

Once the ADK CLI is installed, create a new bot:

adk init my-bot         # Create project (choose "Hello World" template for beginners)
cd my-bot
npm install             # Or bun/pnpm/yarn
adk login               # Authenticate with Botpress Cloud
adk add chat            # Add the chat integration for testing
adk dev                 # Start dev server with hot reload
adk chat                # Test in CLI (run in separate terminal)
adk deploy              # Deploy to production when ready

The visual console at http://localhost:3001/ lets you configure integrations and test the bot.

Docs: https://www.botpress.com/docs/adk/quickstart
GitHub: https://raw.githubusercontent.com/botpress/skills/master/skills/adk/references/cli.md

### Linking and Deploying Your Bot

IMPORTANT: Your bot must be linked to Botpress Cloud and deployed for it to work. The ADK runs locally during development but the bot itself lives in Botpress Cloud.

### The Correct Order: Link → Dev → Deploy

Follow this order to get your bot working:

# 1. LINK - Connect your project to Botpress Cloud (creates agent.json)
adk link

# 2. DEV - Start the development server (hot reload, testing)
adk dev

# 3. DEPLOY - Push to production when ready
adk deploy

Step-by-step:

adk link - Links your local project to a bot in Botpress Cloud. This creates agent.json with your workspace and bot IDs. Run this first before anything else.


adk dev - Starts the local development server with hot reloading. Opens the dev console at http://localhost:3001 where you can configure integrations and test your bot. Use adk chat in a separate terminal to test.


adk deploy - Deploys your bot to production. Run this when you're ready for your bot to be live and accessible through production channels (Slack, WhatsApp, webchat, etc.).

### Troubleshooting Errors

If you encounter errors when running adk dev or adk deploy:

Check the logs - Look at the terminal output or the logs panel in the dev console at http://localhost:3001
Copy the error message - Select and copy the full error message from the logs
Ask for help - Paste the error back to the AI assistant and ask it to help fix the issue

Common error scenarios:

Integration configuration errors: Usually means an integration needs to be configured in the UI at localhost:3001
Type errors: Often caused by incorrect imports or schema mismatches
Deployment failures: May indicate missing environment variables or invalid configuration

Example workflow for fixing errors:

1. Run \`adk dev\` or \`adk deploy\`
2. See error in terminal/logs
3. Copy the error message
4. Tell the AI: "I got this error when running adk dev: [paste error]"
5. The AI will help diagnose and fix the issue

Docs: https://www.botpress.com/docs/adk/quickstart
GitHub: https://raw.githubusercontent.com/botpress/skills/master/skills/adk/references/cli.md

### Project Structure

Critical rule: File location determines behavior. Place components in the correct src/ subdirectory or they won't be discovered.

my-bot/
├── agent.config.ts    # Bot configuration: name, models, state schemas, integrations
├── agent.json         # Workspace/bot IDs (auto-generated by adk link/dev, add to .gitignore)
├── package.json       # Node.js dependencies and scripts (dev, build, deploy)
├── tsconfig.json      # TypeScript configuration
├── .env               # API keys and secrets (never commit!)
├── .gitignore         # Should include: agent.json, .env, node_modules/, .botpress/
├── src/
│   ├── conversations/ # Handle incoming messages → use execute() for AI responses
│   ├── workflows/     # Background processes → use step() for resumable operations
│   ├── actions/       # Reusable functions → call from anywhere with actions.name()
│   ├── tools/         # AI-callable functions → AI decides when to invoke these
│   ├── tables/        # Data storage → auto-synced to cloud, supports semantic search
│   ├── triggers/      # Event handlers → react to user.created, integration events, etc.
│   └── knowledge/     # RAG sources → index docs, websites, or tables for AI context
└── .botpress/         # Auto-generated types (never edit manually)

Key Configuration Files:

agent.config.ts - Primary configuration defining bot metadata, AI models, state schemas, and integrations (you edit this)
agent.json - Links agent to workspace/bot IDs. Auto-generated by adk link or adk dev. Add to .gitignore - contains environment-specific IDs that differ per developer
package.json - Node.js config with @botpress/runtime dependency and scripts for dev, build, deploy
tsconfig.json - TypeScript configuration for the project
.env - Environment variables for API keys and secrets (never commit!)
.gitignore - Should include: agent.json, .env, node_modules/, .botpress/

Docs: https://www.botpress.com/docs/adk/project-structure
GitHub: https://raw.githubusercontent.com/botpress/skills/master/skills/adk/references/agent-config.md

### Agent Configuration

The agent.config.ts file defines your bot's identity, AI models, state schemas, and integrations. Always start here when setting up a new bot.

import { defineConfig, z } from "@botpress/runtime";

export default defineConfig({
  name: "my-support-bot",
  description: "AI customer support assistant",

  // AI models for different operations
  defaultModels: {
    autonomous: "openai:gpt-4o",      // Used by execute() for conversations
    zai: "openai:gpt-4o-mini"         // Used by zai operations (cheaper, faster)
  },

  // Global bot state - shared across all conversations and users
  bot: {
    state: z.object({
      maintenanceMode: z.boolean().default(false),
      totalConversations: z.number().default(0)
    })
  },

  // Per-user state - persists across all conversations for each user
  user: {
    state: z.object({
      name: z.string().optional(),
      tier: z.enum(["free", "pro"]).default("free"),
      preferredLanguage: z.enum(["en", "es", "fr"]).default("en")
    }),
    tags: {
      source: z.string(),
      region: z.string().optional()
    }
  },

  // Per-conversation state
  conversation: {
    state: z.object({
      context: z.string().optional()
    }),
    tags: {
      category: z.enum(["support", "sales", "general"]),
      priority: z.enum(["low", "medium", "high"]).optional()
    }
  },

  // Integrations your bot uses (ADK 1.9+ format)
  dependencies: {
    integrations: {
      chat: { version: "chat@0.7.3", enabled: true },
      slack: { version: "slack@2.5.5", enabled: true }
    }
  }
});

Available models:

OpenAI: openai:gpt-4o, openai:gpt-4o-mini, openai:gpt-4-turbo
Anthropic: anthropic:claude-3-5-sonnet, anthropic:claude-3-opus
Google: google:gemini-1.5-pro, google:gemini-1.5-flash

Docs: https://www.botpress.com/docs/adk/project-structure
GitHub: https://raw.githubusercontent.com/botpress/skills/master/skills/adk/references/agent-config.md

### 1. Actions - Reusable Business Logic

When to create an Action:

You need reusable logic that will be called from multiple places (workflows, conversations, triggers)
You're wrapping an external API or database operation
You want testable, composable business logic
You need to call integration APIs (Slack, Linear, etc.) with custom logic

When NOT to use an Action (use a Tool instead):

You want the AI to decide when to call it autonomously
The function should be available during execute()

Actions are not directly callable by the AI - convert them to tools with .asTool() if the AI needs to use them.

Location: src/actions/*.ts

import { Action, z } from "@botpress/runtime";

export const fetchUser = new Action({
  name: "fetchUser",
  description: "Retrieves user details from the database",

  // Define input/output with Zod schemas for type safety
  input: z.object({ userId: z.string() }),
  output: z.object({ name: z.string(), email: z.string() }),

  // IMPORTANT: Handler receives { input, client } - destructure input INSIDE the handler
  async handler({ input, client }) {
    const { user } = await client.getUser({ id: input.userId });
    return { name: user.name, email: user.tags.email };
  }
});

Calling actions:

import { actions } from "@botpress/runtime";
const userData = await actions.fetchUser({ userId: "123" });

// To make an action callable by the AI, convert it to a tool:
tools: [actions.fetchUser.asTool()]

Key Rules:

Handler receives { input, client } - must destructure input inside the handler
Cannot destructure input fields directly in parameters
Can call other actions, integration actions, access state
Can be converted to tools with .asTool()

Docs: https://www.botpress.com/docs/adk/concepts/actions
GitHub: https://raw.githubusercontent.com/botpress/skills/master/skills/adk/references/actions.md

### 2. Tools - AI-Callable Functions

When to create a Tool:

You want the AI to autonomously decide when to use this function
The function retrieves information the AI needs (search, lookup, fetch)
The function performs actions on behalf of the user (create ticket, send message)
You're building capabilities the AI should have during conversations

The AI decides when to use tools based on:

The tool's description - Make this clear and specific about WHEN to use it
The input schema's .describe() fields - Help AI understand what parameters mean
The conversation context and user's intent

Key difference from Actions: Tools can destructure input directly; Actions cannot.

Location: src/tools/*.ts

import { Autonomous, z } from "@botpress/runtime";

export const searchProducts = new Autonomous.Tool({
  name: "searchProducts",
  // This description is critical - it tells the AI when to use this tool
  description: "Search the product catalog. Use when user asks about products, availability, pricing, or wants to browse items.",

  input: z.object({
    query: z.string().describe("Search keywords"),
    category: z.string().optional().describe("Filter by category")
  }),
  output: z.object({
    products: z.array(z.object({ id: z.string(), name: z.string(), price: z.number() }))
  }),

  // Unlike actions, tools CAN destructure input directly in the handler
  handler: async ({ query, category }) => {
    // Your search logic here
    return { products: [] };
  }
});

Using ThinkSignal: When a tool can't complete but you want to give the AI context:

import { Autonomous } from "@botpress/runtime";

// Inside handler - AI will see this message and can respond appropriately
throw new Autonomous.ThinkSignal(
  "No results found",
  "No products found matching that query. Ask user to try different search terms."
);

Advanced Tool Properties:

export const myTool = new Autonomous.Tool({
  name: "myTool",
  description: "Tool description",
  input: z.object({...}),
  output: z.object({...}),
  aliases: ["searchDocs", "findDocs"],  // Alternative names
  handler: async (input, ctx) => {
    console.log(\`Call ID: ${ctx.callId}\`);  // Unique call identifier
    // ...
  },
  retry: async ({ attempt, error }) => {
    if (attempt < 3 && error?.code === 'RATE_LIMIT') {
      await new Promise(r => setTimeout(r, 1000 * attempt));
      return true;  // Retry
    }
    return false;  // Don't retry
  }
});

Docs: https://www.botpress.com/docs/adk/concepts/tools
GitHub: https://raw.githubusercontent.com/botpress/skills/master/skills/adk/references/tools.md

### 3. Conversations - Message Handlers

When to create a Conversation:

Every bot needs at least one conversation handler to respond to users
Create separate handlers for different channels if they need different behavior
Use channel: "*" to handle all channels with one handler

Key decisions when building a conversation:

Which channels? - Specify "*" for all, or specific channels like "slack.dm"
What tools does the AI need? - Pass them to execute({ tools: [...] })
What knowledge should ground responses? - Pass to execute({ knowledge: [...] })
What instructions guide the AI? - Define personality, rules, and context

The execute() function is the heart of ADK - it runs autonomous AI logic with your tools and knowledge. Most conversation handlers will call execute().

Location: src/conversations/*.ts

import { Conversation, z } from "@botpress/runtime";

export const Chat = new Conversation({
  // Which channels this handler responds to
  channel: "chat.channel",  // Or "*" for all, or ["slack.dm", "webchat.channel"]

  // Per-conversation state (optional)
  state: z.object({
    messageCount: z.number().default(0)
  }),

  async handler({ message, state, conversation, execute, user }) {
    state.messageCount += 1;

    // Handle commands
    if (message?.payload?.text?.startsWith("/help")) {
      await conversation.send({
        type: "text",
        payload: { text: "Available commands: /help, /status" }
      });
      return;
    }

    // Let the AI handle the response with your tools and knowledge
    await execute({
      // Instructions guide the AI's behavior and personality
      instructions: \`You are a helpful customer support agent for Acme Corp.
        User's name: ${user.state.name || "there"}
        User's tier: ${user.state.tier}
        Be friendly, concise, and always offer to help further.\`,

      // Tools the AI can use during this conversation
      tools: [searchProducts, actions.createTicket.asTool()],

      // Knowledge bases for RAG - AI will search these to ground responses
      knowledge: [DocsKnowledgeBase],

      model: "openai:gpt-4o",
      temperature: 0.7,
      iterations: 10  // Max tool call iterations
    });
  }
});

Handler Context:

message - User's message data
execute - Run autonomous AI logic
conversation - Conversation instance methods (send, startTyping, stopTyping)
state - Mutable state (bot, user, conversation)
client - Botpress API client
type - Event classification (message, workflow_request)

Execute Function Options:

await execute({
  instructions: string | async function,  // Required
  tools: Tool[],                          // AI-callable tools
  knowledge: Knowledge[],                 // Knowledge bases for RAG
  exits: Exit[],                          // Structured exit handlers
  model: string,                          // AI model to use
  temperature: number,                    // 0-1, default 0.7
  iterations: number,                     // Max tool calls, default 10
  hooks: {
    onBeforeTool: async ({ tool, input }) => { ... },
    onAfterTool: async ({ tool, output }) => { ... },
    onTrace: async (trace) => { ... }
  }
});

Common channels: chat.channel, webchat.channel, slack.dm, slack.channel, discord.channel, whatsapp.channel, "*" (all)

Docs: https://www.botpress.com/docs/adk/concepts/conversations
GitHub: https://raw.githubusercontent.com/botpress/skills/master/skills/adk/references/conversations.md

### 4. Workflows - Background & Multi-Step Processes

When to create a Workflow:

Operations that take longer than 2 minutes (the default timeout)
Multi-step processes that need to survive crashes/restarts
Scheduled/recurring tasks (daily reports, periodic syncs)
Background processing (order fulfillment, data migration)
Operations that need to wait for external events or user input

When NOT to use a Workflow (handle in conversation instead):

Quick operations that complete immediately
Simple request-response patterns
Operations that don't need persistence

Key workflow concepts:

Steps are checkpoints - If workflow crashes, it resumes from last completed step
State persists - Store progress in state to track across steps
Always pass conversationId - If the workflow needs to message users back

Location: src/workflows/*.ts

import { Workflow, z } from "@botpress/runtime";

export const ProcessOrderWorkflow = new Workflow({
  name: "processOrder",
  description: "Processes customer orders",
  timeout: "6h",                    // Max duration
  schedule: "0 9 * * *",            // Optional: run daily at 9am (cron syntax)

  input: z.object({
    orderId: z.string(),
    conversationId: z.string()      // Include this to message the user back!
  }),

  state: z.object({
    currentStep: z.number().default(0),
    processedItems: z.array(z.string()).default([])
  }),

  output: z.object({
    success: z.boolean(),
    itemsProcessed: z.number()
  }),

  async handler({ input, state, step, client, execute }) {
    // State is passed as parameter, auto-tracked
    state.currentStep = 1;

    // IMPORTANT: Each step needs a unique, stable name (no dynamic names!)
    const orderData = await step("fetch-order", async () => {
      return await fetchOrderData(input.orderId);
    });

    // Steps can have retry logic
    await step("process-payment", async () => {
      return await processPayment(orderData);
    }, { maxAttempts: 3 });

    // To message the user from a workflow, use client.createMessage (NOT conversation.send)
    await step("notify-user", async () => {
      await client.createMessage({
        conversationId: input.conversationId,
        type: "text",
        payload: { text: "Your order has been processed!" }
      });
    });

    return {
      success: true,
      itemsProcessed: state.processedItems.length
    };
  }
});

// Start a workflow from a conversation or trigger
await ProcessOrderWorkflow.start({
  orderId: "123",
  conversationId: conversation.id  // Always pass this if you need to message back
});

// Get or create with deduplication
const instance = await ProcessOrderWorkflow.getOrCreate({
  key: \`order-${orderId}\`,  // Prevents duplicate workflows
  input: { orderId, conversationId }
});

Step Methods:

MethodPurposestep(name, fn)Basic execution with cachingstep.sleep(name, ms)Pause for millisecondsstep.sleepUntil(name, date)Pause until specific datestep.listen()Wait for external eventsstep.progress(msg)Update progress messagestep.request(name, prompt)Request user input (blocking)step.executeWorkflow()Start and await another workflowstep.waitForWorkflow(id)Wait for existing workflowstep.map(items, fn)Process array with concurrencystep.forEach(items, fn)Execute on items without resultsstep.batch(items, fn)Process in groupsstep.fail(reason)Mark workflow as failedstep.abort()Stop immediately without failure

Critical Rules:

Step names must be unique and stable (avoid dynamic naming in loops)
State is passed as a parameter, not accessed via this.state
Always pass conversationId for workflows that need to message users
Default timeout is 2 minutes - use steps for longer processes

Docs: https://www.botpress.com/docs/adk/concepts/workflows/overview
GitHub: https://raw.githubusercontent.com/botpress/skills/master/skills/adk/references/workflows.md

### 5. Tables - Data Storage

When to create a Table:

You need to persist structured data (users, orders, tickets, logs)
You want to query/filter data by fields
You need semantic search on text content (set searchable: true)
You're storing data that should survive bot restarts

When NOT to use a Table (use State instead):

Simple key-value data per user/conversation → use user.state or conversation.state
Temporary data that doesn't need persistence
Small amounts of data that fit in state

Tables vs Knowledge Bases:

Tables = Structured data you CRUD (create, read, update, delete)
Knowledge Bases = Documents/content for AI to search and reference

Location: src/tables/*.ts

CRITICAL RULES (violations will cause errors):

Do NOT define an id column - it's created automatically as a number
Table names MUST end with "Table" (e.g., OrdersTable, not Orders)

import { Table, z } from "@botpress/runtime";

export const OrdersTable = new Table({
  name: "OrdersTable",  // Must end with "Table"
  description: "Stores order information",
  columns: {
    // NO id column - it's automatic!
    orderId: z.string(),
    userId: z.string(),
    status: z.enum(["pending", "completed", "cancelled"]),
    total: z.number(),
    createdAt: z.date(),
    // Enable semantic search on a column:
    notes: {
      schema: z.string(),
      searchable: true
    }
  }
});

CRUD operations:

// Create - id is auto-assigned
await OrdersTable.createRows({
  rows: [{ orderId: "ord-123", userId: "user-456", status: "pending", total: 99.99, createdAt: new Date() }]
});

// Read with filters
const { rows } = await OrdersTable.findRows({
  filter: { userId: "user-456", status: "pending" },
  orderBy: "createdAt",
  orderDirection: "desc",
  limit: 10
});

// Get single row by id
const row = await OrdersTable.getRow({ id: 123 });

// Semantic search (on searchable columns)
const { rows } = await OrdersTable.findRows({
  search: "delivery issue",
  limit: 5
});

// Update - must include the id
await OrdersTable.updateRows({
  rows: [{ id: 1, status: "completed" }]
});

// Upsert - insert or update based on key column
await OrdersTable.upsertRows({
  rows: [{ orderId: "ord-123", status: "shipped" }],
  keyColumn: "orderId"
});

// Delete by filter
await OrdersTable.deleteRows({ status: "cancelled" });

// Delete by IDs
await OrdersTable.deleteRowIds([123, 456]);

Advanced: Computed Columns:

columns: {
  basePrice: z.number(),
  taxRate: z.number(),
  fullPrice: {
    computed: true,
    schema: z.number(),
    dependencies: ["basePrice", "taxRate"],
    value: async (row) => row.basePrice * (1 + row.taxRate)
  }
}

Docs: https://www.botpress.com/docs/adk/concepts/tables
GitHub: https://raw.githubusercontent.com/botpress/skills/master/skills/adk/references/tables.md

### 6. Knowledge Bases - RAG for AI Context

When to create a Knowledge Base:

You want the AI to answer questions based on your documentation
You have FAQs, policies, or product info the AI should reference
You want AI responses grounded in specific content (not hallucinated)
You're building a support bot that needs access to help articles

How RAG works in ADK:

You define knowledge sources (websites, files, tables)
Content is indexed and embedded for semantic search
During execute(), the AI automatically searches relevant knowledge
AI uses retrieved content to generate grounded responses

Choosing a DataSource type:

Website - Index public documentation, help sites, blogs
Directory - Index local markdown/text files (dev only!)
Table - Index structured data from your tables

Location: src/knowledge/*.ts

import { Knowledge, DataSource } from "@botpress/runtime";

// Website source - index via sitemap
const websiteSource = DataSource.Website.fromSitemap(
  "https://docs.example.com/sitemap.xml",
  {
    id: "website-docs",
    maxPages: 500,
    maxDepth: 10,
    filter: (ctx) => ctx.url.includes("/docs/")  // Only index /docs/ pages
  }
);

// Local files (development only - won't work in production)
const localSource = DataSource.Directory.fromPath("src/knowledge/docs", {
  id: "local-docs",
  filter: (path) => path.endsWith(".md")
});

// Table-based knowledge
const tableSource = DataSource.Table.fromTable(FAQTable, {
  id: "faq-table",
  transform: ({ row }) => \`Question: ${row.question}\\nAnswer: ${row.answer}\`,
  filter: ({ row }) => row.published === true
});

export const DocsKB = new Knowledge({
  name: "docsKB",
  description: "Product documentation and help articles",
  sources: [websiteSource, localSource, tableSource]
});

// Use in conversations - AI will search this knowledge base
await execute({
  instructions: "Answer based on the documentation",
  knowledge: [DocsKB]
});

// Manually refresh knowledge base
await DocsKB.refresh();                  // Smart refresh (only changed content)
await DocsKB.refresh({ force: true });   // Force full re-index
await DocsKB.refreshSource("website-docs", { force: true });  // Refresh specific source

Website Source Methods:

fromSitemap(url, options) - Parse XML sitemap
fromWebsite(baseUrl, options) - Crawl from base URL (requires Browser integration)
fromLlmsTxt(url, options) - Parse llms.txt file
fromUrls(urls, options) - Index specific URLs

Docs: https://www.botpress.com/docs/adk/concepts/knowledge
GitHub: https://raw.githubusercontent.com/botpress/skills/master/skills/adk/references/knowledge-bases.md

### 7. Triggers - Event-Driven Automation

When to create a Trigger:

You need to react to events automatically (user signs up, issue created, etc.)
You want to start workflows when specific events occur
You need to sync data when external systems change
You want to send notifications based on events

Common trigger patterns:

User onboarding - Trigger on user.created → start onboarding workflow
Integration sync - Trigger on linear:issueCreated → create record in table
Notifications - Trigger on workflow.completed → send Slack message

Finding available events:

Bot events: user.created, conversation.started, workflow.completed, etc.
Integration events: Run adk info <integration> --events to see available events

Location: src/triggers/*.ts

import { Trigger } from "@botpress/runtime";

export default new Trigger({
  name: "onNewUser",
  description: "Start onboarding when user created",
  events: ["user.created"],  // Can listen to multiple events

  handler: async ({ event, client, actions }) => {
    const { userId, email } = event.payload;

    // Start an onboarding workflow
    await OnboardingWorkflow.start({
      userId,
      email
    });
  }
});

// Integration events use format: integration:eventName
export const LinearTrigger = new Trigger({
  name: "onLinearIssue",
  description: "Handle Linear issue events",
  events: ["linear:issueCreated", "linear:issueUpdated"],

  handler: async ({ event, actions }) => {
    if (event.type === "linear:issueCreated") {
      await actions.slack.sendMessage({
        channel: "#notifications",
        text: \`New issue: ${event.payload.title}\`
      });
    }
  }
});

Common Bot Events:

user.created, user.updated, user.deleted
conversation.started, conversation.ended, message.created
workflow.started, workflow.completed, workflow.failed
bot.started, bot.stopped

Common Integration Events:

Slack: slack:reactionAdded, slack:memberJoinedChannel
Linear: linear:issueCreated, linear:issueUpdated
GitHub: github:issueOpened, github:pullRequestOpened
Intercom: intercom:conversationEvent, intercom:contactEvent

Find integration events: Run adk info <integration> --events

Docs: https://www.botpress.com/docs/adk/concepts/triggers
GitHub: https://raw.githubusercontent.com/botpress/skills/master/skills/adk/references/triggers.md

### Sending Messages

CRITICAL: The method depends on WHERE you're sending from:

ContextMethodWhyIn Conversationsconversation.send()Has conversation contextIn Workflows/Actionsclient.createMessage()Needs explicit conversationId

Common mistake: Using client.createMessage() in conversations. Always use conversation.send() instead.

The method depends on where you're sending from:

In conversations - Use conversation.send():

await conversation.send({ type: "text", payload: { text: "Hello!" } });
await conversation.send({ type: "image", payload: { imageUrl: "https://..." } });
await conversation.send({
  type: "choice",
  payload: {
    text: "Pick one:",
    choices: [
      { title: "Option A", value: "a" },
      { title: "Option B", value: "b" }
    ]
  }
});

In workflows or actions - Use client.createMessage() with conversationId:

await client.createMessage({
  conversationId: input.conversationId,  // Must have this!
  type: "text",
  payload: { text: "Workflow complete!" }
});

All Message Types:

// Text
{ type: "text", payload: { text: "Hello!" } }

// Markdown
{ type: "markdown", payload: { text: "# Heading\\n**Bold**" } }

// Image
{ type: "image", payload: { imageUrl: "https://..." } }

// Audio
{ type: "audio", payload: { audioUrl: "https://..." } }

// Video
{ type: "video", payload: { videoUrl: "https://..." } }

// File
{ type: "file", payload: { fileUrl: "https://...", title: "Document.pdf" } }

// Location
{ type: "location", payload: { latitude: 40.7128, longitude: -74.0060, address: "New York, NY" } }

// Card
{ type: "card", payload: {
  title: "Product Name",
  subtitle: "Description",
  imageUrl: "https://...",
  actions: [
    { action: "url", label: "View", value: "https://..." },
    { action: "postback", label: "Buy", value: "buy_123" }
  ]
}}

// Carousel
{ type: "carousel", payload: {
  items: [
    { title: "Item 1", subtitle: "...", imageUrl: "...", actions: [...] },
    { title: "Item 2", subtitle: "...", imageUrl: "...", actions: [...] }
  ]
}}

// Choice (Quick Replies)
{ type: "choice", payload: {
  text: "Select an option:",
  choices: [
    { title: "Option 1", value: "opt1" },
    { title: "Option 2", value: "opt2" }
  ]
}}

// Dropdown
{ type: "dropdown", payload: {
  text: "Select country:",
  options: [
    { label: "United States", value: "us" },
    { label: "Canada", value: "ca" }
  ]
}}

GitHub: https://raw.githubusercontent.com/botpress/skills/master/skills/adk/references/messages.md

### Zai - LLM Utility Operations

When to use Zai vs execute():

Use zai for specific, structured AI operations (extract data, classify, summarize)
Use execute() for autonomous, multi-turn AI conversations with tools

Zai is perfect for:

Extracting structured data from user messages (zai.extract)
Classifying/labeling content (zai.check, zai.label)
Summarizing long content (zai.summarize)
Answering questions from documents (zai.answer)
Sorting/filtering/grouping data intelligently (zai.sort, zai.filter, zai.group)

Zai operations are optimized for speed and cost - they use the zai model configured in agent.config.ts (typically a faster/cheaper model).

import { adk, z } from "@botpress/runtime";

// Extract structured data from text
const contact = await adk.zai.extract(
  "Contact John at john@example.com, phone 555-0100",
  z.object({
    name: z.string(),
    email: z.string(),
    phone: z.string()
  })
);
// Returns: { name: "John", email: "john@example.com", phone: "555-0100" }

// Check if text matches a condition (returns boolean)
const isSpam = await adk.zai.check(messageText, "is spam or promotional");

// Label text with multiple criteria
const labels = await adk.zai.label(customerEmail, {
  spam: "is spam",
  urgent: "needs immediate response",
  complaint: "expresses dissatisfaction"
});
// Returns: { spam: false, urgent: true, complaint: true }

// Summarize content
const summary = await adk.zai.summarize(longDocument, {
  length: 200,
  bulletPoints: true
});

// Answer questions from documents (with citations)
const result = await adk.zai.answer(docs, "What is the refund policy?");
if (result.type === "answer") {
  console.log(result.answer);
  console.log(result.citations);
}
// Response types: "answer", "ambiguous", "out_of_topic", "invalid_question", "missing_knowledge"

// Rate items on 1-5 scale
const scores = await adk.zai.rate(products, "quality score");

// Sort by criteria
const sorted = await adk.zai.sort(tickets, "by urgency, most urgent first");

// Group items semantically
const groups = await adk.zai.group(emails, {
  instructions: "categorize by topic"
});

// Rewrite text
const professional = await adk.zai.rewrite("hey wassup", "make it professional and friendly");

// Filter arrays
const activeUsers = await adk.zai.filter(users, "have been active this month");

// Generate text
const blogPost = await adk.zai.text("Write about AI in healthcare", {
  length: 1000,
  temperature: 0.7
});

// Patch code files
const patched = await adk.zai.patch(files, "add JSDoc comments to all functions");

Zai Configuration:

// Create configured instance
const preciseZai = adk.zai.with({
  modelId: "best",        // "best" | "fast" | custom model ID
  temperature: 0.1
});

// Enable active learning
const learningZai = adk.zai.learn("sentiment-analysis");

Docs: https://www.botpress.com/docs/adk/zai/overview
GitHub: https://raw.githubusercontent.com/botpress/skills/master/skills/adk/references/zai-complete-guide.md

### Integrations

When to add an Integration:

You need to connect to an external service (Slack, Linear, GitHub, etc.)
You want to receive messages from a channel (webchat, WhatsApp, Discord)
You need to call external APIs with pre-built actions
You want to react to events from external systems

Integration workflow:

Search - Find integrations with adk search <name>
Add - Install with adk add <name>@<version>
Configure - Set up credentials in the UI at http://localhost:3001/
Use - Call actions via actions.<integration>.<action>()

Making integration actions available to AI:

// Convert any integration action to an AI-callable tool
tools: [actions.slack.sendMessage.asTool()]

CLI commands:

adk search slack           # Find integrations
adk add slack@latest       # Add to project
adk add slack --alias my-slack  # Add with custom alias
adk info slack --events    # See available events
adk list                   # List installed integrations
adk upgrade slack          # Update to latest
adk remove slack           # Remove integration

Using integration actions:

import { actions } from "@botpress/runtime";

// Slack
await actions.slack.sendMessage({ channel: "#general", text: "Hello!" });
await actions.slack.addReaction({ channel: "C123", timestamp: "123", name: "thumbsup" });

// Linear
await actions.linear.issueCreate({ teamId: "123", title: "Bug report", description: "Details" });
const { items } = await actions.linear.issueList({
  first: 10,
  filter: { state: { name: { eq: "In Progress" } } }
});

// GitHub
await actions.github.createIssue({ owner: "org", repo: "repo", title: "Issue" });

// Browser (web scraping)
const results = await actions.browser.webSearch({ query: "Botpress docs", maxResults: 5 });

// Make integration actions available to AI as tools
await execute({ tools: [actions.slack.sendMessage.asTool()] });

Docs: https://www.botpress.com/docs/adk/managing-integrations
GitHub: https://raw.githubusercontent.com/botpress/skills/master/skills/adk/references/integration-actions.md

### State Management

Understanding the state hierarchy - choose the right level:

State LevelScopeUse Forbot.stateGlobal, all usersFeature flags, counters, maintenance modeuser.statePer user, all their conversationsUser preferences, profile, tierconversation.statePer conversationContext, message count, active workflowworkflow.statePer workflow instanceProgress tracking, intermediate results

State is automatically persisted - just modify it and it saves.

Access and modify state from anywhere in your bot:

import { bot, user, conversation } from "@botpress/runtime";

// Bot state - global, shared across all users
bot.state.maintenanceMode = true;
bot.state.totalConversations += 1;

// User state - per user, persists across conversations
user.state.name = "Alice";
user.state.tier = "pro";
user.state.preferredLanguage = "es";

// In handlers, state is passed as a parameter
async handler({ state }) {
  state.messageCount += 1;  // Auto-persisted
}

// Tags - simple string key-value pairs for categorization
user.tags.source = "website";
user.tags.region = "north-america";
conversation.tags.category = "support";
conversation.tags.priority = "high";

State Types:

Bot State - Global, shared across all users and conversations
User State - Per-user, persists across all their conversations
Conversation State - Per-conversation, isolated between conversations
Workflow State - Per-workflow instance, persists across steps

Tags vs State:

Use Tags for: categorization, simple strings, filtering/querying
Use State for: complex objects, arrays, nested data, business logic

GitHub: https://raw.githubusercontent.com/botpress/skills/master/skills/adk/references/tags.md

### Context API

Access runtime services in any handler:

import { context } from "@botpress/runtime";

// Always available
const client = context.get("client");           // Botpress API client
const citations = context.get("citations");     // Citation manager
const cognitive = context.get("cognitive");     // LLM client
const logger = context.get("logger");           // Structured logger
const botId = context.get("botId");            // Current bot ID
const configuration = context.get("configuration");  // Bot config

// Conditionally available (use { optional: true })
const user = context.get("user", { optional: true });
const conversation = context.get("conversation", { optional: true });
const message = context.get("message", { optional: true });
const workflow = context.get("workflow", { optional: true });
const chat = context.get("chat", { optional: true });  // Conversation transcript

if (user) {
  console.log(\`User: ${user.id}\`);
}

GitHub: https://raw.githubusercontent.com/botpress/skills/master/skills/adk/references/context-api.md

### CLI Quick Reference

# Project Lifecycle
adk init <name>              # Create new project
adk login                    # Authenticate with Botpress
adk dev                      # Start dev server (hot reload)
adk dev --port 3000          # Custom port
adk chat                     # Test in CLI
adk build                    # Build for production
adk deploy                   # Deploy to Botpress Cloud
adk deploy --env production  # Deploy to specific environment

# Integration Management
adk add <integration>        # Add integration
adk add slack@2.5.5          # Add specific version
adk add slack --alias my-slack  # Add with alias
adk remove <integration>     # Remove integration
adk search <query>           # Search integrations
adk list                     # List installed integrations
adk list --available         # List all available
adk info <name>              # Integration details
adk info <name> --events     # Show available events
adk upgrade <name>           # Update integration
adk upgrade                  # Interactive upgrade all

# Knowledge & Assets
adk kb sync --dev            # Sync knowledge bases
adk kb sync --prod --force   # Force re-sync production
adk assets sync              # Sync static files

# Advanced
adk run <script.ts>          # Run TypeScript script
adk mcp                      # Start MCP server
adk link --workspace ws_123 --bot bot_456  # Link to existing bot

# Utilities
adk self-upgrade             # Update CLI
adk telemetry --disable      # Disable telemetry
adk --help                   # Full CLI help
adk <command> --help         # Help for specific command

Docs: https://www.botpress.com/docs/adk/cli-reference
GitHub: https://raw.githubusercontent.com/botpress/skills/master/skills/adk/references/cli.md

### Autonomous Execution with execute()

The execute() function is the core of ADK's AI capabilities. It runs an autonomous AI agent that can:

Understand user intent from natural language
Decide which tools to call and when
Search knowledge bases for relevant information
Generate contextual responses
Loop through multiple tool calls until the task is complete

When to use execute():

In conversation handlers to generate AI responses
In workflows when you need AI decision-making
Anywhere you want autonomous, multi-step AI behavior

Key parameters to configure:

instructions - Tell the AI who it is and how to behave
tools - Give the AI capabilities (search, create, update, etc.)
knowledge - Ground the AI in your documentation
exits - Define structured output schemas for specific outcomes

The execute() function enables autonomous AI agent behavior:

import { Autonomous, z } from "@botpress/runtime";

// Define custom tool
const searchTool = new Autonomous.Tool({
  name: "search",
  description: "Search documentation",
  input: z.object({ query: z.string() }),
  output: z.string(),
  handler: async ({ query }) => {
    // Search implementation
    return "results...";
  }
});

// Define exit (structured response)
const AnswerExit = new Autonomous.Exit({
  name: "Answer",
  description: "Provide final answer to the user",
  schema: z.object({
    answer: z.string(),
    confidence: z.number(),
    sources: z.array(z.string())
  })
});

// Execute AI with tools, knowledge, and exits
const result = await execute({
  instructions: "Help the user with their request. Be helpful and concise.",

  // Add tools
  tools: [
    searchTool,
    actions.linear.issueCreate.asTool()
  ],

  // Add knowledge bases
  knowledge: [DocsKnowledgeBase, FAQKnowledgeBase],

  // Define exits for structured outputs
  exits: [AnswerExit],

  // Model configuration
  model: "openai:gpt-4o",
  temperature: 0.7,
  iterations: 10,  // Max tool call iterations

  // Hooks for monitoring
  hooks: {
    onBeforeTool: async ({ tool, input }) => {
      console.log(\`Calling ${tool.name}\`, input);
      return { input: { ...input, enhanced: true } };  // Modify input
    },
    onAfterTool: async ({ tool, output }) => {
      console.log(\`Result:\`, output);
    }
  }
});

// Handle structured exit
if (result.is(AnswerExit)) {
  console.log(result.output.answer);
  console.log(result.output.sources);
}

### Troubleshooting

ErrorCauseSolution"Cannot destructure property" in ActionsDestructuring input directly in handler paramsUse async handler({ input, client }) then const { field } = input insideTable creation failsInvalid table name or id definedRemove id column, ensure name ends with "Table"Integration action not foundIntegration not installed or configuredRun adk list, add with adk add, configure in UI at localhost:3001Knowledge base not updatingKB not syncedRun adk kb sync --dev or adk kb sync --forceWorkflow not resumingDynamic step namesUse stable, unique step names (no step(\\item-${i}\`)\`)Types out of dateGenerated types staleRun adk dev or adk build to regenerateCan't message user from workflowMissing conversationIdPass conversationId when starting workflow, use client.createMessage()"user is not defined"Accessing conversation context outside conversationUse context.get("user", { optional: true })State changes not persistingCreating new objects instead of modifyingModify state directly: state.user.name = "Alice"Tool not being used by AIPoor descriptionImprove tool description, add detailed .describe() to inputs

For more help: Run adk --help or check:

Docs: https://www.botpress.com/docs/adk/
GitHub: https://github.com/botpress/skills/tree/master/skills/adk/references

### 1. Always Pass conversationId for Workflows

// In conversation - starting a workflow that needs to message back
await MyWorkflow.start({
  conversationId: conversation.id,  // Always include this!
  data: "..."
});

// In workflow - messaging back to user
await client.createMessage({
  conversationId: input.conversationId,
  type: "text",
  payload: { text: "Processing complete!" }
});

### 2. Use Environment Variables for Secrets

// In .env (never commit!)
API_KEY=sk-...
SLACK_TOKEN=xoxb-...

// In code
config: { apiKey: process.env.API_KEY }

### 3. Keep Step Names Stable in Workflows

// GOOD - Single step for batch
await step("process-all-items", async () => {
  for (const item of items) {
    await processItem(item);
  }
});

// BAD - Dynamic names break resume
for (let i = 0; i < items.length; i++) {
  await step(\`process-${i}\`, async () => { ... });  // Don't do this!
}

### 4. Error Handling in Actions/Tools

export default new Action({
  handler: async ({ input }) => {
    try {
      // Action logic
      return { success: true };
    } catch (error) {
      console.error("Action failed:", error);
      throw new Error(\`Failed to process: ${error.message}\`);
    }
  }
});

### 5. ThinkSignal for Tool Edge Cases

handler: async ({ query }) => {
  const results = await search(query);

  if (!results.length) {
    throw new Autonomous.ThinkSignal(
      "No results",
      "No results found. Ask the user to try different search terms."
    );
  }

  return results;
}

### 6. Multi-Channel Handling

export default new Conversation({
  channels: ["slack.channel", "webchat.channel"],
  handler: async ({ conversation }) => {
    const channel = conversation.channel;

    if (channel === "slack.channel") {
      // Slack-specific handling (threading, mentions, etc.)
    } else if (channel === "webchat.channel") {
      // Webchat-specific handling
    }
  }
});

### Official Botpress ADK Documentation

Base URL: https://www.botpress.com/docs/adk/

TopicURLIntroductionhttps://www.botpress.com/docs/adk/introductionQuickstarthttps://www.botpress.com/docs/adk/quickstartProject Structurehttps://www.botpress.com/docs/adk/project-structureActionshttps://www.botpress.com/docs/adk/concepts/actionsToolshttps://www.botpress.com/docs/adk/concepts/toolsConversationshttps://www.botpress.com/docs/adk/concepts/conversationsWorkflows Overviewhttps://www.botpress.com/docs/adk/concepts/workflows/overviewWorkflow Stepshttps://www.botpress.com/docs/adk/concepts/workflows/stepsTableshttps://www.botpress.com/docs/adk/concepts/tablesTriggershttps://www.botpress.com/docs/adk/concepts/triggersKnowledge Baseshttps://www.botpress.com/docs/adk/concepts/knowledgeManaging Integrationshttps://www.botpress.com/docs/adk/managing-integrationsZai Overviewhttps://www.botpress.com/docs/adk/zai/overviewZai Referencehttps://www.botpress.com/docs/adk/zai/referenceCLI Referencehttps://www.botpress.com/docs/adk/cli-reference

### GitHub Repository References (AI-Optimized)

Base URL: https://github.com/botpress/skills/tree/master/skills/adk/references

For detailed specifications beyond this guide, fetch the corresponding reference file:

TopicReference FileActionshttps://raw.githubusercontent.com/botpress/skills/master/skills/adk/references/actions.mdToolshttps://raw.githubusercontent.com/botpress/skills/master/skills/adk/references/tools.mdWorkflowshttps://raw.githubusercontent.com/botpress/skills/master/skills/adk/references/workflows.mdConversationshttps://raw.githubusercontent.com/botpress/skills/master/skills/adk/references/conversations.mdTableshttps://raw.githubusercontent.com/botpress/skills/master/skills/adk/references/tables.mdTriggershttps://raw.githubusercontent.com/botpress/skills/master/skills/adk/references/triggers.mdKnowledge Baseshttps://raw.githubusercontent.com/botpress/skills/master/skills/adk/references/knowledge-bases.mdMessageshttps://raw.githubusercontent.com/botpress/skills/master/skills/adk/references/messages.mdAgent Confighttps://raw.githubusercontent.com/botpress/skills/master/skills/adk/references/agent-config.mdCLIhttps://raw.githubusercontent.com/botpress/skills/master/skills/adk/references/cli.mdIntegration Actionshttps://raw.githubusercontent.com/botpress/skills/master/skills/adk/references/integration-actions.mdModel Configurationhttps://raw.githubusercontent.com/botpress/skills/master/skills/adk/references/model-configuration.mdContext APIhttps://raw.githubusercontent.com/botpress/skills/master/skills/adk/references/context-api.mdTagshttps://raw.githubusercontent.com/botpress/skills/master/skills/adk/references/tags.mdFileshttps://raw.githubusercontent.com/botpress/skills/master/skills/adk/references/files.mdZai Complete Guidehttps://raw.githubusercontent.com/botpress/skills/master/skills/adk/references/zai-complete-guide.mdZai Agent Referencehttps://raw.githubusercontent.com/botpress/skills/master/skills/adk/references/zai-agent-reference.mdMCP Serverhttps://raw.githubusercontent.com/botpress/skills/master/skills/adk/references/mcp-server.md

### Common Scenarios - What to Build

"I want to build a support bot that answers questions from our docs"

Create a Knowledge Base with your documentation as a source
Create a Conversation handler that uses execute() with that knowledge
Add the chat integration for testing

"I want the bot to create tickets in Linear when users report issues"

Add the Linear integration: adk add linear
Create a Tool that calls actions.linear.issueCreate()
Pass the tool to execute() in your conversation

"I need to run a daily sync job"

Create a Workflow with schedule: "0 9 * * *" (cron syntax)
Implement the sync logic in steps
The workflow will run automatically at the scheduled time

"I want to store user preferences"

Define the schema in agent.config.ts under user.state
Access/modify via user.state.preferenceField = value
State persists automatically

"I need to react when a new user signs up"

Create a Trigger listening to user.created event
In the handler, start an onboarding workflow or send a welcome message

"I want to store order data and search it"

Create a Table with your schema (remember: no id field, name ends with "Table")
Use searchable: true on text columns you want to search
Use CRUD methods: createRows, findRows, updateRows, deleteRows

### Summary

This skill provides comprehensive guidance for building Botpress bots using the ADK:

Setup & Initialization - ADK installation and project creation
Project Structure - Conventions, files, and organization
Core Concepts - Actions, Tools, Workflows, Conversations, Tables, Knowledge, Triggers
State Management - Bot, user, conversation, and workflow state
Integration Management - Adding and configuring integrations
Zai (AI Operations) - Extract, check, label, summarize, answer, sort, group, rewrite, filter
CLI Reference - Complete command guide
Testing & Deployment - Local testing and cloud deployment
Common Patterns - Best practices and troubleshooting

Core Principle: The ADK is a convention-based framework where file location determines behavior. Place components in the correct src/ subdirectory and they automatically become bot capabilities.

When to use this skill:

User wants to create a new Botpress bot
User asks how to add actions, tools, workflows, conversations, tables, knowledge bases, or triggers
User needs help with integrations (Slack, Linear, GitHub, etc.)
User wants to understand ADK patterns and best practices
User has errors or needs troubleshooting
User asks about CLI commands, configuration, or deployment

Official Documentation: https://www.botpress.com/docs/adk/
GitHub Repository: https://github.com/botpress/adk
Skills Repository: https://github.com/botpress/skills
## Trust
- Source: tencent
- Verification: Indexed source record
- Publisher: yueranlu
- Version: 1.0.1
## Source health
- Status: healthy
- Item download looks usable.
- Yavira can redirect you to the upstream package for this item.
- Health scope: item
- Reason: direct_download_ok
- Checked at: 2026-04-30T22:19:54.502Z
- Expires at: 2026-05-07T22:19:54.502Z
- Recommended action: Download for OpenClaw
## Links
- [Detail page](https://openagent3.xyz/skills/botpress-adk)
- [Send to Agent page](https://openagent3.xyz/skills/botpress-adk/agent)
- [JSON manifest](https://openagent3.xyz/skills/botpress-adk/agent.json)
- [Markdown brief](https://openagent3.xyz/skills/botpress-adk/agent.md)
- [Download page](https://openagent3.xyz/downloads/botpress-adk)