starchild-ai-agent/official-skills

composio

Composio gateway: act on 1000+ connected apps like Gmail, Slack, GitHub, Calendar. Use when the user wants to act in a connected SaaS app (e.g. send Gmail, create Notion page, add Calendar event, open a GitHub issue).

All-time #3218 First seen Apr 1, 2026
8-week activity · all time api

Installation

$ npx skills add starchild-ai-agent/official-skills --skill composio

Similar popular skills

Related neighbors and high-traction skills in the same topics — useful to compare before installing.

Also in this package

Other skills from starchild-ai-agent/official-skills · top by installs.

npx skills add starchild-ai-agent/official-skills

Browse all from starchild-ai-agent/official-skills

More details

Agent compatibility

Declared targets from SKILL.md / docs. Unmarked agents are not listed — the skill may still install via the CLI.

Claude Code Not declared
Cursor Not declared
Codex Not declared
GitHub Copilot Not declared
Windsurf Not declared
Gemini CLI Not declared
Cline Not declared
OpenCode Not declared

Repository health

Stars 26
Default branch main
Open issues 0
Status Active

Skill metadata

Parsed from SKILL.md frontmatter.

Version1.4.1
More metadata
starchild
{"emoji":"🔌","skillKey":"composio"}

Package contents

Files included with this skill beyond the listing page.

  • skill md SKILL.md 31,218 B
  • docs SUMMARY.md 233 B

History

  1. First seen on skills.sh
  2. First recorded snapshot · 4,300 installs

SKILL.md

Composio — External App Integration via Gateway

Composio lets users connect 1000+ external apps (Gmail, Slack, GitHub, Google Calendar, Notion, etc.) to their Starchild agent. All operations go through the Composio Gateway (composio-gateway.fly.dev), which handles auth and API key management.

Architecture

Agent (Fly 6PN network)
    ↓  HTTP (auto-authenticated by IPv6)
Composio Gateway (composio-gateway.fly.dev)
    ↓  Composio SDK
Composio Cloud → Target API (Gmail, Slack, etc.)
  • You never touch the COMPOSIOAPIKEY — the gateway holds it
  • You never call Composio SDK directly — use the gateway HTTP API
  • Authentication is automatic — your Fly 6PN IPv6 resolves to a user_id via the billing DB
  • No env vars needed — the gateway is always accessible from any agent container

Gateway Base URL

GATEWAY = "http://composio-gateway.flycast"

All requests use plain HTTP over Fly internal network (flycast). No JWT needed.

CRITICAL — never route the gateway through sc-proxy:

  • Use curl (as in the examples below) or plain requests / http.client with no proxy.
  • Do not use proxiedget / proxiedpost for this host (even though PROTOCOL says “always proxied” for external APIs — flycast is the documented exception; core.http_client also auto-bypasses *.flycast).
  • Do not set HTTPPROXY / HTTPSPROXY or curl -x toward the gateway.
  • Proxying rewrites the caller identity so the gateway sees the wrong user and connections/execute fail or hit another locker.

API Reference

1. Search Tools (compact)

Find the right tool slug for a task. Returns compact tool info — just slug, description, and parameter names. Enough to pick the right tool.

curl -s -X POST $GATEWAY/internal/search \
  -H "Content-Type: application/json" \
  -d '{"query": "send email via gmail"}'

Response (compact):

{
  "results": [{"primary_tool_slugs": ["GMAIL_SEND_EMAIL"], "use_case": "send email", ...}],
  "tool_schemas": {
    "GMAIL_SEND_EMAIL": {
      "tool_slug": "GMAIL_SEND_EMAIL",
      "toolkit": "gmail",
      "description": "Send an email...",
      "parameters": ["to", "subject", "body", "cc", "bcc"],
      "required": ["to", "subject", "body"]
    }
  },
  "toolkit_connection_statuses": [...]
}

2. Get Tool Schema (full)

Get the complete parameter definitions for a specific tool — types, descriptions, enums, defaults. Use this after search when you need exact parameter formats.

curl -s -X POST $GATEWAY/internal/tool_schema \
  -H "Content-Type: application/json" \
  -d '{"tool": "GOOGLECALENDAR_EVENTS_LIST"}'

Response:

{
  "data": {
    "tool_slug": "GOOGLECALENDAR_EVENTS_LIST",
    "description": "Returns events on the specified calendar.",
    "input_parameters": {
      "properties": {
        "timeMin": {"type": "string", "description": "RFC3339 timestamp..."},
        "timeMax": {"type": "string", "description": "RFC3339 timestamp..."},
        "calendarId": {"type": "string", "default": "primary"}
      },
      "required": ["calendarId"]
    }
  },
  "error": null
}

3. Execute a Tool

Execute a Composio tool. Key name is arguments, not params.

curl -s -X POST $GATEWAY/internal/execute \
  -H "Content-Type: application/json" \
  -d '{"tool": "GMAIL_SEND_EMAIL", "arguments": {"to": "[email protected]", "subject": "Hi", "body": "Hello!"}}'

On success:

{"data": {"messages": [...]}, "error": null}

On failure — includes tool_schema so you can self-correct:

{
  "data": null,
  "error": "Missing required parameter: calendarId",
  "tool_schema": {
    "tool_slug": "GOOGLECALENDAR_EVENTS_LIST",
    "description": "...",
    "input_parameters": {"properties": {...}, "required": [...]}
  }
}

4. List User's Connections (and confirm OAuth completion)

# Optional toolkit filter: oauth_completed_active only turns true
# when that toolkit status is ACTIVE.
curl -s "$GATEWAY/internal/connections?toolkit=gmail"

Response includes:

  • connections: current deduplicated connection list
  • oauthcompletedactive: boolean, true only when OAuth completion is observed as ACTIVE

Cache invalidation is triggered only after ACTIVE is observed, and it targets the user's instance (fly-force-instance-id=<user containerid from usermapping>), not composio-gateway's own instance.

5. Initiate New Connection

curl -s -X POST $GATEWAY/api/connect \
  -H "Content-Type: application/json" \
  -d '{"toolkit": "gmail"}'

Returns connect_url for the user to complete OAuth.

Zero-friction connect (MANDATORY when acting)

Trigger only when the user wants to perform an action in an external app (send/read mail, create calendar event, open GitHub issue, post, …) and that app is not connected yet.

Do not trigger for pure Q&A / explanations ("what is Gmail?", "how does Calendar work?").

  1. Do not narrate connection checks, empty lists, OAuth mechanics, or ask "want me to connect?".
  2. On Web (active SSE session): call the built-in tool for the one primary toolkit:

`` frontendaction(actiontype="composioconnect", toolkit="gmail") ` Optional: displayname="Gmail", title=..., description=.... Use the correct lowercase toolkit slug. Do not paste connecturl` markdown links — the frontend renders a Connect card from the actionrequest.

  1. Visible reply: one short line about what you will do after they connect (no emoji chrome, no authorize URLs).
  2. Stop and wait for the user to finish OAuth / Done, then continue the original task.
  3. Never send the user to dig through the Connections page first.
  4. Fallback (no SSE / non-Web channel, or frontend_action fails):

``bash curl -s -X POST $GATEWAY/api/connect \ -H "Content-Type: application/json" \ -d '{"toolkit": "gmail"}' ` Then give the bare connect_url` as plain text (still no markdown card soft-match required). If that also fails, say so in one short line and stop — do not invent alternate setup flows.

6. Disconnect

curl -s -X DELETE $GATEWAY/api/connections/{connection_id}

Instagram Posting (important slug mapping)

Composio search may return legacy Instagram slugs that are not executable in this environment. When posting to Instagram, use these working slugs:

  1. Create draft container:
  • INSTAGRAMCREATEMEDIA_CONTAINER
  • Required: iguserid
  • Typical args for photo: {"iguserid":"...","imageurl":"https://...","contenttype":"photo","caption":"..."}
  1. Publish draft:
  • INSTAGRAMCREATEPOST
  • Required: iguserid, creation_id

Two-step flow:

  • Execute INSTAGRAMCREATEMEDIACONTAINER → read data.data.id as creationid
  • Execute INSTAGRAMCREATEPOST with that creation_id

