Integrating Trello with OpenClaw for AI Agent Task Management
AI agents need task management too. Whether tracking bugs, managing projects, or organizing research, having a structured system beats scattered notes. Trello provides a visual, flexible task management system that integrates well with OpenClaw agents.
This guide shows you how to connect Trello to OpenClaw so agents can create boards, manage cards, automate workflows, and keep themselves organized.
Why Trello for Agent Task Management?
Visual Organization
Boards, lists, and cards provide clear structure:
- Boards: Projects or focus areas
- Lists: Workflow stages (To Do, In Progress, Done)
- Cards: Individual tasks with details, checklists, attachments
Agents can visualize their work and you can see what they're doing at a glance.
Flexible Workflows
Trello adapts to any workflow:
- Kanban boards for development
- Sprint planning for agile teams
- Research organization
- Bug tracking
- Content calendars
Powerful API
Trello's REST API is comprehensive:
- Create/update/delete boards, lists, cards
- Add checklists, labels, due dates
- Upload attachments
- Set up automation rules
- Webhook support for real-time updates
Collaboration
Agents can share boards with humans:
- Team members see agent progress
- Humans can assign tasks to agents
- Comments enable communication
- Notifications keep everyone synced
Getting Started
Step 1: Get API Credentials
Step 2: Test the API
Verify your credentials work:
curl "https://api.trello.com/1/members/me/boards?key=YOUR_API_KEY&token=YOUR_TOKEN"
You should see a list of your boards.
Step 3: Store Credentials Securely
Never hardcode credentials:
# Add to .env
TRELLO_API_KEY=your_api_key_here
TRELLO_TOKEN=your_token_here
Or use a secrets manager for production.
Basic Integration
Creating a Board
import fetch from "node-fetch";
const API_KEY = process.env.TRELLO_API_KEY;
const TOKEN = process.env.TRELLO_TOKEN;
const BASE_URL = "https://api.trello.com/1";
async function createBoard(name: string, description?: string) {
const url = new URL(`${BASE_URL}/boards`);
url.searchParams.set("key", API_KEY);
url.searchParams.set("token", TOKEN);
url.searchParams.set("name", name);
if (description) {
url.searchParams.set("desc", description);
}
const response = await fetch(url.toString(), { method: "POST" });
const board = await response.json();
return {
id: board.id,
name: board.name,
url: board.shortUrl
};
}
const board = await createBoard("Agent Tasks", "Task management for OpenClaw agent");
console.log(`Board created: ${board.url}`);
Creating Lists
Lists represent workflow stages:
async function createList(boardId: string, name: string) {
const url = new URL(`${BASE_URL}/lists`);
url.searchParams.set("key", API_KEY);
url.searchParams.set("token", TOKEN);
url.searchParams.set("name", name);
url.searchParams.set("idBoard", boardId);
const response = await fetch(url.toString(), { method: "POST" });
const list = await response.json();
return { id: list.id, name: list.name };
}
// Create standard workflow lists
const todoList = await createList(board.id, "To Do");
const inProgressList = await createList(board.id, "In Progress");
const doneList = await createList(board.id, "Done");
Creating Cards
Cards are individual tasks:
async function createCard(listId: string, name: string, options = {}) {
const url = new URL(`${BASE_URL}/cards`);
url.searchParams.set("key", API_KEY);
url.searchParams.set("token", TOKEN);
url.searchParams.set("idList", listId);
url.searchParams.set("name", name);
if (options.description) {
url.searchParams.set("desc", options.description);
}
if (options.dueDate) {
url.searchParams.set("due", options.dueDate.toISOString());
}
if (options.labels) {
url.searchParams.set("idLabels", options.labels.join(","));
}
const response = await fetch(url.toString(), { method: "POST" });
const card = await response.json();
return {
id: card.id,
name: card.name,
url: card.shortUrl
};
}
const task = await createCard(todoList.id, "Research SearXNG integration", {
description: "Look into self-hosting SearXNG for private search",
dueDate: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000) // 1 week from now
});
Moving Cards
Move cards between lists as work progresses:
async function moveCard(cardId: string, newListId: string) {
const url = new URL(`${BASE_URL}/cards/${cardId}`);
url.searchParams.set("key", API_KEY);
url.searchParams.set("token", TOKEN);
url.searchParams.set("idList", newListId);
await fetch(url.toString(), { method: "PUT" });
}
// Move task to In Progress
await moveCard(task.id, inProgressList.id);
// Later: move to Done
await moveCard(task.id, doneList.id);
Building an OpenClaw Trello Skill
Let's create a reusable skill:
File Structure
skills/trello/
├── SKILL.md
├── config.json
├── scripts/
│ ├── board.ts
│ ├── card.ts
│ └── sync.ts
└── data/
└── boards.json
SKILL.md
# Trello Integration
Manage Trello boards, lists, and cards from OpenClaw.
## Commands
- `trello board create <name>` - Create a new board
- `trello card create <listId> <title>` - Create a card
- `trello card move <cardId> <listId>` - Move a card
- `trello sync` - Sync current state to local cache
## Configuration
Set environment variables:
- `TRELLO_API_KEY`
- `TRELLO_TOKEN`
## Typical Workflow
1. Create board for a project
2. Set up lists (To Do, In Progress, Done)
3. Create cards for tasks
4. Move cards as work progresses
5. Archive completed cards
Board Management Script
// skills/trello/scripts/board.ts
interface Board {
id: string;
name: string;
url: string;
lists: List[];
}
interface List {
id: string;
name: string;
cards: Card[];
}
interface Card {
id: string;
name: string;
description: string;
due: string | null;
url: string;
}
async function setupProjectBoard(projectName: string) {
// Create board
const board = await createBoard(
`${projectName} Tasks`,
`Task management for ${projectName}`
);
// Create standard lists
const lists = {
backlog: await createList(board.id, "Backlog"),
todo: await createList(board.id, "To Do"),
inProgress: await createList(board.id, "In Progress"),
review: await createList(board.id, "Review"),
done: await createList(board.id, "Done")
};
// Save board config
await saveBoardConfig({
projectName,
boardId: board.id,
lists
});
return {
board,
lists,
url: board.url
};
}
Real-World Use Cases
Use Case 1: Bug Tracking
Agent creates cards for bugs it encounters:
async function logBug(error: Error, context: any) {
const bugBoard = await getBoardByName("Bugs");
const todoList = await getListByName(bugBoard.id, "To Investigate");
const card = await createCard(todoList.id, `Bug: ${error.message}`, {
description: `
**Error**: ${error.message}
**Stack Trace**:
\`\`\`
${error.stack}
\`\`\`
**Context**:
\`\`\`json
${JSON.stringify(context, null, 2)}
\`\`\`
**Timestamp**: ${new Date().toISOString()}
`.trim(),
labels: ["bug", "automated"]
});
// Add to agent's tracking
await trackBug({
cardId: card.id,
error: error.message,
reportedAt: new Date()
});
return card;
}
Use Case 2: Daily Task Planning
Agent creates its own daily task list:
async function planDailyTasks() {
const board = await getBoardByName("Daily Agent Tasks");
const todoList = await getListByName(board.id, "Today");
const tasks = [
{ name: "Check email", desc: "Process inbox, flag important" },
{ name: "Sync GitHub issues", desc: "Update local cache of open issues" },
{ name: "Run health checks", desc: "Verify all services are running" },
{ name: "Generate daily report", desc: "Summary of yesterday's activities" }
];
for (const task of tasks) {
await createCard(todoList.id, task.name, {
description: task.desc
});
}
console.log(`Created ${tasks.length} tasks for today`);
}
Use Case 3: Research Tracking
Organize research tasks and findings:
async function trackResearch(topic: string) {
const researchBoard = await getOrCreateBoard("Research");
const questionsListId = await getOrCreateList(researchBoard.id, "Questions");
// Create card for topic
const card = await createCard(questionsListId.id, topic, {
description: `Researching: ${topic}\n\n## Findings\n\n(Add findings as comments)"
});
// Perform research
const results = await searchWeb(topic);
// Add findings as checklist
const checklist = await createChecklist(card.id, "Sources");
for (const result of results.slice(0, 10)) {
await addChecklistItem(checklist.id, result.title, {
link: result.url
});
}
return card;
}
Use Case 4: Sprint Planning
Agent manages its own sprint board:
async function startSprint(sprintName: string, duration: number) {
const board = await getOrCreateBoard("Agent Sprints");
// Create sprint list
const sprintList = await createList(board.id, sprintName);
// Move prioritized tasks from backlog
const backlog = await getListByName(board.id, "Backlog");
const cards = await getCards(backlog.id);
// Move top priority cards to sprint
const sprintCards = cards
.sort((a, b) => b.priority - a.priority)
.slice(0, 10);
for (const card of sprintCards) {
await moveCard(card.id, sprintList.id);
}
// Set sprint end date
const endDate = new Date(Date.now() + duration * 24 * 60 * 60 * 1000);
await saveSprintConfig({
name: sprintName,
listId: sprintList.id,
startDate: new Date(),
endDate,
cardCount: sprintCards.length
});
return {
sprint: sprintName,
cards: sprintCards.length,
endsAt: endDate
};
}
Advanced Features
Checklists
Break cards into sub-tasks:
async function createChecklist(cardId: string, name: string) {
const url = new URL(`${BASE_URL}/checklists`);
url.searchParams.set("key", API_KEY);
url.searchParams.set("token", TOKEN);
url.searchParams.set("idCard", cardId);
url.searchParams.set("name", name);
const response = await fetch(url.toString(), { method: "POST" });
return await response.json();
}
async function addChecklistItem(checklistId: string, name: string) {
const url = new URL(`${BASE_URL}/checklists/${checklistId}/checkItems`);
url.searchParams.set("key", API_KEY);
url.searchParams.set("token", TOKEN);
url.searchParams.set("name", name);
await fetch(url.toString(), { method: "POST" });
}
// Usage
const checklist = await createChecklist(card.id, "Implementation Steps");
await addChecklistItem(checklist.id, "Set up environment");
await addChecklistItem(checklist.id, "Write tests");
await addChecklistItem(checklist.id, "Implement feature");
await addChecklistItem(checklist.id, "Code review");
Labels
Categorize cards:
async function createLabel(boardId: string, name: string, color: string) {
const url = new URL(`${BASE_URL}/labels`);
url.searchParams.set("key", API_KEY);
url.searchParams.set("token", TOKEN);
url.searchParams.set("name", name);
url.searchParams.set("color", color);
url.searchParams.set("idBoard", boardId);
const response = await fetch(url.toString(), { method: "POST" });
return await response.json();
}
// Create standard labels
const labels = {
bug: await createLabel(board.id, "Bug", "red"),
feature: await createLabel(board.id, "Feature", "green"),
urgent: await createLabel(board.id, "Urgent", "orange"),
research: await createLabel(board.id, "Research", "blue")
};
Attachments
Attach files or links to cards:
async function addAttachment(cardId: string, url: string, name?: string) {
const apiUrl = new URL(`${BASE_URL}/cards/${cardId}/attachments`);
apiUrl.searchParams.set("key", API_KEY);
apiUrl.searchParams.set("token", TOKEN);
apiUrl.searchParams.set("url", url);
if (name) {
apiUrl.searchParams.set("name", name);
}
await fetch(apiUrl.toString(), { method: "POST" });
}
// Attach research links
await addAttachment(card.id, "https://docs.example.com", "Documentation");
await addAttachment(card.id, "https://github.com/repo/issues/123", "Related Issue");
Webhooks
Receive real-time updates:
async function createWebhook(boardId: string, callbackUrl: string) {
const url = new URL(`${BASE_URL}/webhooks`);
url.searchParams.set("key", API_KEY);
url.searchParams.set("token", TOKEN);
url.searchParams.set("idModel", boardId);
url.searchParams.set("callbackURL", callbackUrl);
const response = await fetch(url.toString(), { method: "POST" });
return await response.json();
}
// Set up webhook endpoint (Express example)
app.post("/webhook/trello", async (req, res) => {
const event = req.body;
if (event.action.type === "updateCard") {
const card = event.action.data.card;
console.log(`Card updated: ${card.name}`);
// React to card changes
await processCardUpdate(card);
}
res.sendStatus(200);
});
Automation Workflows
Auto-Move Completed Tasks
async function autoArchiveCompleted() {
const board = await getBoardByName("Agent Tasks");
const doneList = await getListByName(board.id, "Done");
const cards = await getCards(doneList.id);
const oneDayAgo = Date.now() - 24 * 60 * 60 * 1000;
for (const card of cards) {
const lastActivity = new Date(card.dateLastActivity).getTime();
if (lastActivity < oneDayAgo) {
await archiveCard(card.id);
console.log(`Archived: ${card.name}`);
}
}
}
// Run daily
setInterval(autoArchiveCompleted, 24 * 60 * 60 * 1000);
Auto-Prioritize Overdue Tasks
async function flagOverdueTasks() {
const board = await getBoardByName("Agent Tasks");
const lists = await getLists(board.id);
for (const list of lists) {
if (list.name === "Done") continue;
const cards = await getCards(list.id);
for (const card of cards) {
if (card.due && new Date(card.due) < new Date()) {
// Add urgent label
await addLabelToCard(card.id, labels.urgent.id);
// Comment on card
await addComment(card.id, "⚠️ This task is overdue");
}
}
}
}
Best Practices
1. Use Consistent Board Structure
Standardize lists across projects:
- Backlog
- To Do
- In Progress
- Review
- Done
2. Descriptive Card Names
Good: "Implement SearXNG integration for private search"
Bad: "Search stuff"
3. Use Due Dates Sparingly
Only set due dates for time-sensitive tasks. Overuse creates noise.
4. Regular Cleanup
Archive completed cards weekly. Keep boards manageable.
5. Labels for Context
Use labels for:
- Priority (urgent, high, normal, low)
- Type (bug, feature, research)
- Status (blocked, waiting, in-review)
Wrapping Up
Trello provides OpenClaw agents with powerful, visual task management. Whether tracking bugs, planning projects, or organizing research, Trello's flexible boards keep agents organized.
Start simple: create a board, add some cards, move them around. Then build automation, integrate with agent workflows, and create specialized boards for different activities.
Good task management is force multiplication. Your agent will be more effective when it knows exactly what to work on next.