PRD-53: Webhook & Trigger System

Version: 1.0 Status: 🟢 Implementation Complete — Testing Required Date: February 13, 2026 Author: Claude Code Prerequisites: PRD-37 (SaaS Foundation — Workspaces), PRD-36 (Composio Integration) Branch: trigger-testing


Executive Summary

Two clean inbound webhook paths for connecting external services (Jira, GitHub, Slack, WhatsApp, Telegram, etc.) to Automatos without requiring Composio:

  1. Recipe Webhooks — Each recipe with trigger type gets a unique URL. External services POST to it, triggering that specific recipe.

  2. General Workspace Webhook — Single URL per workspace. Any incoming request is routed through UniversalRouter to the right agent (chatbot-style).

Both use the URL-as-secret pattern — the URL itself contains a 128-bit random key that acts as the credential. No additional auth headers required.

Problem

  • The recipe trigger UI offered "Composio App" as the primary option (users don't have Composio access)

  • The "Custom Webhook" option generated a fake client-side URL that wasn't persisted

  • No general workspace webhook for chatbot-style routing from external sources

  • Composio triggers are useful for outbound tool execution but shouldn't be required for inbound webhooks

Solution

Path
Purpose
Auth

POST /api/webhooks/recipe/{webhook_id}

Trigger a specific recipe

URL-as-secret (webhook_id = uuid4().hex)

POST /api/webhooks/ws/{workspace_key}

Route to any agent via UniversalRouter

URL-as-secret (workspace_key = uuid4().hex)


Architecture

Request Flow — Recipe Webhook

Request Flow — Workspace Webhook

Data Model Changes

New Enum Value


Implementation Status

Part 1: Recipe Webhook UI — DONE

File
Change
Status

frontend/components/workflows/recipe-schedule-config.tsx

Removed Composio trigger UI, shows real webhook URL or "save to generate" placeholder

frontend/components/workflows/create-recipe-modal.tsx

Passes webhookId prop to RecipeScheduleConfig; added webhook_id?: string to schedule_config type

frontend/hooks/use-recipe-form.ts

Extracts webhook_id from API response after create/update; exposes lastSavedWebhookId

orchestrator/api/workflow_recipes.py

Guarded _auto_register_trigger() — skips Composio SDK when source != 'composio'

Key changes:

  • Removed TRIGGER_SOURCE_OPTIONS (Composio App / Custom Webhook distinction)

  • Removed Composio app dropdown, trigger dropdown, "Configure in Composio" button

  • Trigger mode now shows webhook URL prominently with copy button

  • Before save: shows placeholder "Save the recipe to generate a webhook URL"

  • After save: shows real {API_URL}/api/webhooks/recipe/{webhook_id} with copy + usage example

  • trigger_config is always sent (even empty {}) for trigger type — fixes backend validation

Part 2: General Workspace Webhook — DONE

File
Change
Status

orchestrator/core/models/routing.py

Added WEBHOOK = "webhook" to ChannelSource enum

orchestrator/core/models/workspaces.py

Added webhook_key column (String(64), unique, nullable)

orchestrator/alembic/versions/20260213_add_workspace_webhook_key.py

Migration: add column, backfill existing workspaces, create unique index

orchestrator/core/auth/hybrid.py

Generate webhook_key = uuid4().hex during workspace provisioning

orchestrator/core/routing/ingestors/webhook.py

NEW — WebhookIngestor: normalizes POST body → RequestEnvelope

orchestrator/api/webhooks.py

NEWPOST /api/webhooks/ws/{workspace_key} endpoint

orchestrator/main.py

Registered general_webhooks_router

orchestrator/api/workspaces.py

Returns webhook_url and webhook_key in workspace response; auto-generates key if missing

WebhookIngestor behavior:

  • Extracts content from body.message, body.text, or body.content

  • Falls back to JSON stringify of full body

  • body.agent_id → Tier-0 override (routes directly to specific agent)

  • body.source, body.channel, body.event_type, body.service → metadata for routing rules

Part 3: Settings UI — DONE

File
Change
Status

frontend/components/settings/WebhooksSettingsTab.tsx

NEW — Shows workspace webhook URL with copy button, example POST, field docs

frontend/components/settings/SettingsPanel.tsx

Added Webhooks tab (5-column layout)

frontend/components/workspace-provider.tsx

Added webhookUrl and webhookKey to Workspace interface + state mapping


Files Modified (Complete List)

Modified (11 files)

File
Lines Changed

frontend/components/settings/SettingsPanel.tsx

+8 -6

frontend/components/workflows/create-recipe-modal.tsx

+2 -1

frontend/components/workflows/recipe-schedule-config.tsx

+130 -200 (rewrite)

frontend/components/workspace-provider.tsx

+4

frontend/hooks/use-recipe-form.ts

+15 -8

orchestrator/api/workflow_recipes.py

+10

orchestrator/api/workspaces.py

+14 -2

orchestrator/core/auth/hybrid.py

+5 -2

orchestrator/core/models/routing.py

+1

orchestrator/core/models/workspaces.py

+3

orchestrator/main.py

+2

New (4 files)

File
Purpose

frontend/components/settings/WebhooksSettingsTab.tsx

Webhooks settings tab UI

orchestrator/alembic/versions/20260213_add_workspace_webhook_key.py

Database migration

orchestrator/api/webhooks.py

General workspace webhook endpoint

orchestrator/core/routing/ingestors/webhook.py

Webhook ingestor (BaseIngestor subclass)


API Reference

Recipe Webhook

Workspace Webhook


Security Considerations

Concern
Mitigation

Webhook key entropy

128-bit random hex (uuid4().hex = 32 hex chars) — brute force infeasible

Key in URL

Standard webhook pattern (GitHub, Stripe, Slack all do this). HTTPS encrypts the URL in transit.

No auth headers

By design — URL-as-secret is simpler for webhook integrations

Key rotation

Not yet implemented — would need a rotation endpoint + grace period

Rate limiting

Not yet implemented — unauthenticated endpoints should have per-key throttling

Key exposure

Only returned to authenticated workspace members via /api/workspaces/current


What's Left To Do

Pre-Release (Required)

Post-Release (Follow-up)


Test Plan

1. Recipe Webhook (Create + Trigger)

2. General Workspace Webhook (Route to Agent)

3. No Composio Errors

4. Edge Cases

  • Empty body: curl -X POST {url} -d '{}' — should route with content "{}"

  • Invalid workspace key: curl -X POST /api/webhooks/ws/nonexistent — should return 404

  • Invalid recipe webhook_id: curl -X POST /api/webhooks/recipe/nonexistent — should return 404

  • Recipe with no steps: should return gracefully (no crash)

  • Workspace with no agents/routes: should return { routed: false }

5. Migration


Relationship to Other PRDs

PRD
Relationship

PRD-36 (Composio)

Composio triggers still work when source = "composio". This PRD makes Composio optional, not removed.

PRD-37 (SaaS Foundation)

Extends workspace model with webhook_key. Uses existing auth/provisioning flow.

PRD-39 (Mem0)

No direct relationship. Webhook-triggered agent executions will use memory if agent has Mem0 configured.

Last updated