Tip: If /internal/search suggests INSTAGRAMPOSTIGUSERMEDIA or INSTAGRAMPOSTIGUSERMEDIA_PUBLISH but execute returns "Tool ... not found", switch to the two slugs above.

Browserbase — Hybrid Workflow (Session Management + Playwright CDP)

Composio's Browserbase tools ONLY manage session lifecycle (open/close/list). They do NOT control web pages.

To actually operate a browser (navigate, click, fill forms, scrape data), use Playwright connectovercdp to connect to the session's WebSocket URL.

Step 1: Create a Browserbase Session via Composio

curl -s -X POST $GATEWAY/internal/execute \
  -H "Content-Type: application/json" \
  -d '{"tool": "BROWSERBASE_TOOL_SESSIONS_CREATE", "arguments": {"projectId": "YOUR_PROJECT_ID"}}'

Response includes id (session_id), status, and timestamps.

Step 2: Build the CDP WebSocket URL

import os
session_id = "<session_id from step 1>"
api_key = os.environ.get("BROWSERBASE_API_KEY")  # stored in workspace/.env
cdp_url = f"wss://connect.browserbase.com?apiKey={api_key}&sessionId={session_id}"

Step 3: Control the Browser with Playwright

from playwright.async_api import async_playwright

async with async_playwright() as p:
    browser = await p.chromium.connect_over_cdp(cdp_url)
    page = await browser.new_page()
    await page.goto("https://example.com")

    # Click, fill, screenshot — full Playwright API
    await page.click("button.submit")
    await page.fill("input[name='email']", "[email protected]")
    await page.screenshot(path="result.png")

    content = await page.content()

Step 4: Delete the Session (IMPORTANT — stops billing)

curl -s -X POST $GATEWAY/internal/execute \
  -H "Content-Type: application/json" \
  -d '{"tool": "BROWSERBASE_TOOL_SESSIONS_DELETE", "arguments": {"id": "YOUR_SESSION_ID"}}'

Key Concepts

Aspect Detail
Composio role Session lifecycle only — create, list, delete sessions
Playwright role Page control — navigate, click, fill, scrape, screenshot
Memory cost ~30-50MB locally (Playwright client only); Chromium runs on Browserbase servers
Anti-detection Browserbase handles it server-side — fingerprint masking, captcha solving, Cloudflare bypass. Playwright client does nothing special.
Billing Per-minute (rounded up). Always delete sessions when done.

Full Example Script (Create → Control → Delete)

#!/usr/bin/env python3
"""Browserbase: create session → control with Playwright → clean up."""
import asyncio, os, requests
from playwright.async_api import async_playwright

GATEWAY = "http://composio-gateway.flycast"
PROJECT_ID = os.environ.get("BROWSERBASE_PROJECT_ID")

async def main():
    # 1. Create session via Composio
    resp = requests.post(f"{GATEWAY}/internal/execute", json={
        "tool": "BROWSERBASE_TOOL_SESSIONS_CREATE",
        "arguments": {"projectId": PROJECT_ID}
    }).json()
    session_id = resp["data"]["id"]
    print(f"Session created: {session_id}")

    try:
        # 2. Connect via CDP
        api_key = os.environ["BROWSERBASE_API_KEY"]
        cdp_url = f"wss://connect.browserbase.com?apiKey={api_key}&sessionId={session_id}"

        async with async_playwright() as p:
            browser = await p.chromium.connect_over_cdp(cdp_url)
            page = await browser.new_page()
            await page.goto("https://example.com")
            title = await page.title()
            print(f"Page title: {title}")
            await browser.close()

    finally:
        # 3. Always delete session to stop billing
        requests.post(f"{GATEWAY}/internal/execute", json={
            "tool": "BROWSERBASE_TOOL_SESSIONS_DELETE",
            "arguments": {"id": session_id}
        })
        print("Session deleted")

asyncio.run(main())

Available Browserbase Tools via Composio

