docs/content/changelog/12-30-25.mdx
We're introducing Webhook Payload V3 - a redesigned webhook structure that follows industry standards and provides better developer experience. This update affects how you receive trigger events via webhooks and Pusher.
We're adopting the Standard Webhooks specification for better consistency and reliability.
A new header will identify the webhook version:
x-composio-webhook-version: V3
The payload structure is being reorganized to separate Composio metadata from trigger data:
Before (V2):
{
"log_id": "log_TpxVOLXYnwXZ",
"timestamp": "2025-12-23T13:06:07.695Z",
"type": "gmail_new_gmail_message",
"data": {
"connection_id": "a1b2c3d4-5e6f-7a8b-9c0d-1e2f3a4b5c6d",
"connection_nano_id": "ca_xYz9AbCdEfGh",
"trigger_nano_id": "ti_JZFoTyYKbzhB",
"trigger_id": "7f8e9d0c-1b2a-3c4d-5e6f-7a8b9c0d1e2f",
"user_id": "usr-demo-12a3b4c5...",
// ... actual trigger data mixed with metadata
}
}
After (V3):
{
"id": "msg_a1b2c3d4-5e6f-7a8b-9c0d-1e2f3a4b5c6d",
"timestamp": "2025-12-23T13:06:07.695Z",
"type": "composio.trigger.message",
"metadata": {
"log_id": "log_TpxVOLXYnwXZ",
"trigger_slug": "GMAIL_NEW_GMAIL_MESSAGE",
"auth_config_id": "ac_aCYTppZ5RsRc",
"connected_account_id": "ca_cATYssZ5RrSc",
"trigger_id": "ti_JZFoTyYKbzhB",
"user_id": "pg-test-86c9fc84..."
},
"data": {
// Clean trigger data without Composio metadata
}
}
metadata objectdata field now contains only the actual trigger payload without infrastructure metadatatype field now follows a consistent format (composio.trigger.message) instead of trigger-specific names like gmail_new_gmail_messageGMAIL_NEW_GMAIL_MESSAGE) is now available in metadata.trigger_slug for easy identificationIf you're accessing Composio metadata fields, update your code:
# Before (V2)
trigger_type = payload["type"] # "gmail_new_gmail_message"
connection_id = payload["data"]["connection_id"]
trigger_id = payload["data"]["trigger_id"]
message_text = payload["data"]["message_text"]
# After (V3)
trigger_type = payload["type"] # "composio.trigger.message"
trigger_slug = payload["metadata"]["trigger_slug"] # "GMAIL_NEW_GMAIL_MESSAGE"
connection_id = payload["metadata"]["connected_account_id"]
trigger_id = payload["metadata"]["trigger_id"]
message_text = payload["data"]["message_text"]
// @noErrors
// Before (V2)
const triggerSlug = payload.type; // "gmail_new_gmail_message"
const connectionId = payload.data.connection_id;
const triggerId = payload.data.trigger_id;
const messageText = payload.data.message_text;
// After (V3)
const webhookType = payload.type; // "composio.trigger.message"
const triggerSlug = payload.metadata.trigger_slug; // "GMAIL_NEW_GMAIL_MESSAGE"
const connectionId = payload.metadata.connected_account_id;
const triggerId = payload.metadata.trigger_id;
const messageText = payload.data.message_text;
You can detect the webhook version from headers:
webhook_version = headers.get("x-composio-webhook-version", "V2")
if webhook_version == "V3":
# Use new structure
metadata = payload["metadata"]
else:
# Use old structure
metadata = payload["data"]
Organizations created on or after February 15, 2026 will use V3 by default. </Callout>
If you have questions about migrating to V3 or need assistance: