First chain. Read 1 email's subject. Create a Calendar event whose title is that subject. Two execute_action calls, one transformation between them.
result = toolset.execute_action(Action.GMAIL_FETCH_EMAILS, {"max_results": 1})
messages = result.get("messages", [])
first_subject = messages[0].get("subject", "(no subject)") if messages else "(empty inbox)"
event = toolset.execute_action(Action.GOOGLECALENDAR_CREATE_EVENT, {
"calendar_id": "primary",
"summary": f"Latest inbox: {first_subject}",
"start_datetime": "2026-12-31T08:00:00+00:00",
"event_duration_minutes": 15,
})No new APIs — just two from prior lessons stitched together?
That's the whole point of week 3. Composition. The hard part isn't any individual call; it's the plumbing — guard against empty results, transform shapes, handle the case where the read fails before the write fires.
What if the inbox is empty — should I still write?
Depends on the use case. The scaffold above writes "(empty inbox)" as the event title — sometimes that's the signal you want. Other times you'd if not messages: return and skip the write. Both are valid; pick what your workflow needs.
The shape:
# 1. read
result = toolset.execute_action(Action.GMAIL_FETCH_EMAILS, {"max_results": 1})
messages = result.get("messages", [])
# 2. transform
first_subject = messages[0].get("subject", "(no subject)") if messages else None
# 3. write
if first_subject:
toolset.execute_action(Action.GOOGLECALENDAR_CREATE_EVENT, {
"calendar_id": "primary",
"summary": f"Latest inbox: {first_subject}",
"start_datetime": "2026-12-31T08:00:00+00:00",
"event_duration_minutes": 15,
})The transformation step is where most chain bugs live. The read could return:
messages list (inbox empty)messages not in the dict at all (API edge case)subject field (some emails).get(key, default) handles each layer. Without it, the script crashes mid-chain — and the write never fires, so users notice.
If you genuinely don't want to write on empty:
if not messages:
print("empty inbox — nothing to forward")
return # or `import sys; sys.exit(0)`Clear signal vs. writing an empty-shaped record you'll then have to filter on the consumer side.
A chain that partially succeeds is worse than one that fully fails. If the read works but the write fails, you'd want to know. Catch and log:
try:
toolset.execute_action(Action.GOOGLECALENDAR_CREATE_EVENT, {...})
except Exception as e:
print(f"Calendar create failed: {type(e).__name__}: {e}")Day 19 of this track formalizes the "log every step" pattern.
First chain. Read 1 email's subject. Create a Calendar event whose title is that subject. Two execute_action calls, one transformation between them.
result = toolset.execute_action(Action.GMAIL_FETCH_EMAILS, {"max_results": 1})
messages = result.get("messages", [])
first_subject = messages[0].get("subject", "(no subject)") if messages else "(empty inbox)"
event = toolset.execute_action(Action.GOOGLECALENDAR_CREATE_EVENT, {
"calendar_id": "primary",
"summary": f"Latest inbox: {first_subject}",
"start_datetime": "2026-12-31T08:00:00+00:00",
"event_duration_minutes": 15,
})No new APIs — just two from prior lessons stitched together?
That's the whole point of week 3. Composition. The hard part isn't any individual call; it's the plumbing — guard against empty results, transform shapes, handle the case where the read fails before the write fires.
What if the inbox is empty — should I still write?
Depends on the use case. The scaffold above writes "(empty inbox)" as the event title — sometimes that's the signal you want. Other times you'd if not messages: return and skip the write. Both are valid; pick what your workflow needs.
The shape:
# 1. read
result = toolset.execute_action(Action.GMAIL_FETCH_EMAILS, {"max_results": 1})
messages = result.get("messages", [])
# 2. transform
first_subject = messages[0].get("subject", "(no subject)") if messages else None
# 3. write
if first_subject:
toolset.execute_action(Action.GOOGLECALENDAR_CREATE_EVENT, {
"calendar_id": "primary",
"summary": f"Latest inbox: {first_subject}",
"start_datetime": "2026-12-31T08:00:00+00:00",
"event_duration_minutes": 15,
})The transformation step is where most chain bugs live. The read could return:
messages list (inbox empty)messages not in the dict at all (API edge case)subject field (some emails).get(key, default) handles each layer. Without it, the script crashes mid-chain — and the write never fires, so users notice.
If you genuinely don't want to write on empty:
if not messages:
print("empty inbox — nothing to forward")
return # or `import sys; sys.exit(0)`Clear signal vs. writing an empty-shaped record you'll then have to filter on the consumer side.
A chain that partially succeeds is worse than one that fully fails. If the read works but the write fails, you'd want to know. Catch and log:
try:
toolset.execute_action(Action.GOOGLECALENDAR_CREATE_EVENT, {...})
except Exception as e:
print(f"Calendar create failed: {type(e).__name__}: {e}")Day 19 of this track formalizes the "log every step" pattern.
Create a free account to get started. Paid plans unlock all tracks.