Tool Slug Purpose Key Arguments
BROWSERBASETOOLSESSIONS_CREATE Create a browser session projectId
BROWSERBASETOOLSESSIONS_DELETE Delete a session id
BROWSERBASETOOLSESSIONS_GET Get session info id
BROWSERBASETOOLSESSIONS_LIST List all sessions (none)
BROWSERBASETOOLSESSIONSGETDEBUG_INFO Get debug info id
BROWSERBASETOOLSESSIONS_STOP Stop a session id
BROWSERBASETOOLCONTEXTS_CREATE Create persistent context projectId
BROWSERBASETOOLCONTEXTS_DELETE Delete context id
BROWSERBASETOOLCONTEXTS_GET Get context info id
BROWSERBASETOOLCONTEXTS_LIST List contexts (none)
BROWSERBASETOOLCONTEXTS_UPDATE Update context labels id, labels
BROWSERBASETOOLUPLOADS_CREATE Upload file to session projectId, file data
BROWSERBASETOOLUPLOADS_GET Get upload info id
BROWSERBASETOOLUPLOADS_LIST List uploads (none)
BROWSERBASETOOLUPLOADS_DELETE Delete upload id
BROWSERBASETOOLDOWNLOADS_LIST List downloads sessionId
BROWSERBASETOOLDOWNLOADS_GET Get download downloadId
BROWSERBASETOOLDOWNLOADSGETSTREAM Stream download downloadId
BROWSERBASETOOLKBGETKNOWLEDGE Get KB article id

Browserbase / Browser Tool troubleshooting

If Browserbase is connected but execution fails, check naming mismatches across connection toolkit vs tool slug:

  • Connection may appear as toolkit browserbase_tool
  • Search may return tool slugs like BROWSERTOOLCREATE_TASK
  • Execute may still reject that slug (Tool ... not found) and only resolve legacy slugs under toolkit browserbase

Quick diagnosis:

# 1) Health + active connections
curl -s $GATEWAY/health
curl -s $GATEWAY/internal/connections

# 2) Search browser tool slugs
curl -s -X POST $GATEWAY/internal/search \
  -H "Content-Type: application/json" \
  -d '{"query":"browserbase create task"}'

# 3) Try execute and inspect exact error
curl -s -X POST $GATEWAY/internal/execute \
  -H "Content-Type: application/json" \
  -d '{"tool":"BROWSER_TOOL_CREATE_TASK","arguments":{"task":"open https://example.com"}}'

If error says No active connection found for toolkit 'browserbase', gateway should normalize Browserbase aliases server-side (browser/browserbase/browserbasetool) and normalize execute slug variants (BROWSERBASETOOLBROWSERTOOL) so both old/new clients work with a browserbasetool active connection.

Optimal Workflow (minimize tool calls)

Known tool → Direct execute (1 call)

If you already know the tool slug and parameters from previous use or the Common Tools table below, skip search entirely:

curl -s -X POST $GATEWAY/internal/execute \
  -H "Content-Type: application/json" \
  -d '{"tool": "GOOGLECALENDAR_EVENTS_LIST", "arguments": {"calendarId": "primary", "timeMin": "2026-04-02T00:00:00+08:00", "timeMax": "2026-04-09T00:00:00+08:00", "singleEvents": true, "timeZone": "Asia/Hong_Kong"}}'

Unknown tool → Search + Schema + Execute (2-3 calls)

  1. Search (compact) → pick the right tool slug
  2. Get schema (if param details unclear) → know exact argument format
  3. Execute → with correct arguments

If execute fails, the error response includes the full schema — so you can retry immediately without an extra schema call.

Wrap in a script for repeat use

For recurring queries, write a one-shot Python script:

#!/usr/bin/env python3
import sys, json, requests
from datetime import datetime, timedelta, timezone

GATEWAY = "http://composio-gateway.flycast"
days = int(sys.argv[1]) if len(sys.argv) > 1 else 7
tz_name = sys.argv[2] if len(sys.argv) > 2 else "UTC"

# ... build timeMin/timeMax ...
resp = requests.post(f"{GATEWAY}/internal/execute", json={
    "tool": "GOOGLECALENDAR_EVENTS_LIST",
    "arguments": {"calendarId": "primary", "timeMin": t_min, "timeMax": t_max,
                   "singleEvents": True, "timeZone": tz_name}
}).json()

# ... format and print ...

Then future calls are just: bash("python3 scripts/calendarevents.py 7 Asia/HongKong")1 tool call.

Common Tools Quick Reference (skip search for these)

