create_event wrote directly to your calendar. Now there's another to-do system sitting in the same Google account — Google Tasks. Before you can read tasks from a list, you need to know which lists exist. What's your instinct for the first call?
create_event needed a cal_id before it could do anything — so I'd guess Tasks works the same way. Some kind of LIST_TASK_LISTS first, get an ID, then pass it to the actual read call?
Exactly right. GOOGLETASKS_LIST_TASK_LISTS takes no parameters and returns a dict with an items key — one entry per task list you have. Here's how you discover them:
lists_result = toolset.execute_action(Action.GOOGLETASKS_LIST_TASK_LISTS, {})
task_lists = lists_result.get("items", [])
print(f"Found {len(task_lists)} task lists")So the id from one of those list dicts is what I pass to LIST_TASKS? But the param name — is it list_id, tasklist_id, something else?
The param key is tasklist. Pass the list's id value to GOOGLETASKS_LIST_TASKS and you get back another dict — this time the tasks themselves are under items. The full function reads the list ID you already have and returns those tasks:
def list_tasks(list_id: str) -> list:
result = toolset.execute_action(Action.GOOGLETASKS_LIST_TASKS, {"tasklist": list_id})
return result.get("items", [])Both actions use items as the key. Calendar used items too. Is every Composio collection response literally the same shape?
Almost every list action in this track returns {"items": [...]}. Gmail is the odd one out with messages. Once you've seen the pattern three times it stops being a thing you have to check — it's just how Composio shapes collection responses.
So I can chain these. Call LIST_TASK_LISTS, pick the first ID, pass it to list_tasks, and I've surfaced every task in my inbox — without opening the Tasks sidebar once.
That's the read half of the Tasks API. Each task dict in items has a title, a status — needsAction or completed — and an optional due date string. Those fields are what the next action needs to update a task or mark it done.
Google Tasks splits the read into two steps: discover your lists, then query a specific one.
GOOGLETASKS_LIST_TASK_LISTS requires no parameters — the OAuth connection provides account context. It returns {"items": [...]} where each object has an id and a title.
Pass that id as tasklist to GOOGLETASKS_LIST_TASKS. The response is the same shape: {"items": [...]} where each task dict includes title, status (needsAction / completed), and an optional due date.
.get("items", []) is the right fallback for both calls — an empty or new list returns no items key, and bracket access would crash.
create_event wrote directly to your calendar. Now there's another to-do system sitting in the same Google account — Google Tasks. Before you can read tasks from a list, you need to know which lists exist. What's your instinct for the first call?
create_event needed a cal_id before it could do anything — so I'd guess Tasks works the same way. Some kind of LIST_TASK_LISTS first, get an ID, then pass it to the actual read call?
Exactly right. GOOGLETASKS_LIST_TASK_LISTS takes no parameters and returns a dict with an items key — one entry per task list you have. Here's how you discover them:
lists_result = toolset.execute_action(Action.GOOGLETASKS_LIST_TASK_LISTS, {})
task_lists = lists_result.get("items", [])
print(f"Found {len(task_lists)} task lists")So the id from one of those list dicts is what I pass to LIST_TASKS? But the param name — is it list_id, tasklist_id, something else?
The param key is tasklist. Pass the list's id value to GOOGLETASKS_LIST_TASKS and you get back another dict — this time the tasks themselves are under items. The full function reads the list ID you already have and returns those tasks:
def list_tasks(list_id: str) -> list:
result = toolset.execute_action(Action.GOOGLETASKS_LIST_TASKS, {"tasklist": list_id})
return result.get("items", [])Both actions use items as the key. Calendar used items too. Is every Composio collection response literally the same shape?
Almost every list action in this track returns {"items": [...]}. Gmail is the odd one out with messages. Once you've seen the pattern three times it stops being a thing you have to check — it's just how Composio shapes collection responses.
So I can chain these. Call LIST_TASK_LISTS, pick the first ID, pass it to list_tasks, and I've surfaced every task in my inbox — without opening the Tasks sidebar once.
That's the read half of the Tasks API. Each task dict in items has a title, a status — needsAction or completed — and an optional due date string. Those fields are what the next action needs to update a task or mark it done.
Google Tasks splits the read into two steps: discover your lists, then query a specific one.
GOOGLETASKS_LIST_TASK_LISTS requires no parameters — the OAuth connection provides account context. It returns {"items": [...]} where each object has an id and a title.
Pass that id as tasklist to GOOGLETASKS_LIST_TASKS. The response is the same shape: {"items": [...]} where each task dict includes title, status (needsAction / completed), and an optional due date.
.get("items", []) is the right fallback for both calls — an empty or new list returns no items key, and bracket access would crash.
Create a free account to get started. Paid plans unlock all tracks.