Sync tasks with Trello or Asana
Keep Northwind work aligned across Sheets and your client's tool of choice.
Publicado em 23 de out. de 2025
Every Northwind client seems to run a different task tool. One lives in Trello, another in Asana, and the studio itself reports on everything in a single spreadsheet. Keeping that spreadsheet honest means someone copying card titles and due dates across by hand — work that is tedious, error-prone, and always a little behind.
This script pulls a board’s cards straight into a sheet on a schedule. Point it at a Trello board and it mirrors every card — id, title, list, due date and link — so the studio’s reporting tab reflects the client’s board without anyone retyping a thing. The same pattern works for Asana with a different endpoint, shown at the end.
What you’ll need
- A Google Sheet to hold the mirrored cards. The script writes to its first tab and creates the header row itself.
- A Trello account with access to the board you want to mirror, and its board
ID (the short code in the board URL, e.g.
trello.com/b/<id>/...). - A Trello API key and token, saved as
TRELLO_KEYandTRELLO_TOKENin Script Properties — see Store API keys and secrets securely. - The board ID and sheet ID set in the config block below.
The script (Trello)
// The Trello board to mirror — the short code from the board URL.
const TRELLO_BOARD = 'BOARD_ID';
// The spreadsheet that mirrors the board's cards.
const TASKS_SHEET_ID = '1abcTrelloId';
// The columns written to the sheet, in order.
const HEADERS = ['id', 'name', 'list', 'due', 'url'];
/**
* Pulls every card from a Trello board and rewrites the tasks sheet
* so it mirrors the board.
*/
function pullTrelloCards() {
// 1. Read the API credentials from Script Properties — never inline them.
const props = PropertiesService.getScriptProperties();
const key = props.getProperty('TRELLO_KEY');
const token = props.getProperty('TRELLO_TOKEN');
if (!key || !token) {
Logger.log('Missing TRELLO_KEY or TRELLO_TOKEN in Script Properties.');
return;
}
// 2. Ask Trello for every card on the board. Auth travels as query
// parameters, which is how Trello's REST API expects it.
const url = 'https://api.trello.com/1/boards/' + TRELLO_BOARD +
'/cards?key=' + key + '&token=' + token;
const response = UrlFetchApp.fetch(url, { muteHttpExceptions: true });
// 3. Bail out clearly if the request failed (bad board, bad token).
if (response.getResponseCode() !== 200) {
Logger.log('Trello request failed: ' + response.getContentText());
return;
}
// 4. Flatten each card into a spreadsheet row. `due` can be null,
// so fall back to an empty string for a clean cell.
const cards = JSON.parse(response.getContentText());
const rows = cards.map((c) => [
c.id,
c.name,
c.idList,
c.due || '',
c.url,
]);
// 5. Rewrite the sheet from scratch so it always reflects the board.
const sheet = SpreadsheetApp.openById(TASKS_SHEET_ID).getSheets()[0];
sheet.clear();
sheet.getRange(1, 1, 1, HEADERS.length).setValues([HEADERS]);
if (rows.length) {
sheet.getRange(2, 1, rows.length, HEADERS.length).setValues(rows);
}
Logger.log('Mirrored ' + rows.length + ' Trello cards.');
}
How it works
pullTrelloCardsreadsTRELLO_KEYandTRELLO_TOKENfrom Script Properties. If either is missing the script logs a message and stops, so a half-configured project fails loudly.- It builds the board’s cards endpoint, passing the key and token as query parameters — Trello’s REST API authenticates that way rather than with a header.
muteHttpExceptionslets the script check the response itself. A non-200 code (a wrong board ID or a revoked token) is logged and the run stops.- Each card is flattened to five columns.
c.dueisnullfor cards with no due date, soc.due || ''keeps those cells clean instead of writing the word “null”. - The first tab is cleared and rewritten: header row, then one row per card. It is a full mirror — cards deleted on the board disappear from the sheet on the next run.
Example run
The Trello board has these cards:
| Card | List | Due |
|---|---|---|
| Draft homepage copy | In progress | 2026-05-30 |
| Client review call | To do | (none) |
| Ship favicon fix | Done | 2026-05-20 |
After a run, the first tab of the tasks sheet holds:
| id | name | list | due | url |
|---|---|---|---|---|
| 66f1a… | Draft homepage copy | 5e2b… | 2026-05-30T00:00:00Z | https://trello.com/c/aB1 |
| 66f1b… | Client review call | 5e2a… | https://trello.com/c/cD2 | |
| 66f1c… | Ship favicon fix | 5e2c… | 2026-05-20T00:00:00Z | https://trello.com/c/eF3 |
The list column holds Trello’s internal list IDs. Map those to readable
names with a small lookup tab, or fetch /boards/<id>/lists and join on the ID.
Trigger it
Run this on a schedule so the reporting tab stays current on its own:
- In the Apps Script editor, open Triggers (the clock icon).
- Add a trigger: choose
pullTrelloCards, event source Time-driven, and an Hour timer every few hours during the working day. - Save and approve the authorisation prompt the first time.
For Asana, the same shape applies — only the request changes. Asana uses a
bearer token in a header and returns tasks under a data key:
// The Asana project to mirror, and its personal access token key.
const ASANA_PROJECT = 'PROJECT_GID';
/**
* Pulls tasks from an Asana project. A drop-in alternative to the
* Trello version above — write the rows to the same sheet.
*/
function pullAsanaTasks() {
const token = PropertiesService.getScriptProperties()
.getProperty('ASANA_TOKEN');
if (!token) {
Logger.log('Missing ASANA_TOKEN in Script Properties.');
return;
}
// Asana wants a bearer token and returns tasks under `data`.
const url = 'https://app.asana.com/api/1.0/projects/' + ASANA_PROJECT +
'/tasks?opt_fields=name,due_on,completed,permalink_url';
const response = UrlFetchApp.fetch(url, {
headers: { Authorization: 'Bearer ' + token },
muteHttpExceptions: true,
});
if (response.getResponseCode() !== 200) {
Logger.log('Asana request failed: ' + response.getContentText());
return;
}
const tasks = JSON.parse(response.getContentText()).data;
Logger.log('Asana returned ' + tasks.length + ' tasks.');
// Map and write `tasks` to the sheet exactly as pullTrelloCards does.
}
Watch out for
- Trello returns list IDs, not list names. If the report needs human-readable columns, fetch the board’s lists once and build an ID-to-name lookup.
- The full
clear()then rewrite wipes any manual notes on the sheet each run. Keep annotations on a separate tab so they survive. - Trello tokens belong to a person, not a workspace. If the person who issued the token loses board access, the call starts returning 401 — the response guard will catch it.
- A board can hold hundreds of cards. The cards endpoint returns up to 1000 by
default; very large boards need pagination with the
beforeparameter. - Trello and Asana both rate-limit (roughly 100 and 150 requests per minute). One scheduled call is fine, but do not loop the function in a tight retry.
Relacionados
Sync calendar bookings with Calendly
Bridge Google Calendar and Calendly — Northwind bookings on either side appear on both.
Atualizado em 7 de jan. de 2026
Connect to an air-quality and weather feed
Build a Northwind environmental dashboard — current London AQI plus 5-day forecast.
Atualizado em 30 de dez. de 2025
Build a podcast and media stats tracker
Pull Northwind's podcast download numbers across platforms into a single sheet.
Atualizado em 10 de dez. de 2025
Track real-estate listings for new matches
Monitor property feeds for Northwind office hunts — alert when a match appears.
Atualizado em 28 de nov. de 2025
Translate columns with a translation API
Localise Northwind text in bulk without manual work — via Google Translate or DeepL.
Atualizado em 24 de nov. de 2025