Call a Composio Action
Run any Composio action against the tenant's connected accounts via vendo.data.execute.
vendo.data.execute(action, args) runs any Composio action against the tenant's connected accounts. The proxy resolves the connected account, calls Composio, bills via the composio.action_call meter, and returns the result.
Vendo mode only. Throws VendoOnlyFeature in OSS mode. The Composio adapter lives on its own subdomain (composio-proxy.vendo.run, registered in proxy/src/adapters/index.ts) — the SDK targets it directly.
The action's toolkit must have a connection bound on this deployment with the
composio_managedprofile. Connections bound through a non-Composio path (e.g.oauth_app_installfor Notion) don't satisfydata.execute— the proxy will returnbinding_missingand the SDK raisesNotConnected.
Basic usage
import vendo
from vendo import Vendo
from vendo.errors import NotConnected, UpstreamError
client = Vendo()
def send_gmail(to: str, subject: str, body: str):
try:
return client.data.execute(
"GMAIL_SEND_EMAIL",
{"recipient_email": to, "subject": subject, "body": body},
)
except NotConnected as e:
# User hasn't connected Gmail — point them at the connect URL.
slug = e.slug or "gmail"
return {"needs_connection": slug, "connect_url": vendo.connect_url(slug)}
except UpstreamError as e:
# Composio or Gmail returned an error
return {"error": str(e)}import { Vendo, NotConnected, UpstreamError } from "@vendodev/sdk";
const vendo = new Vendo();
async function sendGmail(to: string, subject: string, body: string) {
try {
return await vendo.data.execute("GMAIL_SEND_EMAIL", {
recipient_email: to,
subject,
body,
});
} catch (e) {
if (e instanceof NotConnected) {
const slug = e.slug ?? "gmail";
return {
needsConnection: slug,
connectUrl: vendo.connectUrl(slug),
};
}
if (e instanceof UpstreamError) {
return { error: e.message };
}
throw e;
}
}How the proxy resolves the connected account
When you call data.execute("GMAIL_SEND_EMAIL", args):
- The SDK POSTs to
https://composio-proxy.vendo.run/v1/executewith yourVENDO_API_KEYbearer token, body{action_id, args}. - The proxy resolves the action name to a Composio toolkit (e.g.
GMAIL_*maps to the Gmail toolkit). - It looks up the
composio_managedconnection for the toolkit on your deployment's binding. - It calls Composio with the resolved account and your
args. - Billing ticks one
composio.action_callunit. - The proxy wraps the Composio response as
{"data": ...}; the SDK unwraps so callers receive the inner payload directly.
If the toolkit has no composio_managed connection on this deployment, the proxy returns HTTP 403 with {"error": "binding_missing", "toolkit": "<slug>"} and the SDK raises NotConnected.
Finding action names
Action names follow the pattern <TOOLKIT>_<ACTION> in SCREAMING_SNAKE_CASE, matching Composio's catalog. Examples:
| Toolkit | Sample action |
|---|---|
| Gmail | GMAIL_SEND_EMAIL |
| Supabase | SUPABASE_BETA_RUN_SQL_QUERY |
| GitHub | GITHUB_CREATE_AN_ISSUE |
| Notion | NOTION_CREATE_PAGE |
| Linear | LINEAR_CREATE_ISSUE |
The full, authoritative list lives at composio.dev — browse by toolkit and copy the action id verbatim. Composio occasionally renames actions; if you start seeing composio_error 4xx responses after an upgrade, re-check the action id against the live catalog. The proxy is a pass-through — Vendo doesn't curate the action catalog.
Notes
argsis passed through to Composio without transformation. Check the Composio docs for each action's required fields.NotConnected.slugis the provider slug the binding is missing for. It's optional on the error type — fall back to a known slug if it's absent.data.executeis for Composio-backed actions only. To read connection metadata or credentials, useconnections.getinstead.