composio

Universal tool gateway via Composio — connect to 1000+ external apps (Gmail, Slack, GitHub, Google Calendar, Notion, etc.) through the Composio Gateway. Use…

INSTALLATION
npx skills add https://github.com/starchild-ai-agent/official-skills --skill composio
Run in your project or agent environment. Adjust flags if your CLI version differs.

SKILL.md

$27

Gateway Base URL

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

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

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": "x@example.com", "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
  • oauth_completed_active: 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 container_id from user_mapping>), 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.

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:

  • Create draft container:
  • INSTAGRAM_CREATE_MEDIA_CONTAINER
  • Required: ig_user_id
  • Typical args for photo: {"ig_user_id":"...","image_url":"https://...","content_type":"photo","caption":"..."}
  • Publish draft:
  • INSTAGRAM_CREATE_POST
  • Required: ig_user_id, creation_id

Two-step flow:

  • Execute INSTAGRAM_CREATE_MEDIA_CONTAINER → read data.data.id as creation_id
  • Execute INSTAGRAM_CREATE_POST with that creation_id

Tip: If /internal/search suggests INSTAGRAM_POST_IG_USER_MEDIA or INSTAGRAM_POST_IG_USER_MEDIA_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 connect_over_cdp** 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}&#x26;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']", "user@test.com")

    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}&#x26;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

BROWSERBASE_TOOL_SESSIONS_CREATE

Create a browser session

projectId

BROWSERBASE_TOOL_SESSIONS_DELETE

Delete a session

id

BROWSERBASE_TOOL_SESSIONS_GET

Get session info

id

BROWSERBASE_TOOL_SESSIONS_LIST

List all sessions

(none)

BROWSERBASE_TOOL_SESSIONS_GET_DEBUG_INFO

Get debug info

id

BROWSERBASE_TOOL_SESSIONS_STOP

Stop a session

id

BROWSERBASE_TOOL_CONTEXTS_CREATE

Create persistent context

projectId

BROWSERBASE_TOOL_CONTEXTS_DELETE

Delete context

id

BROWSERBASE_TOOL_CONTEXTS_GET

Get context info

id

BROWSERBASE_TOOL_CONTEXTS_LIST

List contexts

(none)

BROWSERBASE_TOOL_CONTEXTS_UPDATE

Update context labels

id, labels

BROWSERBASE_TOOL_UPLOADS_CREATE

Upload file to session

projectId, file data

BROWSERBASE_TOOL_UPLOADS_GET

Get upload info

id

BROWSERBASE_TOOL_UPLOADS_LIST

List uploads

(none)

BROWSERBASE_TOOL_UPLOADS_DELETE

Delete upload

id

BROWSERBASE_TOOL_DOWNLOADS_LIST

List downloads

sessionId

BROWSERBASE_TOOL_DOWNLOADS_GET

Get download

downloadId

BROWSERBASE_TOOL_DOWNLOADS_GET_STREAM

Stream download

downloadId

BROWSERBASE_TOOL_KB_GET_KNOWLEDGE

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 BROWSER_TOOL_CREATE_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/browserbase_tool) and normalize execute slug variants (BROWSERBASE_TOOL_*BROWSER_TOOL_*) so both old/new clients work with a browserbase_tool 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)

  • Search (compact) → pick the right tool slug
  • Get schema (if param details unclear) → know exact argument format
  • 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/calendar_events.py 7 Asia/Hong_Kong")1 tool call.

Common Tools Quick Reference (skip search for these)

📧 Gmail

Tool Slug

Purpose

Key Arguments

GMAIL_SEND_EMAIL

Send email

to, subject, body, cc, bcc

GMAIL_FETCH_EMAILS

Fetch emails

max_results (int), label_ids (list), q (Gmail search syntax)

GMAIL_CREATE_EMAIL_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": "user@example.com", "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

TWITTER_CREATION_OF_A_POST

Create post

text (required), media_media_ids, reply_in_reply_to_tweet_id

TWITTER_POST_DELETE_BY_POST_ID

Delete post

id

TWITTER_POST_LOOKUP_BY_POST_ID

Get single tweet

id, tweet_fields

TWITTER_RECENT_SEARCH

Search last 7 days

query, max_results (min 10)

TWITTER_USER_LOOKUP_ME

Get own profile

(no params)

TWITTER_USER_LOOKUP_BY_USERNAME

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, edit_history_tweet_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 (TWITTER_UPLOAD_MEDIA, TWITTER_UPLOAD_LARGE_MEDIA) require both, so they MUST be called via the composio_client 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/TWITTER_UPLOAD_MEDIA returns 404 without a version because it lives in toolkit version 20260501_00+, not the default 00000000_00.
  • 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 TWITTER_UPLOAD_MEDIA is ~5 MB. For larger files / videos / GIFs, use TWITTER_UPLOAD_LARGE_MEDIA (chunked, same flow but additional segment params).

⚠️ Twitter Limitations &#x26; Fallback:

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

📅 Google Calendar

Tool Slug

Purpose

Key Arguments

GOOGLECALENDAR_EVENTS_LIST

List events