📧 Gmail

Tool Slug Purpose Key Arguments
GMAILSENDEMAIL Send email to, subject, body, cc, bcc
GMAILFETCHEMAILS Fetch emails maxresults (int), labelids (list), q (Gmail search syntax)
GMAILCREATEEMAIL_DRAFT Create draft to, subject, body

Gmail Usage Examples:

# Send email
curl -s -X POST $GATEWAY/internal/execute \
  -H "Content-Type: application/json" \
  -d '{"tool": "GMAIL_SEND_EMAIL", "arguments": {"to": "[email protected]", "subject": "Hello", "body": "Hi there!"}}'

# Fetch last 5 emails
curl -s -X POST $GATEWAY/internal/execute \
  -H "Content-Type: application/json" \
  -d '{"tool": "GMAIL_FETCH_EMAILS", "arguments": {"max_results": 5}}'

# Search specific emails (using Gmail search syntax)
curl -s -X POST $GATEWAY/internal/execute \
  -H "Content-Type: application/json" \
  -d '{"tool": "GMAIL_FETCH_EMAILS", "arguments": {"max_results": 10, "q": "from:github.com after:2026/03/01"}}'

Gmail Response Parsing: Email data is in data.data.messages[], each email has id, snippet, payload.headers[] (From/Subject/Date are in headers, lookup by name).

🐦 Twitter

Tool Slug Purpose Key Arguments
TWITTERCREATIONOFAPOST Create post text (required), mediamediaids, replyinreplytotweet_id
TWITTERPOSTDELETEBYPOST_ID Delete post id
TWITTERPOSTLOOKUPBYPOST_ID Get single tweet id, tweet_fields
TWITTERRECENTSEARCH Search last 7 days query, max_results (min 10)
TWITTERUSERLOOKUP_ME Get own profile (no params)
TWITTERUSERLOOKUPBYUSERNAME Get user profile username

Twitter Usage Examples:

# Post tweet
curl -s -X POST $GATEWAY/internal/execute \
  -H "Content-Type: application/json" \
  -d '{"tool": "TWITTER_CREATION_OF_A_POST", "arguments": {"text": "Hello from Composio!"}}'

# Delete tweet
curl -s -X POST $GATEWAY/internal/execute \
  -H "Content-Type: application/json" \
  -d '{"tool": "TWITTER_POST_DELETE_BY_POST_ID", "arguments": {"id": "2039756730192601584"}}'

Twitter Response Structure: Post/create returns data.data.data (3-level nesting), contains id, text, edithistorytweet_ids.

Twitter — Post with Image (FileUploadable flow)

Key constraint: the gateway's /internal/execute is a thin wrapper over Composio v2 actions/{slug}/execute — it does NOT support version pinning or FileUploadable synthesis. Twitter media upload tools (TWITTERUPLOADMEDIA, TWITTERUPLOADLARGEMEDIA) require both, so they MUST be called via the composioclient Python SDK directly, not via gateway.

The gateway is intentionally generic — keep all per-tool flows (like this one) here in the skill.

3-step flow (proven working):

import hashlib, httpx, json
from pathlib import Path
from composio_client import Composio

# COMPOSIO_API_KEY: read from /data/workspace/composio-gateway/.env
# (gateway owns the key; for skill scripts, source it the same way)
client = Composio(api_key=COMPOSIO_API_KEY)

USER_ID = f"starchild-{user_id}"   # NOTE: hyphen, not underscore
img = Path("output/images/foo.jpg")

# 1. Get presigned S3 upload URL
md5 = hashlib.md5(img.read_bytes()).hexdigest()
presigned = client.files.create_presigned_url(
    filename=img.name, md5=md5, mimetype="image/jpeg",
    tool_slug="TWITTER_UPLOAD_MEDIA", toolkit_slug="twitter",
)
# presigned.type == "new" → file is new, must PUT
# presigned.type == "existing" → cached, skip PUT
if presigned.type == "new":
    httpx.put(presigned.new_presigned_url, content=img.read_bytes(),
              headers={"Content-Type": "image/jpeg"}, timeout=60).raise_for_status()

# 2. Execute upload tool — MUST pass version="20260501_00" (or current latest)
#    media is a FileUploadable dict, NOT base64
upload_resp = client.tools.execute(
    tool_slug="TWITTER_UPLOAD_MEDIA",
    user_id=USER_ID,
    version="20260501_00",
    arguments={
        "media": {"name": img.name, "mimetype": "image/jpeg", "s3key": presigned.key},
        "media_type": "image/jpeg",
        "media_category": "tweet_image",   # or "dm_image", "subtitles"
    },
)
result = upload_resp.model_dump()
assert result["successful"], result["error"]
# Response nesting: data.data.id (NOT data.id, NOT data.media_id_string)
media_id = result["data"]["data"]["id"]

# 3. Create tweet with media_media_ids — this one is fine via gateway too
tweet_resp = client.tools.execute(
    tool_slug="TWITTER_CREATION_OF_A_POST",
    user_id=USER_ID,
    arguments={"text": "your tweet text", "media_media_ids": [str(media_id)]},
)
tweet_id = tweet_resp.model_dump()["data"]["data"]["id"]
url = f"https://x.com/i/web/status/{tweet_id}"

Why this works (debugging notes — don't lose this knowledge):

  • GET /api/v3/tools/TWITTERUPLOADMEDIA returns 404 without a version because it lives in toolkit version 2026050100+, not the default 0000000000.
  • client.tools.execute(version=...) routes through /api/v3/tools/execute/{slug} which IS version-aware.
  • Gateway uses v2 /api/v2/actions/{slug}/execute for execute — v2 has no version routing, so it can never reach versioned tools. Don't try to "fix" the gateway for this — adding version + FileUploadable would bloat it. Keep it thin.
  • The media param expects {name, mimetype, s3key} (FileUploadable schema), NOT base64. Passing base64 returns: "Input should be a valid dictionary or instance of FileUploadable on parameter media".
  • File size limit for TWITTERUPLOADMEDIA is ~5 MB. For larger files / videos / GIFs, use TWITTERUPLOADLARGE_MEDIA (chunked, same flow but additional segment params).

⚠️ Twitter Limitations & Fallback:

  • TWITTERRECENTSEARCH only covers last 7 days, older tweets won't appear
  • TWITTERFULLARCHIVE_SEARCH requires Twitter API Pro access, regular OAuth App can't use it
  • When fetching user tweet history, prefer platform native tool twitterusertweets, not limited to 7 days

📅 Google Calendar

Tool Slug Purpose Key Arguments
GOOGLECALENDAREVENTSLIST List events calendarId (default: "primary"), timeMin, timeMax (RFC3339+tz), singleEvents (true), timeZone
GOOGLECALENDARCREATEEVENT Create event calendarId, summary, start, end, description, attendees
GOOGLECALENDARDELETEEVENT Delete event calendarId, eventId

🐙 GitHub

Tool Slug Purpose Key Arguments
GITHUBCREATEAN_ISSUE Create issue owner, repo, title, body, labels, assignees
GITHUBLISTREPOSITORY_ISSUES List issues owner, repo, sort, state (open/closed/all), page, per_page
GITHUBGETAN_ISSUE Get issue detail owner, repo, issue_number
GITHUBCREATEAPULLREQUEST Create PR owner, repo, title, head, base, body, draft
GITHUBLISTPULL_REQUESTS List PRs owner, repo, state, sort, head, base
GITHUBMERGEAPULLREQUEST Merge PR owner, repo, pullnumber, committitle, sha
GITHUBGETA_REPOSITORY Get repo info owner, repo
GITHUBSEARCHCODE Search code q (GitHub search syntax), sort, order, per_page
GITHUBGETREPOSITORY_CONTENT Get file content owner, repo, path, ref
# Create issue
curl -s -X POST $GATEWAY/internal/execute \
  -H "Content-Type: application/json" \
  -d '{"tool": "GITHUB_CREATE_AN_ISSUE", "arguments": {"owner": "myorg", "repo": "myrepo", "title": "Bug: login fails", "body": "Steps to reproduce..."}}'

# List open issues
curl -s -X POST $GATEWAY/internal/execute \
  -H "Content-Type: application/json" \
  -d '{"tool": "GITHUB_LIST_REPOSITORY_ISSUES", "arguments": {"owner": "myorg", "repo": "myrepo", "state": "open", "per_page": 10}}'

📝 Notion

Tool Slug Purpose Key Arguments
NOTIONCREATENOTION_PAGE Create page parent_id, title, markdown, icon, cover
NOTIONSEARCHNOTION_PAGE Search pages/DBs query, filtervalue (page/database), pagesize
NOTIONQUERYDATABASEWITHFILTER Query DB rows databaseid, filter, sorts, pagesize
NOTIONINSERTROW_DATABASE Add DB row database_id, properties
NOTIONUPDATEROW_DATABASE Update DB row row_id, properties, icon, cover
NOTIONFETCHDATABASE Get DB schema database_id
NOTIONFETCHBLOCK_CONTENTS Get page content blockid (= pageid)
NOTIONADDMULTIPLEPAGECONTENT Add blocks parentblockid, content_blocks, after
NOTIONUPDATEPAGE Update page props page_id, properties, icon, cover, archived
NOTIONDELETEBLOCK Delete/archive block block_id
# Search pages
curl -s -X POST $GATEWAY/internal/execute \
  -H "Content-Type: application/json" \
  -d '{"tool": "NOTION_SEARCH_NOTION_PAGE", "arguments": {"query": "Meeting Notes", "page_size": 5}}'

# Query database with filter
curl -s -X POST $GATEWAY/internal/execute \
  -H "Content-Type: application/json" \
  -d '{"tool": "NOTION_QUERY_DATABASE_WITH_FILTER", "arguments": {"database_id": "abc123", "filter": {"property": "Status", "select": {"equals": "In Progress"}}, "page_size": 10}}'

📁 Google Drive

Tool Slug Purpose Key Arguments
GOOGLEDRIVECREATEFILEFROMTEXT Create file filename, textcontent, mimetype, parentid
GOOGLEDRIVEFINDFILE Search files q (Drive search syntax), fields, spaces
GOOGLEDRIVEDOWNLOADFILE Download file fileId, mime_type
GOOGLEDRIVECOPYFILE Copy file fileId
GOOGLEDRIVEADDFILESHARINGPREFERENCE Share file fileId, role, type, emailAddress
# Search files by name
curl -s -X POST $GATEWAY/internal/execute \
  -H "Content-Type: application/json" \
  -d '{"tool": "GOOGLEDRIVE_FIND_FILE", "arguments": {"q": "name contains '\''report'\'' and mimeType != '\''application/vnd.google-apps.folder'\''"}}'

# Create text file
curl -s -X POST $GATEWAY/internal/execute \
  -H "Content-Type: application/json" \
  -d '{"tool": "GOOGLEDRIVE_CREATE_FILE_FROM_TEXT", "arguments": {"file_name": "notes.txt", "text_content": "Hello World"}}'

Google Drive Search Syntax (q param): name contains 'keyword', mimeType = 'application/vnd.google-apps.folder' (folders), '<folderId>' in parents (files in folder), modifiedTime > '2026-01-01'.

📄 Google Docs

Tool Slug Purpose Key Arguments
GOOGLEDOCSCREATEDOCUMENT_MARKDOWN Create doc from markdown title, markdown_text
GOOGLEDOCSGETDOCUMENT_PLAINTEXT Get doc as text documentid, includetables, include_headers
GOOGLEDOCSGETDOCUMENTBYID Get raw doc object id
# Create doc with markdown content
curl -s -X POST $GATEWAY/internal/execute \
  -H "Content-Type: application/json" \
  -d '{"tool": "GOOGLEDOCS_CREATE_DOCUMENT_MARKDOWN", "arguments": {"title": "Meeting Notes", "markdown_text": "# Q2 Planning\n\n- Item 1\n- Item 2"}}'

# Read doc as plain text
curl -s -X POST $GATEWAY/internal/execute \
  -H "Content-Type: application/json" \
  -d '{"tool": "GOOGLEDOCS_GET_DOCUMENT_PLAINTEXT", "arguments": {"document_id": "1abc...xyz"}}'

📊 Google Sheets

Tool Slug Purpose Key Arguments
GOOGLESHEETSCREATEGOOGLE_SHEET1 Create spreadsheet title
GOOGLESHEETSGETSHEET_NAMES List sheets in spreadsheet spreadsheetid, excludehidden
GOOGLESHEETSBATCHGET Read cell values spreadsheet_id, ranges (list, A1 notation), majorDimension, valueRenderOption
GOOGLESHEETSUPDATEVALUES_BATCH Write cell values spreadsheet_id, data (list of {range, values}), valueInputOption
GOOGLESHEETSSPREADSHEETSVALUES_APPEND Append rows spreadsheetId, range, values, valueInputOption, insertDataOption
GOOGLESHEETSSPREADSHEETSVALUESBATCHCLEAR Clear ranges spreadsheet_id, ranges
GOOGLESHEETSGETSPREADSHEET_INFO Get full spreadsheet metadata spreadsheet_id
GOOGLESHEETSUPDATESHEET_PROPERTIES Update sheet props spreadsheetid, sheetid, title, index
# Read cells
curl -s -X POST $GATEWAY/internal/execute \
  -H "Content-Type: application/json" \
  -d '{"tool": "GOOGLESHEETS_BATCH_GET", "arguments": {"spreadsheet_id": "1abc...xyz", "ranges": ["Sheet1!A1:D10"]}}'

# Write cells
curl -s -X POST $GATEWAY/internal/execute \
  -H "Content-Type: application/json" \
  -d '{"tool": "GOOGLESHEETS_UPDATE_VALUES_BATCH", "arguments": {"spreadsheet_id": "1abc...xyz", "valueInputOption": "USER_ENTERED", "data": [{"range": "Sheet1!A1:B2", "values": [["Name", "Score"], ["Alice", 95]]}]}}'

# Append rows
curl -s -X POST $GATEWAY/internal/execute \
  -H "Content-Type: application/json" \
  -d '{"tool": "GOOGLESHEETS_SPREADSHEETS_VALUES_APPEND", "arguments": {"spreadsheetId": "1abc...xyz", "range": "Sheet1!A:B", "valueInputOption": "USER_ENTERED", "values": [["Bob", 88], ["Charlie", 92]]}}'

⚠️ Google Sheets Notes:

  • valueInputOption: "USER_ENTERED" (parses formulas/numbers) or "RAW" (literal text)
  • ranges uses A1 notation: "Sheet1!A1:D10", "Sheet1!A:A" (entire column)
  • BATCH_GET returns data.data.valueRanges[].values (2D array)
  • spreadsheetId vs spreadsheetid: some tools use camelCase, some snakecase — check schema if unsure

Important Notes

  • Tool slugs are UPPERCASE: GMAILSENDEMAIL
  • Toolkit slugs are lowercase: gmail, github
  • Arguments key: always use "arguments", never "params"params silently gets ignored
  • Time parameters: use RFC3339 with timezone offset (2026-04-08T00:00:00+08:00), not UTC unless intended
  • OAuth tokens are managed by Composio — auto-refreshed on expiry
  • Response nesting: Composio execute response is usually data.data, but Twitter is data.data.data (3 levels). Parse by recursively accessing data.
  • Native tool fallback: When Composio tools have limitations (e.g., Twitter search only 7 days), prefer platform built-in native tools (e.g., twitterusertweets)

Common Issues

Browserbase connection name mismatch

If /internal/connections shows toolkit browserbasetool as ACTIVE, but executing BROWSERTOOL* returns "No active connection found for toolkit 'browser'", this is a gateway-side toolkit alias mismatch (browserbasetool vs browser).

What to do:

  1. For session management tools (SESSIONS, CONTEXTS, UPLOADS, etc.), the gateway should normalize Browserbase aliases server-side. If it doesn't, try both BROWSERTOOL and BROWSERBASETOOL_* slugs.
  2. For actual browser control (navigate, click, fill, scrape), do NOT use Composio execute — use Playwright connectovercdp as described in the Browserbase section above. Composio tools only manage sessions, not page interactions.

Gmail Nested JSON Parsing

Gmail returns complex JSON structure with multiple levels of HTML content. Do not try to parse nested strings with json.loads. Access directly as dict in Python — gateway already returns parsed JSON.