To integrate Replit with Trello, generate an API Key from trello.com/power-ups/admin and authorize a Token, store both in Replit Secrets (lock icon in sidebar), then call the Trello REST API to create cards, move them between lists, and manage boards. Register a webhook pointing to your deployed Replit app for real-time card change notifications.
Why Connect Replit to Trello?
Trello's simplicity is its strength β boards, lists, and cards map naturally to any workflow. But when your project management needs go beyond what Trello's built-in automations can handle, the Trello REST API lets you build custom integrations that automate card creation, status updates, and reporting in ways that Power-Ups and Butler rules cannot.
Common integration scenarios include: automatically creating a Trello card when a new support ticket arrives, moving cards between lists based on webhook events from other systems, building a custom reporting dashboard that aggregates data across multiple boards, or syncing Trello card status to a database so you can query project progress. Any time you want to create cards from code (form submissions, scheduled jobs, external triggers) or react to card changes programmatically, the Trello API is the tool.
Trello's API uses simple query parameter authentication β your API Key and Token are appended to every request URL. This is less secure than OAuth but significantly simpler for personal integrations and server-side code where the credentials are stored in Replit Secrets and never exposed to browsers. This tutorial walks through the full setup: generating credentials, making API calls, and receiving webhook events on a deployed Replit app.
Integration method
The Replit-Trello integration works by authenticating with a Trello API Key and Token stored in Replit Secrets, then calling the Trello REST API from your backend server to interact with boards, lists, and cards. API Key + Token authentication is simpler than OAuth for personal integrations and gives access to all boards the authorizing account can see. For event-driven workflows, you register a Trello webhook pointing to your deployed Replit app that fires whenever a card or board changes.
Prerequisites
- A Trello account with at least one board created
- A Replit account with a Node.js or Python Repl ready
- Basic familiarity with REST APIs and HTTP requests
- A deployed Replit app URL (needed for webhook registration β use Autoscale deployment)
Step-by-step guide
Generate Your Trello API Key and Token
Generate Your Trello API Key and Token
Trello's authentication uses two credentials: an API Key (identifies your integration) and a Token (grants access to a specific Trello account's data). Both are required for every API call. To get your API Key, log into Trello and navigate to trello.com/power-ups/admin. Click 'New Power-Up' or find an existing one β even if you are not building a Power-Up, this is where Trello manages developer apps. Fill in the required fields (Power-Up Name: 'Replit Integration', Workspace: choose any, Iframe connector URL: leave blank for now). Click 'Create' and then click on your new app. Under the 'API Key' tab, you will see your API Key displayed. Click 'Show API Key' and copy it. Next, generate a Token. On the same API Key page, click the 'Token' link (shown in a note below the API Key). This opens an authorization page where Trello asks you to grant the app access to your account. Click 'Allow'. You will see a long Token string β copy it immediately as it is shown only once on this page. The Token you just generated grants read/write access to all boards, lists, and cards the authorizing Trello account can access. It does not expire unless you manually revoke it in Trello Settings > Apps. For integrations where you want read-only access or time-limited access, append &expiration=1day or &expiration=never to the token authorization URL and set &scope=read instead of &scope=read,write.
Pro tip: The API Key is associated with your developer app and can be shared across multiple integrations. The Token is per-account authorization β if you need your integration to act on behalf of multiple users, each user must authorize a separate Token.
Expected result: You have both a Trello API Key (32-character string) and a Token (64-character string) ready to store in Replit.
Store Credentials in Replit Secrets
Store Credentials in Replit Secrets
Click the lock icon (π) in the Replit sidebar to open the Secrets panel. Add two secrets: 1. TRELLO_API_KEY β your 32-character API Key from the Power-Up admin page 2. TRELLO_TOKEN β your 64-character Token from the authorization page Click 'Add Secret' after each one. These values are now encrypted with AES-256 and stored separately from your code. They will never appear in your file tree, version control history, or be visible to other users who fork your Repl. If you need to work with multiple Trello workspaces or use different tokens for different environments, you can add additional secrets like TRELLO_TOKEN_PROD and TRELLO_TOKEN_DEV and select the appropriate one with an environment variable check. You can also store frequently used IDs as secrets to avoid hardcoding them: - TRELLO_BOARD_ID β the ID of your main board (find it by adding .json to your board URL: trello.com/b/{boardId}/board-name.json) - TRELLO_LIST_ID_TODO β the ID of your 'To Do' list (find it from the board JSON or the Trello API) Access in code: - Node.js: process.env.TRELLO_API_KEY - Python: os.environ['TRELLO_API_KEY']
Pro tip: To find a board ID quickly, open the board in Trello, click 'Share' in the top right, then 'Export JSON' β the JSON file URL contains the board ID. Or add .json to the board's URL in your browser.
Expected result: TRELLO_API_KEY and TRELLO_TOKEN appear in the Replit Secrets panel with values hidden.
Read Boards, Lists, and Cards from the Trello API
Read Boards, Lists, and Cards from the Trello API
The Trello REST API base URL is https://api.trello.com/1/. All requests append the API key and token as query parameters. This is simpler than bearer token authentication but means both values appear in request URLs β ensure you are always making these calls server-side (from your Replit backend), never from browser JavaScript where they would be visible in network logs. The code below provides both Node.js and Python examples for reading board structure and card data. Install the Python requests library if using Python: in the Replit Shell, run pip install requests.
1// trello-client.js β Node.js Trello API client2const BASE_URL = 'https://api.trello.com/1';34function getAuthParams() {5 const key = process.env.TRELLO_API_KEY;6 const token = process.env.TRELLO_TOKEN;7 if (!key || !token) throw new Error('TRELLO_API_KEY and TRELLO_TOKEN secrets required');8 return `key=${key}&token=${token}`;9}1011async function trelloGet(endpoint) {12 const url = `${BASE_URL}${endpoint}${endpoint.includes('?') ? '&' : '?'}${getAuthParams()}`;13 const response = await fetch(url);14 if (!response.ok) {15 const error = await response.text();16 throw new Error(`Trello API ${response.status}: ${error}`);17 }18 return response.json();19}2021async function trelloPost(endpoint, body) {22 const url = `${BASE_URL}${endpoint}?${getAuthParams()}`;23 const response = await fetch(url, {24 method: 'POST',25 headers: { 'Content-Type': 'application/json' },26 body: JSON.stringify(body)27 });28 if (!response.ok) {29 const error = await response.text();30 throw new Error(`Trello API ${response.status}: ${error}`);31 }32 return response.json();33}3435async function trelloPut(endpoint, body) {36 const url = `${BASE_URL}${endpoint}?${getAuthParams()}`;37 const response = await fetch(url, {38 method: 'PUT',39 headers: { 'Content-Type': 'application/json' },40 body: JSON.stringify(body)41 });42 if (!response.ok) {43 const error = await response.text();44 throw new Error(`Trello API ${response.status}: ${error}`);45 }46 return response.json();47}4849// Get all boards for the authenticated user50async function getBoards() {51 return trelloGet('/members/me/boards?fields=id,name,url');52}5354// Get all lists on a board55async function getLists(boardId) {56 return trelloGet(`/boards/${boardId}/lists?fields=id,name,pos`);57}5859// Get all cards in a list60async function getCards(listId) {61 return trelloGet(`/lists/${listId}/cards?fields=id,name,desc,due,labels,url`);62}6364module.exports = { trelloGet, trelloPost, trelloPut, getBoards, getLists, getCards };Pro tip: Use the ?fields= parameter on all Trello API endpoints to request only the fields you need. This reduces response payload size significantly β Trello card objects can contain 30+ fields by default.
Expected result: Calling getBoards() returns a JSON array of your Trello boards with IDs and names. getLists() returns the lists on a specific board.
Create and Manage Cards via the API
Create and Manage Cards via the API
With the client module ready, build an Express server that exposes endpoints for creating, updating, and moving cards. Card creation requires at minimum a name and the ID of the list to add it to. Optionally, you can set a description, due date, labels, and members. The moveCard function is the most commonly used operation in automation workflows β it changes the list a card belongs to (e.g., moving from 'In Progress' to 'Done') without any other changes. Install Express if not already present: npm install express
1// server.js β Trello card management Express server2const express = require('express');3const { trelloPost, trelloPut, trelloGet, getBoards, getLists } = require('./trello-client');45const app = express();6app.use(express.json());78// GET /boards β list all accessible boards9app.get('/boards', async (req, res) => {10 try {11 const boards = await getBoards();12 res.json({ boards });13 } catch (err) {14 res.status(500).json({ error: err.message });15 }16});1718// GET /boards/:boardId/lists β get lists on a board19app.get('/boards/:boardId/lists', async (req, res) => {20 try {21 const lists = await getLists(req.params.boardId);22 res.json({ lists });23 } catch (err) {24 res.status(500).json({ error: err.message });25 }26});2728// POST /cards β create a new card29app.post('/cards', async (req, res) => {30 try {31 const { listId, name, description, dueDate, labelIds } = req.body;32 if (!listId || !name) {33 return res.status(400).json({ error: 'listId and name are required' });34 }3536 const cardData = {37 idList: listId,38 name,39 desc: description || '',40 pos: 'bottom'41 };4243 if (dueDate) cardData.due = dueDate; // ISO 8601 string44 if (labelIds && labelIds.length > 0) {45 cardData.idLabels = labelIds.join(',');46 }4748 const card = await trelloPost('/cards', cardData);49 res.json({50 id: card.id,51 name: card.name,52 url: card.url,53 shortLink: card.shortLink54 });55 } catch (err) {56 res.status(500).json({ error: err.message });57 }58});5960// PUT /cards/:cardId/move β move card to a different list61app.put('/cards/:cardId/move', async (req, res) => {62 try {63 const { targetListId } = req.body;64 if (!targetListId) {65 return res.status(400).json({ error: 'targetListId required' });66 }6768 const updated = await trelloPut(`/cards/${req.params.cardId}`, {69 idList: targetListId,70 pos: 'bottom'71 });72 res.json({ success: true, cardId: updated.id, newList: updated.idList });73 } catch (err) {74 res.status(500).json({ error: err.message });75 }76});7778// PUT /cards/:cardId β update card details79app.put('/cards/:cardId', async (req, res) => {80 try {81 const { name, description, dueComplete } = req.body;82 const updates = {};83 if (name) updates.name = name;84 if (description !== undefined) updates.desc = description;85 if (dueComplete !== undefined) updates.dueComplete = dueComplete;8687 const updated = await trelloPut(`/cards/${req.params.cardId}`, updates);88 res.json({ success: true, card: { id: updated.id, name: updated.name } });89 } catch (err) {90 res.status(500).json({ error: err.message });91 }92});9394app.listen(3000, '0.0.0.0', () => {95 console.log('Trello integration server running on port 3000');96});Pro tip: To find list IDs for your boards without writing code, use the Trello API Explorer at developer.atlassian.com/cloud/trello/rest/ or append .json to your board URL and search for 'id' inside the lists array in the response.
Expected result: POST /cards creates a new Trello card in the specified list and returns the card ID and URL. PUT /cards/:cardId/move successfully moves the card to a different list.
Set Up Trello Webhooks for Real-Time Card Events
Set Up Trello Webhooks for Real-Time Card Events
Trello webhooks fire whenever something changes on a board or card β card moves, new comments, checklist completions, and more. Unlike other services that have a webhook configuration UI, Trello webhooks are created programmatically via the API. First, deploy your Replit app to get a stable URL. Click the Deploy button, choose 'Autoscale', and note your deployment URL (e.g., https://your-app.replit.app). Your webhook endpoint must be publicly accessible and must respond to Trello's initial HEAD request with a 200 status code. To create a webhook, send a POST request to the Trello webhooks API specifying the callback URL and the model ID to watch (a board ID, list ID, or card ID). The Python code below handles both the webhook registration and the event receiver.
1# trello_webhook.py β Python webhook registration and handler2import os3import requests4from flask import Flask, request, jsonify56app = Flask(__name__)78TRELLO_BASE = 'https://api.trello.com/1'910def get_auth_params():11 return {12 'key': os.environ['TRELLO_API_KEY'],13 'token': os.environ['TRELLO_TOKEN']14 }1516def register_webhook(model_id, callback_url):17 """Register a Trello webhook for a board or card ID."""18 params = {**get_auth_params(),19 'idModel': model_id,20 'callbackURL': callback_url,21 'description': 'Replit Integration Webhook'}22 response = requests.post(f'{TRELLO_BASE}/webhooks', params=params)23 response.raise_for_status()24 return response.json()2526def list_webhooks():27 """List all webhooks for the current token."""28 params = get_auth_params()29 response = requests.get(30 f'{TRELLO_BASE}/tokens/{params["token"]}/webhooks',31 params=params32 )33 return response.json()3435# Trello sends HEAD request to verify webhook URL β must return 20036@app.route('/trello-webhook', methods=['HEAD'])37def webhook_verify():38 return '', 2003940# Handle incoming webhook events41@app.route('/trello-webhook', methods=['POST'])42def trello_webhook():43 payload = request.json44 if not payload:45 return 'OK', 2004647 action = payload.get('action', {})48 action_type = action.get('type', 'unknown')49 data = action.get('data', {})5051 print(f'Trello event: {action_type}')5253 if action_type == 'createCard':54 card_name = data.get('card', {}).get('name')55 list_name = data.get('list', {}).get('name')56 print(f'New card created: "{card_name}" in "{list_name}"')5758 elif action_type == 'updateCard':59 card = data.get('card', {})60 old = data.get('old', {})61 if 'idList' in old:62 # Card was moved between lists63 print(f'Card moved: {card.get("name")} -> list {card.get("idList")}')64 elif 'dueComplete' in old:65 complete = card.get('dueComplete')66 print(f'Card completion changed: {card.get("name")} -> {complete}')6768 elif action_type == 'commentCard':69 text = data.get('text', '')70 member = action.get('memberCreator', {}).get('fullName', 'Unknown')71 print(f'Comment by {member}: {text[:100]}')7273 return 'OK', 2007475@app.route('/register-webhook', methods=['POST'])76def create_webhook():77 """Endpoint to register a new Trello webhook."""78 data = request.json79 board_id = data.get('boardId')80 callback_url = data.get('callbackUrl')8182 if not board_id or not callback_url:83 return jsonify({'error': 'boardId and callbackUrl required'}), 4008485 webhook = register_webhook(board_id, callback_url)86 return jsonify({'webhook': webhook})8788if __name__ == '__main__':89 app.run(host='0.0.0.0', port=3000)Pro tip: Trello only sends one webhook per model (board, list, or card). To monitor multiple boards, you need to register a separate webhook for each board ID. The HEAD endpoint handler is required β Trello will reject webhook registration if the URL does not respond to HEAD with 200.
Expected result: After calling /register-webhook with your board ID and deployment URL, Trello delivers events to /trello-webhook whenever cards are created or moved on that board.
Common use cases
Automated Card Creation from Form Submissions
When a user submits a contact form or support request on your website, automatically create a Trello card in the correct list with the user's details, priority label, and due date. The card appears in your team's Trello board instantly without any manual data entry.
Build an Express server with a /submit-task POST endpoint. When called with title, description, priority, and email fields, create a new Trello card in the specified list ID, add a colored label based on priority, set a due date, and include the email in the card description. Store TRELLO_API_KEY and TRELLO_TOKEN in environment variables.
Copy this prompt to try it in Replit
Cross-System Status Sync
Build a webhook bridge that moves Trello cards between lists automatically when events happen in other systems β for example, move a card from 'In Progress' to 'Done' when a GitHub PR is merged, or move it back to 'Review' when a new comment is added.
Create a Flask server that receives GitHub webhook events. When a PR is merged, it should look up the matching Trello card by matching the PR title or branch name, then use the Trello API to move the card to the 'Done' list ID on the specified board. Return 200 for all recognized events.
Copy this prompt to try it in Replit
Project Progress Dashboard
Build a read-only dashboard that queries your Trello boards and aggregates card counts by list, shows overdue cards, and calculates throughput β all without Trello Power-Ups or paid add-ons, just your own Replit backend serving the data.
Create an Express API with a /board-stats/:boardId endpoint that fetches all lists and cards from a Trello board, counts cards per list, identifies cards with due dates in the past that are not marked complete, and returns the aggregated stats as JSON for a dashboard frontend.
Copy this prompt to try it in Replit
Troubleshooting
API returns '401 Unauthorized' or 'invalid token'
Cause: The most common causes are: the API Key or Token was copied with extra whitespace or newline characters, the Token was revoked in Trello Settings, or you are using an expired time-limited Token (if you generated a Token with expiration=1day).
Solution: Verify your credentials by testing directly in the Replit Shell: curl 'https://api.trello.com/1/members/me?key=YOUR_KEY&token=YOUR_TOKEN'. A valid response returns your profile JSON. If it fails, regenerate your Token at trello.com/power-ups/admin and update the TRELLO_TOKEN secret in Replit. Check that you authorized a 'never' expiration Token.
Webhook registration returns 'callbackURL is not valid' or 403 error
Cause: Trello validates the callback URL when you register a webhook by sending a HEAD request to it. If the URL is not accessible (development URL, not yet deployed), returns a non-200 status, or does not exist, the registration will fail.
Solution: Deploy your Replit app first using Autoscale deployment to get a stable URL. Ensure your webhook handler includes a route that responds to HEAD requests with 200. Test the URL manually in a browser or with curl before registering it with Trello.
1// Express β handle Trello's HEAD verification request2app.head('/trello-webhook', (req, res) => res.sendStatus(200));3app.post('/trello-webhook', (req, res) => {4 // handle event...5 res.sendStatus(200);6});createCard returns 'invalid id' for the list ID
Cause: The list ID (idList) provided in the card creation request does not exist or is malformed. Trello list IDs are 24-character hexadecimal strings. A common mistake is using the list name instead of the ID, or copying an ID with extra characters.
Solution: Find valid list IDs by calling GET /boards/{boardId}/lists with your credentials and checking the 'id' field on each list object. List IDs look like 5f3d8e1a2b4c6d8e0a2b4c6d. You can also find them by adding .json to your board URL and searching for the list name in the JSON response.
1// Find list IDs for a board2const lists = await trelloGet(`/boards/${boardId}/lists?fields=id,name`);3console.log(lists.map(l => ({ id: l.id, name: l.name })));Webhook events stop arriving after a period of time
Cause: Trello automatically deletes webhooks that return non-200 responses for too many consecutive deliveries (typically after 50 consecutive failures). If your Replit app was redeploying or experiencing downtime, webhook deliveries may have failed repeatedly.
Solution: Check your webhook status by calling GET /tokens/{token}/webhooks. If the webhook is missing, re-register it. To prevent this, use a Reserved VM deployment for critical webhook endpoints that need guaranteed uptime, or implement proper error handling in your webhook handler to always return 200 (even if internal processing fails).
1// Always return 200, handle errors internally2app.post('/trello-webhook', async (req, res) => {3 res.sendStatus(200); // Acknowledge immediately4 try {5 await processWebhookEvent(req.body);6 } catch (err) {7 console.error('Webhook processing error:', err.message);8 // Don't let errors cause non-200 responses to Trello9 }10});Best practices
- Always make Trello API calls from your Replit backend server, never from client-side JavaScript β the API Key and Token appear in request URLs, and exposing them in the browser allows anyone viewing your page source to access your Trello boards.
- Store your TRELLO_API_KEY and TRELLO_TOKEN in Replit Secrets (lock icon in sidebar) and also consider storing frequently-used board and list IDs as secrets or environment variables to avoid hardcoding them in your codebase.
- Use the ?fields= query parameter to request only the fields you need β by default Trello returns 30+ fields per card, but most use cases need only id, name, desc, and idList, which significantly reduces response size.
- Always include a HEAD request handler on your webhook endpoint β Trello sends an HTTP HEAD request to validate the URL before registering a webhook, and the registration will fail if your endpoint does not respond with 200.
- Deploy webhook receivers on Autoscale to ensure 24/7 availability β Trello will delete webhooks that receive too many consecutive failures, and the development URL is not available when your browser is closed.
- Cache board structure (board ID, list IDs, label IDs) in your application to avoid fetching it on every API call β board structure rarely changes and these values are safe to store in Replit Secrets or a configuration object.
- Implement idempotent card creation to prevent duplicate cards when your endpoint is called multiple times β check for an existing card with the same title before creating a new one, or use a unique identifier in the card description to deduplicate.
Alternatives
Asana offers more sophisticated project views including timelines and portfolios, better for teams that need structured project management beyond Kanban boards.
Notion combines project management with a knowledge base and has a more powerful API for structured data, making it better for teams that use it as both a wiki and task tracker.
ClickUp offers all-in-one project management with built-in docs, goals, and time tracking at a comparable price, suitable for teams that have outgrown Trello's simplicity.
Todoist is a simpler personal task manager with a clean REST API, better for individual developers tracking personal tasks than team project management.
Frequently asked questions
How do I connect Replit to Trello?
Generate a Trello API Key at trello.com/power-ups/admin, then authorize a Token by clicking the Token link on the API Key page. Store both as TRELLO_API_KEY and TRELLO_TOKEN in Replit Secrets (lock icon in the sidebar). Access them in code as process.env.TRELLO_API_KEY (Node.js) or os.environ['TRELLO_API_KEY'] (Python), and append them as query parameters to all Trello API requests.
Does Replit work with Trello webhooks?
Yes, but your Replit app must be deployed to use webhooks β the development URL only works while your browser is open. Deploy with Autoscale to get a stable https://your-app.replit.app URL. Your webhook endpoint must also respond to HEAD requests with 200, which Trello uses to verify the URL when registering the webhook.
How do I find Trello list IDs and board IDs?
The quickest way is to add .json to your Trello board URL in the browser (e.g., trello.com/b/XXXX/my-board.json) and search for 'id' in the response. For list IDs, call GET https://api.trello.com/1/boards/{boardId}/lists?key=KEY&token=TOKEN and find the id field for each list. You can also use the Trello API Explorer at developer.atlassian.com/cloud/trello/rest/.
Can I use the Trello API for free?
Yes, the Trello REST API is free for all Trello account types. There is a rate limit of 300 requests per 10 seconds per API key and 100 requests per 10 seconds per token. For typical integration use cases (creating cards, reading boards, moving cards), these limits are more than sufficient. Free Trello accounts have limits on Power-Ups and attachments but not on API access.
How do I store Trello API credentials securely in Replit?
Open the Replit Secrets panel (lock icon π in the sidebar) and add TRELLO_API_KEY and TRELLO_TOKEN as separate secrets. Replit encrypts these with AES-256 and injects them as environment variables at runtime. Never paste them directly into your code β Replit's Secret Scanner monitors code files for API key patterns and will prompt you to move them to Secrets.
Why is my Trello webhook not receiving events?
The two most common reasons are: you registered the webhook with the development URL instead of the deployment URL, or your webhook endpoint does not respond to HEAD requests. Deploy your app using Autoscale deployment to get a stable URL, add a HEAD handler that returns 200, then re-register the webhook. You can verify your webhook status by calling GET /tokens/{token}/webhooks with your Trello credentials.
Talk to an Expert
Our team has built 600+ apps. Get personalized help with your project.
Book a free consultation