calendarId (default: "primary"), timeMin, timeMax (RFC3339+tz), singleEvents (true), timeZone

GOOGLECALENDAR_CREATE_EVENT

Create event

calendarId, summary, start, end, description, attendees

GOOGLECALENDAR_DELETE_EVENT

Delete event

calendarId, eventId

🐙 GitHub

Tool Slug

Purpose

Key Arguments

GITHUB_CREATE_AN_ISSUE

Create issue

owner, repo, title, body, labels, assignees

GITHUB_LIST_REPOSITORY_ISSUES

List issues

owner, repo, sort, state (open/closed/all), page, per_page

GITHUB_GET_AN_ISSUE

Get issue detail

owner, repo, issue_number

GITHUB_CREATE_A_PULL_REQUEST

Create PR

owner, repo, title, head, base, body, draft

GITHUB_LIST_PULL_REQUESTS

List PRs

owner, repo, state, sort, head, base

GITHUB_MERGE_A_PULL_REQUEST

Merge PR

owner, repo, pull_number, commit_title, sha

GITHUB_GET_A_REPOSITORY

Get repo info

owner, repo

GITHUB_SEARCH_CODE

Search code

q (GitHub search syntax), sort, order, per_page

GITHUB_GET_REPOSITORY_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

NOTION_CREATE_NOTION_PAGE

Create page

parent_id, title, markdown, icon, cover

NOTION_SEARCH_NOTION_PAGE

Search pages/DBs

query, filter_value (page/database), page_size

NOTION_QUERY_DATABASE_WITH_FILTER

Query DB rows

database_id, filter, sorts, page_size

NOTION_INSERT_ROW_DATABASE

Add DB row

database_id, properties

NOTION_UPDATE_ROW_DATABASE

Update DB row

row_id, properties, icon, cover

NOTION_FETCH_DATABASE

Get DB schema

database_id

NOTION_FETCH_BLOCK_CONTENTS

Get page content

block_id (= page_id)

NOTION_ADD_MULTIPLE_PAGE_CONTENT

Add blocks

parent_block_id, content_blocks, after

NOTION_UPDATE_PAGE

Update page props

page_id, properties, icon, cover, archived

NOTION_DELETE_BLOCK

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

GOOGLEDRIVE_CREATE_FILE_FROM_TEXT

Create file

file_name, text_content, mime_type, parent_id

GOOGLEDRIVE_FIND_FILE

Search files

q (Drive search syntax), fields, spaces

GOOGLEDRIVE_DOWNLOAD_FILE

Download file

fileId, mime_type

GOOGLEDRIVE_COPY_FILE

Copy file

fileId

GOOGLEDRIVE_ADD_FILE_SHARING_PREFERENCE

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

GOOGLEDOCS_CREATE_DOCUMENT_MARKDOWN

Create doc from markdown

title, markdown_text

GOOGLEDOCS_GET_DOCUMENT_PLAINTEXT

Get doc as text

document_id, include_tables, include_headers

GOOGLEDOCS_GET_DOCUMENT_BY_ID

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

GOOGLESHEETS_CREATE_GOOGLE_SHEET1

Create spreadsheet

title

GOOGLESHEETS_GET_SHEET_NAMES

List sheets in spreadsheet

spreadsheet_id, exclude_hidden

GOOGLESHEETS_BATCH_GET

Read cell values

spreadsheet_id, ranges (list, A1 notation), majorDimension, valueRenderOption

GOOGLESHEETS_UPDATE_VALUES_BATCH

Write cell values

spreadsheet_id, data (list of {range, values}), valueInputOption

GOOGLESHEETS_SPREADSHEETS_VALUES_APPEND

Append rows

spreadsheetId, range, values, valueInputOption, insertDataOption

GOOGLESHEETS_SPREADSHEETS_VALUES_BATCH_CLEAR

Clear ranges

spreadsheet_id, ranges

GOOGLESHEETS_GET_SPREADSHEET_INFO

Get full spreadsheet metadata

spreadsheet_id

GOOGLESHEETS_UPDATE_SHEET_PROPERTIES

Update sheet props

spreadsheet_id, sheet_id, 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 spreadsheet_id: some tools use camelCase, some snake_case — check schema if unsure

Important Notes

  • Tool slugs are UPPERCASE: GMAIL_SEND_EMAIL
  • 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., twitter_user_tweets)

Common Issues

Browserbase connection name mismatch

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

What to do:

  • For session management tools (SESSIONS_*, CONTEXTS_*, UPLOADS_*, etc.), the gateway should normalize Browserbase aliases server-side. If it doesn't, try both BROWSER_TOOL_* and BROWSERBASE_TOOL_* slugs.
  • For actual browser control (navigate, click, fill, scrape), do NOT use Composio execute — use Playwright connect_over_cdp 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.

BrowserAct

Let your agent run on any real-world website

Bypass CAPTCHA & anti-bot for free. Start local, scale to cloud.

Explore BrowserAct Skills →

Stop writing automation&scrapers

Install the CLI. Run your first Skill in 30 seconds. Scale when you're ready.

Start free
free · no credit card