send_email fired and the message landed in your inbox. That was Gmail — one app, one action. Your calendar is a different app. What do you actually know about your own calendars before we touch them?
Same shape as send_email — one action, one response dict, right? I'm guessing I have at least two calendars: work and personal. Maybe a shared team one. I've never actually counted.
Composio sees them the same way. GOOGLECALENDAR_LIST_CALENDARS returns a dict with an items key — one entry per calendar you have access to. Here's the call:
result = toolset.execute_action(Action.GOOGLECALENDAR_LIST_CALENDARS, {})
calendars = result.get("items", [])
print(f"Found {len(calendars)} calendars")Empty params dict? No arguments at all — it just knows which account to ask?
It knows because of the OAuth connection you set up. The entity ID ties the action to your account — no credentials in the call itself. The params dict is empty because listing calendars takes no filters. The pattern is identical to GMAIL_FETCH_EMAILS, just a different action slug.
So if items is missing — like on a brand-new account — I'd get a KeyError without the .get()?
You just answered your own question, and you didn't even hesitate. Week 1 reflex, fully installed. .get("items", []) is the safe fallback, same as messages in Gmail. Every Composio response dict deserves that treatment. Here's the full function:
def list_calendars() -> list:
result = toolset.execute_action(Action.GOOGLECALENDAR_LIST_CALENDARS, {})
return result.get("items", [])Calendar objects are just dicts too, aren't they. Each item in that list probably has an id, a name, a timezone. Everything in this track is dicts all the way down.
Exactly right. Each calendar dict has a summary (display name), an id you'll use to target specific calendars, and a timezone. That id is what the next action needs — you can't find or create events without knowing which calendar to write to.
GOOGLECALENDAR_LIST_CALENDARS requires no parameters — the OAuth connection carries the account context. It returns {"items": [...]} where each object represents a calendar you have access to.
Each calendar dict includes:
id — the calendar identifier you'll pass to find/create event actionssummary — the human-readable name shown in the sidebartimeZone — the calendar's default timezone.get("items", []) mattersOn accounts with no accessible calendars, the items key may be absent. Bracket access crashes; .get() returns an empty list. Same reflex as every other response dict in this track.
send_email fired and the message landed in your inbox. That was Gmail — one app, one action. Your calendar is a different app. What do you actually know about your own calendars before we touch them?
Same shape as send_email — one action, one response dict, right? I'm guessing I have at least two calendars: work and personal. Maybe a shared team one. I've never actually counted.
Composio sees them the same way. GOOGLECALENDAR_LIST_CALENDARS returns a dict with an items key — one entry per calendar you have access to. Here's the call:
result = toolset.execute_action(Action.GOOGLECALENDAR_LIST_CALENDARS, {})
calendars = result.get("items", [])
print(f"Found {len(calendars)} calendars")Empty params dict? No arguments at all — it just knows which account to ask?
It knows because of the OAuth connection you set up. The entity ID ties the action to your account — no credentials in the call itself. The params dict is empty because listing calendars takes no filters. The pattern is identical to GMAIL_FETCH_EMAILS, just a different action slug.
So if items is missing — like on a brand-new account — I'd get a KeyError without the .get()?
You just answered your own question, and you didn't even hesitate. Week 1 reflex, fully installed. .get("items", []) is the safe fallback, same as messages in Gmail. Every Composio response dict deserves that treatment. Here's the full function:
def list_calendars() -> list:
result = toolset.execute_action(Action.GOOGLECALENDAR_LIST_CALENDARS, {})
return result.get("items", [])Calendar objects are just dicts too, aren't they. Each item in that list probably has an id, a name, a timezone. Everything in this track is dicts all the way down.
Exactly right. Each calendar dict has a summary (display name), an id you'll use to target specific calendars, and a timezone. That id is what the next action needs — you can't find or create events without knowing which calendar to write to.
GOOGLECALENDAR_LIST_CALENDARS requires no parameters — the OAuth connection carries the account context. It returns {"items": [...]} where each object represents a calendar you have access to.
Each calendar dict includes:
id — the calendar identifier you'll pass to find/create event actionssummary — the human-readable name shown in the sidebartimeZone — the calendar's default timezone.get("items", []) mattersOn accounts with no accessible calendars, the items key may be absent. Bracket access crashes; .get() returns an empty list. Same reflex as every other response dict in this track.
Create a free account to get started. Paid plans unlock all tracks.