You can already pull messages from Gmail by query — subjects, senders, unread flags. All reads. What's the first write action you'd want from a professional inbox?
Drafting the weekly-update email. I write the same one every Monday. Same recipients, same subject line, different body. I've done it 200 times.
That's the moment this track is designed for. Today we go the other direction — instead of reading, we write. One action creates a draft in your Gmail Drafts folder, ready to review and send:
result = toolset.execute_action(Action.GMAIL_CREATE_EMAIL_DRAFT, {
"recipient_email": to,
"subject": subject,
"body": body
})So it doesn't actually send? It just sits in Drafts?
Exactly — and that's deliberate. In real workflows, always draft first, confirm second, send third. GMAIL_CREATE_EMAIL_DRAFT gives you a safety valve: your code runs, you see the draft in Gmail, you decide. The discipline of drafting before sending is the habit that keeps automation trustworthy. Here's the full function:
def draft_email(to: str, subject: str, body: str) -> dict:
result = toolset.execute_action(Action.GMAIL_CREATE_EMAIL_DRAFT, {
"recipient_email": to,
"subject": subject,
"body": body
})
print(f"Drafted email to {to}: {subject}")
return resultWhat does the response look like? Is it the same dict shape as fetch?
Every Composio action returns a dict — you're starting to see the pattern. This one contains the new draft's id and metadata. You don't need to parse it today; returning it unchanged is the right answer. Your function is a thin, reliable wrapper.
Wait — I just created a draft in my real Gmail account. It's sitting there right now, in Drafts, subject line and everything.
That's automation touching your real data for the first time as a write. The next step in any real workflow is sending — which means you'll want to confirm the recipient before running it. Get comfortable with that pause. It's the habit that separates reliable automation from the kind that causes Monday-morning incidents.
Drafting before sending is the discipline that makes write automation trustworthy. GMAIL_CREATE_EMAIL_DRAFT deposits the message in your Drafts folder — nothing goes anywhere until you or your workflow explicitly sends it.
toolset.execute_action(Action.GMAIL_CREATE_EMAIL_DRAFT, {
"recipient_email": to,
"subject": subject,
"body": body
})| Approach | Risk |
|---|---|
| Draft, review, send | Zero accidental sends |
| Send directly | One typo in to goes to the wrong person |
Return the response dict unchanged — it carries the draft id your workflow can reference later. The print confirms which draft was created without touching the return value.
You can already pull messages from Gmail by query — subjects, senders, unread flags. All reads. What's the first write action you'd want from a professional inbox?
Drafting the weekly-update email. I write the same one every Monday. Same recipients, same subject line, different body. I've done it 200 times.
That's the moment this track is designed for. Today we go the other direction — instead of reading, we write. One action creates a draft in your Gmail Drafts folder, ready to review and send:
result = toolset.execute_action(Action.GMAIL_CREATE_EMAIL_DRAFT, {
"recipient_email": to,
"subject": subject,
"body": body
})So it doesn't actually send? It just sits in Drafts?
Exactly — and that's deliberate. In real workflows, always draft first, confirm second, send third. GMAIL_CREATE_EMAIL_DRAFT gives you a safety valve: your code runs, you see the draft in Gmail, you decide. The discipline of drafting before sending is the habit that keeps automation trustworthy. Here's the full function:
def draft_email(to: str, subject: str, body: str) -> dict:
result = toolset.execute_action(Action.GMAIL_CREATE_EMAIL_DRAFT, {
"recipient_email": to,
"subject": subject,
"body": body
})
print(f"Drafted email to {to}: {subject}")
return resultWhat does the response look like? Is it the same dict shape as fetch?
Every Composio action returns a dict — you're starting to see the pattern. This one contains the new draft's id and metadata. You don't need to parse it today; returning it unchanged is the right answer. Your function is a thin, reliable wrapper.
Wait — I just created a draft in my real Gmail account. It's sitting there right now, in Drafts, subject line and everything.
That's automation touching your real data for the first time as a write. The next step in any real workflow is sending — which means you'll want to confirm the recipient before running it. Get comfortable with that pause. It's the habit that separates reliable automation from the kind that causes Monday-morning incidents.
Drafting before sending is the discipline that makes write automation trustworthy. GMAIL_CREATE_EMAIL_DRAFT deposits the message in your Drafts folder — nothing goes anywhere until you or your workflow explicitly sends it.
toolset.execute_action(Action.GMAIL_CREATE_EMAIL_DRAFT, {
"recipient_email": to,
"subject": subject,
"body": body
})| Approach | Risk |
|---|---|
| Draft, review, send | Zero accidental sends |
| Send directly | One typo in to goes to the wrong person |
Return the response dict unchanged — it carries the draft id your workflow can reference later. The print confirms which draft was created without touching the return value.
Create a free account to get started. Paid plans unlock all tracks.