docs/content/changelog/01-29-26-sdk-major-update.mdx
@composio/core and all provider packages0.6.0+composio and all provider packages0.11.0+This major release focuses on Cloudflare Workers compatibility, platform-specific optimizations, and critical bug fixes. It includes breaking changes in both TypeScript and Python SDKs.
<Callout type="warn"> **Breaking Changes**: This release includes breaking changes that require code modifications:TypeScript:
await + new required params: id, timestamp)Python:
id, timestamp - still synchronous)See the Migration Guide for detailed upgrade instructions.
</Callout>TypeScript PR: #2436 | Python PR: #2361
The webhook verification API has breaking changes in both SDKs to support multiple webhook versions (v1, v2, v3) and edge runtime compatibility.
The verifyWebhook() method has two breaking changes: it's now async AND requires additional parameters.
Before:
// @noErrors
import { Composio } from '@composio/core';
const composio = new Composio();
// Old signature - synchronous, fewer parameters
const payload = composio.triggers.verifyWebhook({
payload: req.body.toString(),
signature: req.headers['x-composio-signature'] as string,
secret: process.env.COMPOSIO_WEBHOOK_SECRET!,
});
// Returns: TriggerEvent
After:
// @noErrors
import { Composio } from '@composio/core';
const composio = new Composio();
// New signature - async, requires id and timestamp
const result = await composio.triggers.verifyWebhook({
id: req.headers['webhook-id'] as string, // NEW: required
payload: req.body.toString(),
secret: process.env.COMPOSIO_WEBHOOK_SECRET!,
signature: req.headers['webhook-signature'] as string, // header name changed
timestamp: req.headers['webhook-timestamp'] as string, // NEW: required
tolerance: 300, // optional, default 300 seconds
});
// Returns: VerifyWebhookResult with version info
Key Changes:
awaitid (from webhook-id header)timestamp (from webhook-timestamp header)x-composio-signature → webhook-signatureTriggerEvent → VerifyWebhookResultWhy?
node:cryptoThe method signature changed to support new webhook versions and requires additional headers.
Before:
from composio import Composio
composio = Composio()
# Old signature - fewer parameters
event = composio.triggers.verify_webhook(
payload=request.get_data(as_text=True),
signature=request.headers.get('x-composio-signature'),
secret=os.environ['COMPOSIO_WEBHOOK_SECRET'],
)
# Returns: TriggerEvent
After:
from composio import Composio
composio = Composio()
# New signature - requires id and timestamp headers
result = composio.triggers.verify_webhook(
id=request.headers.get('webhook-id'), # NEW: required
payload=request.get_data(as_text=True),
secret=os.environ['COMPOSIO_WEBHOOK_SECRET'],
signature=request.headers.get('webhook-signature'), # header name changed
timestamp=request.headers.get('webhook-timestamp'), # NEW: required
tolerance=300, # optional
)
# Returns: VerifyWebhookResult with version info
Key Changes:
await needed)id (webhook-id header)timestamp (webhook-timestamp header)x-composio-signature → webhook-signatureTriggerEvent → VerifyWebhookResultparam=value)Why? Supports v1, v2, and v3 webhook formats with improved signature verification.
PR: #2433
The @composio/mastra provider has been updated to support Mastra v1, which has breaking API changes from v0.x.
// @noErrors
import { MastraProvider } from '@composio/mastra';
import { Mastra } from '@mastra/core';
const mastra = new Mastra({
/* v0 config */
});
const provider = new MastraProvider();
// v0 API usage
const tools = await provider.wrapTools(composioTools);
// @noErrors
import { MastraProvider } from '@composio/mastra';
import { Mastra } from '@mastra/core';
const mastra = new Mastra({
/* v1 config */
});
const provider = new MastraProvider({ mastra });
// v1 API - different tool format
const tools = await provider.wrapTools(composioTools);
npm install @mastra/core@latestPR: #2437
Separate implementations for Node.js and Cloudflare Workers to optimize file handling for each runtime.
Key Improvements:
PR: #2437
Different default configurations for different runtimes to optimize behavior.
Changed the autoUploadDownloadFiles default configuration for the Cloudflare Workers runtime, to avoid triggering runtime errors on unsupported features.
Cloudflare Workers defaults:
{
autoUploadDownloadFiles: false; // We don't currently support file uploads/downloads in Cloudflare Workers
}
Benefits:
PR: #2438
All node:buffer usage replaced with standard Uint8Array for universal compatibility.
Impact:
PR: #2518
Fixed issues when executing tools with version constraints.
// @noErrors
// This would fail or fetch wrong version
await composio.tools.execute('GITHUB_CREATE_ISSUE', {
userId: 'user_123',
version: '1.2.0', // Version constraint ignored
arguments: {
/* ... */
},
});
// @noErrors
// Now correctly respects version constraint
await composio.tools.execute('GITHUB_CREATE_ISSUE', {
userId: 'user_123',
version: '1.2.0', // Version constraint honored
arguments: {
/* ... */
},
});
Test Coverage: Added 174 new test cases to prevent regression.
PR: #2506
Fixed type coercion for stringified default values in JSON schemas.
# Schema with stringified default
{
"type": "integer",
"default": "42" # String instead of int
}
# Would cause runtime errors
# SDK automatically coerces to correct type
{
"type": "integer",
"default": 42 # Properly coerced to int
}
# Works correctly
Impact: Fixes runtime errors with tools that have stringified defaults in their schemas.
PR: #2426
Fixed SALESFORCE_CREATE_LEAD type error with custom_fields parameter.
# Would fail with type error
composio.tools.execute('SALESFORCE_CREATE_LEAD', {
'user_id': 'user_123',
'arguments': {
'custom_fields': {
'CustomField__c': 'value'
}
}
})
# Now works correctly
composio.tools.execute('SALESFORCE_CREATE_LEAD', {
'user_id': 'user_123',
'arguments': {
'custom_fields': {
'CustomField__c': 'value'
}
}
})
Technical Details: Implemented proper support for anyOf and allOf JSON Schema constructs in Python SDK.
<Tabs groupId="package-manager" items={['npm', 'pnpm', 'yarn', 'pip', 'uv']} persist>
<Tab value="npm">
bash npm update @composio/core@latest # Update provider packages if using them npm update @composio/openai@latest npm update @composio/anthropic@latest npm update @composio/vercel@latest # etc.
</Tab>
<Tab value="pnpm">
bash pnpm update @composio/core@latest # Update provider packages if using them pnpm update @composio/openai@latest pnpm update @composio/anthropic@latest pnpm update @composio/vercel@latest # etc.
</Tab>
<Tab value="yarn">
bash yarn upgrade @composio/core@latest # Update provider packages if using them yarn upgrade @composio/openai@latest yarn upgrade @composio/anthropic@latest yarn upgrade @composio/vercel@latest # etc.
</Tab>
<Tab value="pip">bash pip install --upgrade composio </Tab>
<Tab value="uv">bash uv pip install --upgrade composio </Tab>
</Tabs>
<Tabs groupId="language" items={['TypeScript', 'Python']} persist> <Tab value="TypeScript">
Update signature with new parameters and add await:
// @noErrors
// Before - old signature
const payload = composio.triggers.verifyWebhook({
payload: req.body.toString(),
signature: req.headers['x-composio-signature'] as string,
secret: process.env.COMPOSIO_WEBHOOK_SECRET!,
});
// After - new signature with await and additional parameters
const result = await composio.triggers.verifyWebhook({
id: req.headers['webhook-id'] as string, // NEW
payload: req.body.toString(),
secret: process.env.COMPOSIO_WEBHOOK_SECRET!,
signature: req.headers['webhook-signature'] as string, // header name changed
timestamp: req.headers['webhook-timestamp'] as string, // NEW
});
// Access the normalized payload and version info
const event = result.payload;
const version = result.version; // 'V1', 'V2', or 'V3'
Express.js Example:
// @noErrors
import express from 'express';
import { Composio } from '@composio/core';
const app = express();
const composio = new Composio();
// Make route handler async
app.post('/webhook', express.raw({ type: 'application/json' }), async (req, res) => {
try {
// New signature with all required headers
const result = await composio.triggers.verifyWebhook({
id: req.headers['webhook-id'] as string,
payload: req.body.toString(),
secret: process.env.COMPOSIO_WEBHOOK_SECRET!,
signature: req.headers['webhook-signature'] as string,
timestamp: req.headers['webhook-timestamp'] as string,
});
// Access verified webhook data
console.log('Webhook version:', result.version);
console.log('Received trigger:', result.payload.triggerSlug);
res.status(200).send('OK');
} catch (error) {
console.error('Webhook verification failed:', error);
res.status(401).send('Unauthorized');
}
});
Cloudflare Workers Example:
// @noErrors
import { Composio } from '@composio/core';
export default {
async fetch(request: Request, env: Env): Promise<Response> {
const composio = new Composio({ apiKey: env.COMPOSIO_API_KEY });
try {
const body = await request.text();
// Now works in Cloudflare Workers!
const result = await composio.triggers.verifyWebhook({
id: request.headers.get('webhook-id') || '',
payload: body,
secret: env.COMPOSIO_WEBHOOK_SECRET,
signature: request.headers.get('webhook-signature') || '',
timestamp: request.headers.get('webhook-timestamp') || '',
});
// Process verified webhook
console.log('Trigger:', result.payload.triggerSlug);
return new Response('OK', { status: 200 });
} catch (error) {
return new Response('Unauthorized', { status: 401 });
}
},
};
Update signature to include new required parameters:
# Before - old signature
event = composio.triggers.verify_webhook(
payload=request.get_data(as_text=True),
signature=request.headers.get('x-composio-signature'),
secret=os.environ['COMPOSIO_WEBHOOK_SECRET'],
)
# After - new signature with additional parameters
result = composio.triggers.verify_webhook(
id=request.headers.get('webhook-id'), # NEW
payload=request.get_data(as_text=True),
secret=os.environ['COMPOSIO_WEBHOOK_SECRET'],
signature=request.headers.get('webhook-signature'), # header name changed
timestamp=request.headers.get('webhook-timestamp'), # NEW
)
# Access the normalized payload
event = result['payload']
# Check webhook version
version = result['version'] # 'V1', 'V2', or 'V3'
FastAPI Example:
from fastapi import FastAPI, Request, HTTPException
from composio import Composio
import os
app = FastAPI()
composio = Composio()
@app.post("/webhook")
def webhook(request: Request): # Still synchronous!
# New signature with all required headers
try:
result = composio.triggers.verify_webhook(
id=request.headers.get('webhook-id', ''),
payload=request.body().decode('utf-8'),
secret=os.environ['COMPOSIO_WEBHOOK_SECRET'],
signature=request.headers.get('webhook-signature', ''),
timestamp=request.headers.get('webhook-timestamp', ''),
)
# Access normalized payload and version info
print(f"Webhook version: {result['version']}")
print(f"Trigger: {result['payload']['trigger_slug']}")
return {"success": True}
except Exception as e:
raise HTTPException(status_code=401, detail=str(e))
Flask Example:
from flask import Flask, request
from composio import Composio
import os
app = Flask(__name__)
composio = Composio()
@app.route('/webhook', methods=['POST'])
def webhook(): # Still synchronous!
try:
result = composio.triggers.verify_webhook(
id=request.headers.get('webhook-id', ''),
payload=request.get_data(as_text=True),
secret=os.environ['COMPOSIO_WEBHOOK_SECRET'],
signature=request.headers.get('webhook-signature', ''),
timestamp=request.headers.get('webhook-timestamp', ''),
)
# Process verified webhook
event = result['payload']
return 'OK', 200
except Exception as e:
return 'Unauthorized', 401
Only required if you use @composio/mastra provider
<Tabs groupId="package-manager" items={['npm', 'pnpm', 'yarn']} persist>
<Tab value="npm">
bash # Update to Mastra v1 npm install @mastra/core@latest @composio/mastra@latest
</Tab>
<Tab value="pnpm">bash pnpm update @mastra/core@latest @composio/mastra@latest </Tab>
<Tab value="yarn">bash yarn upgrade @mastra/core@latest @composio/mastra@latest </Tab>
</Tabs>
Update your code:
// @noErrors
// Before
import { MastraProvider } from '@composio/mastra';
const provider = new MastraProvider();
// After
import { MastraProvider } from '@composio/mastra';
import { Mastra } from '@mastra/core';
const mastra = new Mastra({
// Your Mastra v1 config
});
const provider = new MastraProvider({ mastra });
The following features work without any code changes:
The following features require code updates:
await AND add id + timestamp parameters, update header namesid + timestamp parameters, update header names (still synchronous)| Change | TypeScript Breaking | Python Breaking | Migration Required | Effort |
|---|---|---|---|---|
| Webhook verification - TypeScript | ✅ Yes (async + signature) | N/A | ✅ Required | Medium - Add await + params |
| Webhook verification - Python | N/A | ✅ Yes (signature only) | ✅ Required | Low - Add parameters |
| Mastra v1 support | ✅ Yes | N/A | ✅ Required (if using) | Medium - Update API |
| Platform-specific file modifier | ❌ No | ❌ No | ❌ Optional | None - Automatic |
| Config defaults | ❌ No | ❌ No | ❌ Optional | None - Automatic |
| Buffer → Uint8Array | ❌ No | N/A | ❌ Optional | None - Automatic |
| Tool version fix | ❌ No | ❌ No | ❌ Optional | None - Automatic |
| JSON schema coercion | ❌ No | ❌ No | ❌ Optional | None - Automatic |
| Salesforce type fix | ❌ No | ❌ No | ❌ Optional | None - Automatic |
This release adds support for Cloudflare Workers:
// @noErrors
import { Composio } from '@composio/core';
export default {
async fetch(request: Request, env: Env): Promise<Response> {
const composio = new Composio({
apiKey: env.COMPOSIO_API_KEY,
});
// SDK now works in Cloudflare Workers!
const tools = await composio.tools.get('user_123', {
toolkits: ['github'],
});
const result = await composio.tools.execute('GITHUB_CREATE_ISSUE', {
userId: 'user_123',
arguments: {
/* ... */
},
});
return new Response(JSON.stringify(result));
},
};
Key Features Working in Cloudflare Workers:
This release includes extensive E2E testing:
Total new tests: 200+ test cases added across TypeScript and Python SDKs.
If you encounter issues during migration:
Future releases will focus on:
Stay tuned for updates!