diff --git a/web/oss/src/components/pages/settings/Triggers/Triggers.tsx b/web/oss/src/components/pages/settings/Triggers/Triggers.tsx
index 0c65e99f53..1bb4ebdf6c 100644
--- a/web/oss/src/components/pages/settings/Triggers/Triggers.tsx
+++ b/web/oss/src/components/pages/settings/Triggers/Triggers.tsx
@@ -1,9 +1,11 @@
+import GatewaySubscriptionsSection from "./components/GatewaySubscriptionsSection"
import GatewayTriggersSection from "./components/GatewayTriggersSection"
export default function Triggers() {
return (
+
)
}
diff --git a/web/oss/src/components/pages/settings/Triggers/components/GatewaySubscriptionsSection.tsx b/web/oss/src/components/pages/settings/Triggers/components/GatewaySubscriptionsSection.tsx
new file mode 100644
index 0000000000..6e06bde2cc
--- /dev/null
+++ b/web/oss/src/components/pages/settings/Triggers/components/GatewaySubscriptionsSection.tsx
@@ -0,0 +1,264 @@
+import {useCallback, useMemo} from "react"
+
+import {
+ deliveriesDrawerAtom,
+ subscriptionDrawerAtom,
+ useTriggerConnectionsQuery,
+ useTriggerSubscription,
+ useTriggerSubscriptions,
+ type TriggerSubscription,
+} from "@agenta/entities/gatewayTrigger"
+import {TriggerDeliveriesDrawer, TriggerSubscriptionDrawer} from "@agenta/entity-ui/gatewayTrigger"
+import {MoreOutlined} from "@ant-design/icons"
+import {
+ ArrowsClockwise,
+ GearSix,
+ ListChecks,
+ PencilSimpleLine,
+ Plus,
+ Trash,
+ XCircle,
+} from "@phosphor-icons/react"
+import {Button, Dropdown, Empty, Table, Tag, Typography, message} from "antd"
+import type {ColumnsType} from "antd/es/table"
+import {useSetAtom} from "jotai"
+
+import {formatDay} from "@/oss/lib/helpers/dateTimeHelper"
+
+export default function GatewaySubscriptionsSection() {
+ const {subscriptions, isLoading} = useTriggerSubscriptions()
+ const {connections} = useTriggerConnectionsQuery()
+ const {revoke, refresh, remove, isMutating} = useTriggerSubscription()
+ const openDrawer = useSetAtom(subscriptionDrawerAtom)
+ const openDeliveries = useSetAtom(deliveriesDrawerAtom)
+
+ const connectionLabel = useCallback(
+ (connectionId?: string) => {
+ const c = connections.find((conn) => conn.id === connectionId)
+ return c ? c.name || c.slug || c.integration_key : (connectionId ?? "-")
+ },
+ [connections],
+ )
+
+ const handleCreate = useCallback(() => openDrawer({}), [openDrawer])
+
+ const handleEdit = useCallback(
+ (record: TriggerSubscription) => openDrawer({subscriptionId: record.id ?? undefined}),
+ [openDrawer],
+ )
+
+ const handleRevoke = useCallback(
+ async (record: TriggerSubscription) => {
+ if (!record.id) return
+ try {
+ await revoke(record.id)
+ message.success("Subscription revoked")
+ } catch {
+ message.error("Failed to revoke subscription")
+ }
+ },
+ [revoke],
+ )
+
+ const handleRefresh = useCallback(
+ async (record: TriggerSubscription) => {
+ if (!record.id) return
+ try {
+ await refresh(record.id)
+ message.success("Subscription refreshed")
+ } catch {
+ message.error("Failed to refresh subscription")
+ }
+ },
+ [refresh],
+ )
+
+ const handleDelete = useCallback(
+ async (record: TriggerSubscription) => {
+ if (!record.id) return
+ try {
+ await remove(record.id)
+ message.success("Subscription deleted")
+ } catch {
+ message.error("Failed to delete subscription")
+ }
+ },
+ [remove],
+ )
+
+ const columns: ColumnsType = useMemo(
+ () => [
+ {
+ title: "Name",
+ key: "name",
+ onHeaderCell: () => ({style: {minWidth: 160}}),
+ render: (_, record) => (
+ {record.name || record.id || "-"}
+ ),
+ },
+ {
+ title: "Connection",
+ key: "connection",
+ onHeaderCell: () => ({style: {minWidth: 160}}),
+ render: (_, record) => (
+ {connectionLabel(record.connection_id)}
+ ),
+ },
+ {
+ title: "Event",
+ key: "event",
+ onHeaderCell: () => ({style: {minWidth: 160}}),
+ render: (_, record) => (
+
+ {record.data?.event_key ?? "-"}
+
+ ),
+ },
+ {
+ title: "Status",
+ key: "status",
+ onHeaderCell: () => ({style: {minWidth: 120}}),
+ render: (_, record) =>
+ !record.valid ? (
+ Invalid
+ ) : record.enabled ? (
+ Enabled
+ ) : (
+ Disabled
+ ),
+ },
+ {
+ title: "Created at",
+ dataIndex: "created_at",
+ key: "created_at",
+ onHeaderCell: () => ({style: {minWidth: 160}}),
+ render: (value: string) =>
+ value ? formatDay({date: value, outputFormat: "YYYY-MM-DD HH:mm"}) : "-",
+ },
+ {
+ title: ,
+ key: "actions",
+ width: 61,
+ fixed: "right" as const,
+ align: "center" as const,
+ render: (_, record) => (
+ ,
+ onClick: (e) => {
+ e.domEvent.stopPropagation()
+ if (record.id)
+ openDeliveries({
+ subscriptionId: record.id,
+ subscriptionName: record.name ?? undefined,
+ })
+ },
+ },
+ {
+ key: "edit",
+ label: "Edit",
+ icon: ,
+ onClick: (e) => {
+ e.domEvent.stopPropagation()
+ handleEdit(record)
+ },
+ },
+ {
+ key: "refresh",
+ label: "Refresh",
+ icon: ,
+ onClick: (e) => {
+ e.domEvent.stopPropagation()
+ handleRefresh(record)
+ },
+ },
+ {type: "divider" as const},
+ {
+ key: "revoke",
+ label: "Revoke",
+ icon: ,
+ onClick: (e) => {
+ e.domEvent.stopPropagation()
+ handleRevoke(record)
+ },
+ },
+ {
+ key: "delete",
+ label: "Delete",
+ icon: ,
+ danger: true,
+ onClick: (e) => {
+ e.domEvent.stopPropagation()
+ handleDelete(record)
+ },
+ },
+ ],
+ }}
+ >
+ }
+ aria-label="Open subscription actions"
+ onClick={(e) => e.stopPropagation()}
+ />
+
+ ),
+ },
+ ],
+ [connectionLabel, handleDelete, handleEdit, handleRefresh, handleRevoke, openDeliveries],
+ )
+
+ return (
+ <>
+
+
+
+ Trigger subscriptions
+
+ }
+ onClick={handleCreate}
+ disabled={connections.length === 0}
+ >
+ New subscription
+
+
+
+
+ Bind a provider event to a workflow. Each subscription dispatches matching
+ events to its bound workflow.
+
+
+
+ className="ph-no-capture"
+ columns={columns}
+ dataSource={subscriptions}
+ rowKey={(record) => record.id ?? record.slug ?? record.data?.event_key ?? ""}
+ bordered
+ pagination={false}
+ loading={isLoading || isMutating}
+ locale={{emptyText: }}
+ onRow={(record) => ({
+ onClick: () => handleEdit(record),
+ className: "cursor-pointer",
+ })}
+ />
+
+
+
+
+ >
+ )
+}
diff --git a/web/packages/agenta-entities/src/gatewayTrigger/api/api.ts b/web/packages/agenta-entities/src/gatewayTrigger/api/api.ts
index 9de7f2d628..08faeca609 100644
--- a/web/packages/agenta-entities/src/gatewayTrigger/api/api.ts
+++ b/web/packages/agenta-entities/src/gatewayTrigger/api/api.ts
@@ -18,11 +18,23 @@ import {
triggerCatalogProviderResponseSchema,
triggerCatalogProvidersResponseSchema,
triggerConnectionsResponseSchema,
+ triggerDeliveriesResponseSchema,
+ triggerDeliveryResponseSchema,
+ triggerSubscriptionResponseSchema,
+ triggerSubscriptionsResponseSchema,
type TriggerCatalogEventResponse,
type TriggerCatalogEventsResponse,
type TriggerCatalogProviderResponse,
type TriggerCatalogProvidersResponse,
type TriggerConnectionsResponse,
+ type TriggerDeliveriesResponse,
+ type TriggerDeliveryQuery,
+ type TriggerDeliveryResponse,
+ type TriggerSubscriptionCreate,
+ type TriggerSubscriptionEdit,
+ type TriggerSubscriptionQuery,
+ type TriggerSubscriptionResponse,
+ type TriggerSubscriptionsResponse,
} from "../core/types"
import {axios, projectScopedParams, triggersBaseUrl} from "./client"
@@ -117,3 +129,146 @@ export const queryTriggerConnections = async (params?: {
)
return (validated as TriggerConnectionsResponse | null) ?? {count: 0, connections: []}
}
+
+// --- Subscriptions ---
+
+export const queryTriggerSubscriptions = async (
+ subscription?: TriggerSubscriptionQuery,
+): Promise => {
+ const {data} = await axios.post(
+ `${triggersBaseUrl()}/subscriptions/query`,
+ {subscription: subscription ?? null},
+ projectScopedParams(),
+ )
+ return (
+ safeParseWithLogging(
+ triggerSubscriptionsResponseSchema,
+ data,
+ "[queryTriggerSubscriptions]",
+ ) ?? {count: 0, subscriptions: []}
+ )
+}
+
+export const fetchTriggerSubscription = async (
+ subscriptionId: string,
+): Promise => {
+ const {data} = await axios.get(
+ `${triggersBaseUrl()}/subscriptions/${subscriptionId}`,
+ projectScopedParams(),
+ )
+ return (
+ safeParseWithLogging(
+ triggerSubscriptionResponseSchema,
+ data,
+ "[fetchTriggerSubscription]",
+ ) ?? {count: 0, subscription: null}
+ )
+}
+
+export const createTriggerSubscription = async (
+ subscription: TriggerSubscriptionCreate,
+): Promise => {
+ const {data} = await axios.post(
+ `${triggersBaseUrl()}/subscriptions/`,
+ {subscription},
+ projectScopedParams(),
+ )
+ return (
+ safeParseWithLogging(
+ triggerSubscriptionResponseSchema,
+ data,
+ "[createTriggerSubscription]",
+ ) ?? {count: 0, subscription: null}
+ )
+}
+
+export const editTriggerSubscription = async (
+ subscription: TriggerSubscriptionEdit,
+): Promise => {
+ const {data} = await axios.put(
+ `${triggersBaseUrl()}/subscriptions/${subscription.id}`,
+ {subscription},
+ projectScopedParams(),
+ )
+ return (
+ safeParseWithLogging(
+ triggerSubscriptionResponseSchema,
+ data,
+ "[editTriggerSubscription]",
+ ) ?? {count: 0, subscription: null}
+ )
+}
+
+export const refreshTriggerSubscription = async (
+ subscriptionId: string,
+): Promise => {
+ const {data} = await axios.post(
+ `${triggersBaseUrl()}/subscriptions/${subscriptionId}/refresh`,
+ {},
+ projectScopedParams(),
+ )
+ return (
+ safeParseWithLogging(
+ triggerSubscriptionResponseSchema,
+ data,
+ "[refreshTriggerSubscription]",
+ ) ?? {count: 0, subscription: null}
+ )
+}
+
+export const revokeTriggerSubscription = async (
+ subscriptionId: string,
+): Promise => {
+ const {data} = await axios.post(
+ `${triggersBaseUrl()}/subscriptions/${subscriptionId}/revoke`,
+ {},
+ projectScopedParams(),
+ )
+ return (
+ safeParseWithLogging(
+ triggerSubscriptionResponseSchema,
+ data,
+ "[revokeTriggerSubscription]",
+ ) ?? {count: 0, subscription: null}
+ )
+}
+
+export const deleteTriggerSubscription = async (subscriptionId: string): Promise => {
+ await axios.delete(
+ `${triggersBaseUrl()}/subscriptions/${subscriptionId}`,
+ projectScopedParams(),
+ )
+}
+
+// --- Deliveries (read-only) ---
+
+export const queryTriggerDeliveries = async (
+ delivery?: TriggerDeliveryQuery,
+): Promise => {
+ const {data} = await axios.post(
+ `${triggersBaseUrl()}/deliveries/query`,
+ {delivery: delivery ?? null},
+ projectScopedParams(),
+ )
+ return (
+ safeParseWithLogging(triggerDeliveriesResponseSchema, data, "[queryTriggerDeliveries]") ?? {
+ count: 0,
+ deliveries: [],
+ }
+ )
+}
+
+export const fetchTriggerDelivery = async (
+ deliveryId: string,
+): Promise => {
+ const {data} = await axios.get(
+ `${triggersBaseUrl()}/deliveries/${deliveryId}`,
+ projectScopedParams(),
+ )
+ return (
+ safeParseWithLogging(triggerDeliveryResponseSchema, data, "[fetchTriggerDelivery]") ?? {
+ count: 0,
+ delivery: null,
+ }
+ )
+}
diff --git a/web/packages/agenta-entities/src/gatewayTrigger/api/index.ts b/web/packages/agenta-entities/src/gatewayTrigger/api/index.ts
index c307965ae8..f99959cd17 100644
--- a/web/packages/agenta-entities/src/gatewayTrigger/api/index.ts
+++ b/web/packages/agenta-entities/src/gatewayTrigger/api/index.ts
@@ -1,8 +1,17 @@
export {
+ createTriggerSubscription,
+ deleteTriggerSubscription,
+ editTriggerSubscription,
+ fetchTriggerDelivery,
fetchTriggerEvent,
fetchTriggerEvents,
fetchTriggerProvider,
fetchTriggerProviders,
+ fetchTriggerSubscription,
queryTriggerConnections,
+ queryTriggerDeliveries,
+ queryTriggerSubscriptions,
+ refreshTriggerSubscription,
+ revokeTriggerSubscription,
} from "./api"
export {triggersBaseUrl, projectScopedParams} from "./client"
diff --git a/web/packages/agenta-entities/src/gatewayTrigger/core/types.ts b/web/packages/agenta-entities/src/gatewayTrigger/core/types.ts
index 16b6d8ea26..1ba1a27bb4 100644
--- a/web/packages/agenta-entities/src/gatewayTrigger/core/types.ts
+++ b/web/packages/agenta-entities/src/gatewayTrigger/core/types.ts
@@ -129,3 +129,173 @@ export type TriggerConnection = ToolConnection
export type TriggerConnectionsResponse = ToolConnectionsResponse
export {isConnectionActive, isConnectionValid} from "../../gatewayTool/core/types"
+
+// ---------------------------------------------------------------------------
+// Subscriptions — a standing watch binding a provider event to a workflow.
+//
+// Mirrors the frozen backend DTOs (`api/oss/src/core/triggers/dtos.py`:
+// TriggerSubscription / *Create / *Edit / *Query). Validated at the axios
+// boundary; the aliases swap to `AgentaApi.*` once the triggers resource lands
+// in the Fern client.
+// ---------------------------------------------------------------------------
+
+// A workflow reference (the /retrieve shape): {id, slug?, version?}.
+export const triggerReferenceSchema = z
+ .object({
+ id: z.string().nullish(),
+ slug: z.string().nullish(),
+ version: z.string().nullish(),
+ })
+ .passthrough()
+export type TriggerReference = z.infer
+
+export const triggerSelectorSchema = z
+ .object({
+ key: z.string().nullish(),
+ path: z.string().nullish(),
+ })
+ .passthrough()
+export type TriggerSelector = z.infer
+
+export const triggerSubscriptionDataSchema = z
+ .object({
+ event_key: z.string(),
+ ti_id: z.string().nullish(),
+ trigger_config: z.record(z.string(), z.unknown()).nullish(),
+ inputs_fields: z.record(z.string(), z.unknown()).nullish(),
+ references: z.record(z.string(), triggerReferenceSchema).nullish(),
+ selector: triggerSelectorSchema.nullish(),
+ })
+ .passthrough()
+export type TriggerSubscriptionData = z.infer
+
+export const triggerSubscriptionSchema = z
+ .object({
+ id: z.string().nullish(),
+ slug: z.string().nullish(),
+ name: z.string().nullish(),
+ description: z.string().nullish(),
+ flags: jsonRecordSchema,
+ tags: jsonRecordSchema,
+ meta: jsonRecordSchema,
+ created_at: z.string().nullish(),
+ updated_at: z.string().nullish(),
+ deleted_at: z.string().nullish(),
+ created_by_id: z.string().nullish(),
+ updated_by_id: z.string().nullish(),
+ deleted_by_id: z.string().nullish(),
+ connection_id: z.string(),
+ data: triggerSubscriptionDataSchema,
+ enabled: z.boolean().default(true),
+ valid: z.boolean().default(true),
+ })
+ .passthrough()
+export type TriggerSubscription = z.infer
+
+export const triggerSubscriptionResponseSchema = z
+ .object({
+ count: z.number().default(0),
+ subscription: triggerSubscriptionSchema.nullish(),
+ })
+ .passthrough()
+export type TriggerSubscriptionResponse = z.infer
+
+export const triggerSubscriptionsResponseSchema = z
+ .object({
+ count: z.number().default(0),
+ subscriptions: z.array(triggerSubscriptionSchema).default([]),
+ })
+ .passthrough()
+export type TriggerSubscriptionsResponse = z.infer
+
+// Create body (Header + Metadata + connection_id + data); no id.
+export interface TriggerSubscriptionCreate {
+ name?: string | null
+ description?: string | null
+ flags?: Record | null
+ tags?: Record | null
+ meta?: Record | null
+ connection_id: string
+ data: TriggerSubscriptionData
+}
+
+// Edit body — full PUT: Identifier + Header + Metadata + connection_id + data + flags.
+export interface TriggerSubscriptionEdit extends TriggerSubscriptionCreate {
+ id: string
+ enabled: boolean
+ valid: boolean
+}
+
+export interface TriggerSubscriptionQuery {
+ name?: string
+ connection_id?: string
+ event_key?: string
+}
+
+// ---------------------------------------------------------------------------
+// Deliveries — read-only audit rows, one per inbound event dispatched.
+// Mirrors `TriggerDelivery` / `TriggerDeliveryQuery`. `status` is the shared
+// `core.shared.dtos.Status` (timestamp/type/code/message/stacktrace).
+// ---------------------------------------------------------------------------
+
+export const triggerStatusSchema = z
+ .object({
+ timestamp: z.string().nullish(),
+ type: z.string().nullish(),
+ code: z.string().nullish(),
+ message: z.string().nullish(),
+ stacktrace: z.string().nullish(),
+ })
+ .passthrough()
+export type TriggerStatus = z.infer
+
+export const triggerDeliveryDataSchema = z
+ .object({
+ event_key: z.string().nullish(),
+ references: z.record(z.string(), triggerReferenceSchema).nullish(),
+ inputs: z.record(z.string(), z.unknown()).nullish(),
+ result: z.record(z.string(), z.unknown()).nullish(),
+ error: z.string().nullish(),
+ })
+ .passthrough()
+export type TriggerDeliveryData = z.infer
+
+export const triggerDeliverySchema = z
+ .object({
+ id: z.string().nullish(),
+ slug: z.string().nullish(),
+ created_at: z.string().nullish(),
+ updated_at: z.string().nullish(),
+ deleted_at: z.string().nullish(),
+ created_by_id: z.string().nullish(),
+ updated_by_id: z.string().nullish(),
+ deleted_by_id: z.string().nullish(),
+ status: triggerStatusSchema,
+ data: triggerDeliveryDataSchema.nullish(),
+ subscription_id: z.string(),
+ event_id: z.string(),
+ })
+ .passthrough()
+export type TriggerDelivery = z.infer
+
+export const triggerDeliveryResponseSchema = z
+ .object({
+ count: z.number().default(0),
+ delivery: triggerDeliverySchema.nullish(),
+ })
+ .passthrough()
+export type TriggerDeliveryResponse = z.infer
+
+export const triggerDeliveriesResponseSchema = z
+ .object({
+ count: z.number().default(0),
+ deliveries: z.array(triggerDeliverySchema).default([]),
+ })
+ .passthrough()
+export type TriggerDeliveriesResponse = z.infer
+
+export interface TriggerDeliveryQuery {
+ status?: TriggerStatus
+ subscription_id?: string
+ event_id?: string
+}
diff --git a/web/packages/agenta-entities/src/gatewayTrigger/hooks/index.ts b/web/packages/agenta-entities/src/gatewayTrigger/hooks/index.ts
index 3cbfeb2535..31afdb9936 100644
--- a/web/packages/agenta-entities/src/gatewayTrigger/hooks/index.ts
+++ b/web/packages/agenta-entities/src/gatewayTrigger/hooks/index.ts
@@ -6,3 +6,11 @@ export {
useTriggerConnectionsQuery,
useTriggerIntegrationConnections,
} from "./useTriggerConnections"
+export {
+ triggerConnectionSubscriptionsAtomFamily,
+ triggerSubscriptionsQueryAtom,
+ useTriggerConnectionSubscriptions,
+ useTriggerSubscriptions,
+} from "./useTriggerSubscriptions"
+export {triggerSubscriptionQueryAtomFamily, useTriggerSubscription} from "./useTriggerSubscription"
+export {triggerDeliveriesAtomFamily, useTriggerDeliveries} from "./useTriggerDeliveries"
diff --git a/web/packages/agenta-entities/src/gatewayTrigger/hooks/useTriggerDeliveries.ts b/web/packages/agenta-entities/src/gatewayTrigger/hooks/useTriggerDeliveries.ts
new file mode 100644
index 0000000000..c35f7156ae
--- /dev/null
+++ b/web/packages/agenta-entities/src/gatewayTrigger/hooks/useTriggerDeliveries.ts
@@ -0,0 +1,36 @@
+import {useMemo} from "react"
+
+import {useAtomValue} from "jotai"
+import {atomFamily} from "jotai/utils"
+import {atomWithQuery} from "jotai-tanstack-query"
+
+import {queryTriggerDeliveries} from "../api"
+import type {TriggerDelivery, TriggerDeliveriesResponse} from "../core/types"
+
+// Deliveries scoped to one subscription. Distinct from subscription keys.
+export const triggerDeliveriesAtomFamily = atomFamily((subscriptionId: string) =>
+ atomWithQuery(() => ({
+ queryKey: ["triggers", "deliveries", subscriptionId],
+ queryFn: () => queryTriggerDeliveries({subscription_id: subscriptionId}),
+ staleTime: 15_000,
+ refetchOnWindowFocus: false,
+ enabled: !!subscriptionId,
+ })),
+)
+
+export const useTriggerDeliveries = (subscriptionId?: string) => {
+ const query = useAtomValue(triggerDeliveriesAtomFamily(subscriptionId ?? ""))
+
+ const deliveries = useMemo(
+ () => query.data?.deliveries ?? [],
+ [query.data?.deliveries],
+ )
+
+ return {
+ deliveries,
+ count: query.data?.count ?? 0,
+ isLoading: subscriptionId ? query.isPending : false,
+ error: query.error,
+ refetch: query.refetch,
+ }
+}
diff --git a/web/packages/agenta-entities/src/gatewayTrigger/hooks/useTriggerSubscription.ts b/web/packages/agenta-entities/src/gatewayTrigger/hooks/useTriggerSubscription.ts
new file mode 100644
index 0000000000..cbb9122b1e
--- /dev/null
+++ b/web/packages/agenta-entities/src/gatewayTrigger/hooks/useTriggerSubscription.ts
@@ -0,0 +1,94 @@
+import {useCallback, useState} from "react"
+
+import {queryClient} from "@agenta/shared/api"
+import {useAtomValue} from "jotai"
+import {atomFamily} from "jotai/utils"
+import {atomWithQuery} from "jotai-tanstack-query"
+
+import {
+ createTriggerSubscription,
+ deleteTriggerSubscription,
+ editTriggerSubscription,
+ fetchTriggerSubscription,
+ refreshTriggerSubscription,
+ revokeTriggerSubscription,
+} from "../api"
+import type {
+ TriggerSubscription,
+ TriggerSubscriptionCreate,
+ TriggerSubscriptionEdit,
+ TriggerSubscriptionResponse,
+} from "../core/types"
+
+const invalidateSubscriptions = () => {
+ queryClient.invalidateQueries({queryKey: ["triggers", "subscriptions"]})
+}
+
+// Single subscription (used to source the full PUT body before editing).
+export const triggerSubscriptionQueryAtomFamily = atomFamily((subscriptionId: string) =>
+ atomWithQuery(() => ({
+ queryKey: ["triggers", "subscriptions", "detail", subscriptionId],
+ queryFn: () => fetchTriggerSubscription(subscriptionId),
+ staleTime: 30_000,
+ refetchOnWindowFocus: false,
+ enabled: !!subscriptionId,
+ })),
+)
+
+export const useTriggerSubscription = (subscriptionId?: string) => {
+ const query = useAtomValue(triggerSubscriptionQueryAtomFamily(subscriptionId ?? ""))
+ const [isMutating, setIsMutating] = useState(false)
+
+ const run = useCallback(
+ async (
+ fn: () => Promise,
+ ): Promise => {
+ setIsMutating(true)
+ try {
+ const res = await fn()
+ invalidateSubscriptions()
+ return res.subscription ?? null
+ } finally {
+ setIsMutating(false)
+ }
+ },
+ [],
+ )
+
+ const create = useCallback(
+ (subscription: TriggerSubscriptionCreate) =>
+ run(() => createTriggerSubscription(subscription)),
+ [run],
+ )
+
+ const edit = useCallback(
+ (subscription: TriggerSubscriptionEdit) => run(() => editTriggerSubscription(subscription)),
+ [run],
+ )
+
+ const revoke = useCallback((id: string) => run(() => revokeTriggerSubscription(id)), [run])
+
+ const refresh = useCallback((id: string) => run(() => refreshTriggerSubscription(id)), [run])
+
+ const remove = useCallback(async (id: string) => {
+ setIsMutating(true)
+ try {
+ await deleteTriggerSubscription(id)
+ invalidateSubscriptions()
+ } finally {
+ setIsMutating(false)
+ }
+ }, [])
+
+ return {
+ subscription: subscriptionId ? (query.data?.subscription ?? null) : null,
+ isLoading: subscriptionId ? query.isPending : false,
+ error: query.error,
+ isMutating,
+ create,
+ edit,
+ revoke,
+ refresh,
+ remove,
+ }
+}
diff --git a/web/packages/agenta-entities/src/gatewayTrigger/hooks/useTriggerSubscriptions.ts b/web/packages/agenta-entities/src/gatewayTrigger/hooks/useTriggerSubscriptions.ts
new file mode 100644
index 0000000000..80df5d48b3
--- /dev/null
+++ b/web/packages/agenta-entities/src/gatewayTrigger/hooks/useTriggerSubscriptions.ts
@@ -0,0 +1,60 @@
+import {useMemo} from "react"
+
+import {useAtomValue} from "jotai"
+import {atomFamily} from "jotai/utils"
+import {atomWithQuery} from "jotai-tanstack-query"
+
+import {queryTriggerSubscriptions} from "../api"
+import type {TriggerSubscription, TriggerSubscriptionsResponse} from "../core/types"
+
+// Distinct from the catalog/connection keys (["triggers", "catalog"|"connections"]).
+export const triggerSubscriptionsQueryAtom = atomWithQuery(() => ({
+ queryKey: ["triggers", "subscriptions"],
+ queryFn: () => queryTriggerSubscriptions(),
+ staleTime: 30_000,
+ refetchOnWindowFocus: false,
+}))
+
+export const useTriggerSubscriptions = () => {
+ const query = useAtomValue(triggerSubscriptionsQueryAtom)
+
+ const subscriptions = useMemo(
+ () => query.data?.subscriptions ?? [],
+ [query.data?.subscriptions],
+ )
+
+ return {
+ subscriptions,
+ count: query.data?.count ?? 0,
+ isLoading: query.isPending,
+ error: query.error,
+ refetch: query.refetch,
+ }
+}
+
+// Subscriptions scoped to a single connection.
+export const triggerConnectionSubscriptionsAtomFamily = atomFamily((connectionId: string) =>
+ atomWithQuery(() => ({
+ queryKey: ["triggers", "subscriptions", "connection", connectionId],
+ queryFn: () => queryTriggerSubscriptions({connection_id: connectionId}),
+ staleTime: 30_000,
+ refetchOnWindowFocus: false,
+ enabled: !!connectionId,
+ })),
+)
+
+export const useTriggerConnectionSubscriptions = (connectionId: string) => {
+ const query = useAtomValue(triggerConnectionSubscriptionsAtomFamily(connectionId))
+
+ const subscriptions = useMemo(
+ () => query.data?.subscriptions ?? [],
+ [query.data?.subscriptions],
+ )
+
+ return {
+ subscriptions,
+ count: query.data?.count ?? 0,
+ isLoading: query.isPending,
+ error: query.error,
+ }
+}
diff --git a/web/packages/agenta-entities/src/gatewayTrigger/index.ts b/web/packages/agenta-entities/src/gatewayTrigger/index.ts
index 1177cb2375..24fb926f9c 100644
--- a/web/packages/agenta-entities/src/gatewayTrigger/index.ts
+++ b/web/packages/agenta-entities/src/gatewayTrigger/index.ts
@@ -24,7 +24,22 @@ export type {
TriggerCatalogProvidersResponse,
TriggerConnection,
TriggerConnectionsResponse,
+ TriggerDelivery,
+ TriggerDeliveriesResponse,
+ TriggerDeliveryData,
+ TriggerDeliveryQuery,
+ TriggerDeliveryResponse,
TriggerProviderKind,
+ TriggerReference,
+ TriggerSelector,
+ TriggerStatus,
+ TriggerSubscription,
+ TriggerSubscriptionCreate,
+ TriggerSubscriptionData,
+ TriggerSubscriptionEdit,
+ TriggerSubscriptionQuery,
+ TriggerSubscriptionResponse,
+ TriggerSubscriptionsResponse,
} from "./core"
export {isConnectionActive, isConnectionValid} from "./core"
@@ -33,19 +48,34 @@ export {isConnectionActive, isConnectionValid} from "./core"
// ---------------------------------------------------------------------------
export {
+ createTriggerSubscription,
+ deleteTriggerSubscription,
+ editTriggerSubscription,
+ fetchTriggerDelivery,
fetchTriggerEvent,
fetchTriggerEvents,
fetchTriggerProvider,
fetchTriggerProviders,
+ fetchTriggerSubscription,
queryTriggerConnections,
+ queryTriggerDeliveries,
+ queryTriggerSubscriptions,
+ refreshTriggerSubscription,
+ revokeTriggerSubscription,
} from "./api"
// ---------------------------------------------------------------------------
// STATE — drawer + selection atoms
// ---------------------------------------------------------------------------
-export {eventsDrawerAtom, eventSearchAtom, selectedCatalogEventAtom} from "./state"
-export type {EventsDrawerState} from "./state"
+export {
+ deliveriesDrawerAtom,
+ eventsDrawerAtom,
+ eventSearchAtom,
+ selectedCatalogEventAtom,
+ subscriptionDrawerAtom,
+} from "./state"
+export type {DeliveriesDrawerState, EventsDrawerState, SubscriptionDrawerState} from "./state"
// ---------------------------------------------------------------------------
// HOOKS — query hooks for React consumers
@@ -55,10 +85,18 @@ export {
catalogEventsInfiniteFamily,
eventsSearchAtom,
triggerConnectionsQueryAtom,
+ triggerConnectionSubscriptionsAtomFamily,
+ triggerDeliveriesAtomFamily,
triggerEventDetailQueryFamily,
triggerIntegrationConnectionsAtomFamily,
+ triggerSubscriptionQueryAtomFamily,
+ triggerSubscriptionsQueryAtom,
useCatalogEvents,
useTriggerConnectionsQuery,
+ useTriggerConnectionSubscriptions,
+ useTriggerDeliveries,
useTriggerEvent,
useTriggerIntegrationConnections,
+ useTriggerSubscription,
+ useTriggerSubscriptions,
} from "./hooks"
diff --git a/web/packages/agenta-entities/src/gatewayTrigger/state/atoms.ts b/web/packages/agenta-entities/src/gatewayTrigger/state/atoms.ts
index 975e2f378a..c9a823eeab 100644
--- a/web/packages/agenta-entities/src/gatewayTrigger/state/atoms.ts
+++ b/web/packages/agenta-entities/src/gatewayTrigger/state/atoms.ts
@@ -15,3 +15,24 @@ export const eventsDrawerAtom = atom(null)
// Drawer-local browsing state (reset on close)
export const eventSearchAtom = atom("")
export const selectedCatalogEventAtom = atom(null)
+
+// ---------------------------------------------------------------------------
+// Subscription drawer state — create (no id) or edit (existing subscription id)
+// ---------------------------------------------------------------------------
+
+export interface SubscriptionDrawerState {
+ // Edit mode when set; create mode otherwise.
+ subscriptionId?: string
+ // Optional create-mode prefill from a chosen connection.
+ connectionId?: string
+ integrationKey?: string
+ integrationName?: string
+}
+export const subscriptionDrawerAtom = atom(null)
+
+// Deliveries drawer state — opened against one subscription.
+export interface DeliveriesDrawerState {
+ subscriptionId: string
+ subscriptionName?: string
+}
+export const deliveriesDrawerAtom = atom(null)
diff --git a/web/packages/agenta-entities/src/gatewayTrigger/state/index.ts b/web/packages/agenta-entities/src/gatewayTrigger/state/index.ts
index 6d177ea88d..d5f81c8210 100644
--- a/web/packages/agenta-entities/src/gatewayTrigger/state/index.ts
+++ b/web/packages/agenta-entities/src/gatewayTrigger/state/index.ts
@@ -1,2 +1,8 @@
-export {eventsDrawerAtom, eventSearchAtom, selectedCatalogEventAtom} from "./atoms"
-export type {EventsDrawerState} from "./atoms"
+export {
+ deliveriesDrawerAtom,
+ eventsDrawerAtom,
+ eventSearchAtom,
+ selectedCatalogEventAtom,
+ subscriptionDrawerAtom,
+} from "./atoms"
+export type {DeliveriesDrawerState, EventsDrawerState, SubscriptionDrawerState} from "./atoms"
diff --git a/web/packages/agenta-entities/tests/unit/gatewayTriggerApi.test.ts b/web/packages/agenta-entities/tests/unit/gatewayTriggerApi.test.ts
index 50034bec1c..faad94054c 100644
--- a/web/packages/agenta-entities/tests/unit/gatewayTriggerApi.test.ts
+++ b/web/packages/agenta-entities/tests/unit/gatewayTriggerApi.test.ts
@@ -32,10 +32,14 @@ vi.mock("jotai", async (importOriginal) => {
})
import {
+ createTriggerSubscription,
+ fetchTriggerSubscription,
fetchTriggerEvent,
fetchTriggerEvents,
fetchTriggerProviders,
queryTriggerConnections,
+ queryTriggerDeliveries,
+ queryTriggerSubscriptions,
} from "../../src/gatewayTrigger/api/api"
beforeEach(() => {
@@ -171,3 +175,108 @@ describe("connections (F2 — shared rows)", () => {
expect(res).toEqual({count: 0, connections: []})
})
})
+
+describe("subscriptions", () => {
+ const sampleSubscription = {
+ id: "sub-1",
+ name: "Star watch",
+ connection_id: "conn-1",
+ enabled: true,
+ valid: true,
+ data: {
+ event_key: "github_star_added_event",
+ ti_id: "ti_abc",
+ trigger_config: {owner: "agenta", repo: "agenta"},
+ inputs_fields: {message: "{{event.data.action}}"},
+ references: {workflow_revision: {id: "rev-1"}},
+ },
+ }
+
+ it("creates a subscription with the {subscription} envelope and project scope", async () => {
+ post.mockResolvedValueOnce({data: {count: 1, subscription: sampleSubscription}})
+
+ const res = await createTriggerSubscription({
+ name: "Star watch",
+ connection_id: "conn-1",
+ data: {
+ event_key: "github_star_added_event",
+ inputs_fields: {message: "{{event.data.action}}"},
+ references: {workflow_revision: {id: "rev-1"}},
+ },
+ })
+
+ const [url, body, opts] = post.mock.calls[0]
+ expect(url).toBe("https://api.test/triggers/subscriptions/")
+ expect(body.subscription.connection_id).toBe("conn-1")
+ expect(body.subscription.data.references.workflow_revision.id).toBe("rev-1")
+ expect(opts.params).toMatchObject({project_id: "proj-42"})
+ expect(res.subscription?.id).toBe("sub-1")
+ })
+
+ it("queries subscriptions and passes the filter under {subscription}", async () => {
+ post.mockResolvedValueOnce({data: {count: 1, subscriptions: [sampleSubscription]}})
+
+ const res = await queryTriggerSubscriptions({connection_id: "conn-1"})
+
+ const [url, body] = post.mock.calls[0]
+ expect(url).toBe("https://api.test/triggers/subscriptions/query")
+ expect(body).toEqual({subscription: {connection_id: "conn-1"}})
+ expect(res.subscriptions).toHaveLength(1)
+ expect(res.subscriptions[0].data.event_key).toBe("github_star_added_event")
+ })
+
+ it("fetches a single subscription by id", async () => {
+ get.mockResolvedValueOnce({data: {count: 1, subscription: sampleSubscription}})
+
+ const res = await fetchTriggerSubscription("sub-1")
+
+ const [url, opts] = get.mock.calls[0]
+ expect(url).toBe("https://api.test/triggers/subscriptions/sub-1")
+ expect(opts.params).toMatchObject({project_id: "proj-42"})
+ expect(res.subscription?.connection_id).toBe("conn-1")
+ })
+
+ it("falls back to an empty list when the subscriptions payload fails validation", async () => {
+ post.mockResolvedValueOnce({data: {subscriptions: "nope"}})
+
+ const res = await queryTriggerSubscriptions()
+
+ expect(res).toEqual({count: 0, subscriptions: []})
+ })
+})
+
+describe("deliveries (read-only)", () => {
+ it("queries deliveries for a subscription under the {delivery} envelope", async () => {
+ post.mockResolvedValueOnce({
+ data: {
+ count: 1,
+ deliveries: [
+ {
+ id: "del-1",
+ subscription_id: "sub-1",
+ event_id: "evt-123",
+ status: {type: "success", code: "200", timestamp: "2026-06-18T00:00:00Z"},
+ data: {event_key: "github_star_added_event", result: {ok: true}},
+ },
+ ],
+ },
+ })
+
+ const res = await queryTriggerDeliveries({subscription_id: "sub-1"})
+
+ const [url, body, opts] = post.mock.calls[0]
+ expect(url).toBe("https://api.test/triggers/deliveries/query")
+ expect(body).toEqual({delivery: {subscription_id: "sub-1"}})
+ expect(opts.params).toMatchObject({project_id: "proj-42"})
+ expect(res.deliveries[0].event_id).toBe("evt-123")
+ expect(res.deliveries[0].status.type).toBe("success")
+ })
+
+ it("falls back to an empty list when the deliveries payload fails validation", async () => {
+ post.mockResolvedValueOnce({data: {deliveries: 7}})
+
+ const res = await queryTriggerDeliveries()
+
+ expect(res).toEqual({count: 0, deliveries: []})
+ })
+})
diff --git a/web/packages/agenta-entity-ui/src/gatewayTrigger/drawers/TriggerDeliveriesDrawer.tsx b/web/packages/agenta-entity-ui/src/gatewayTrigger/drawers/TriggerDeliveriesDrawer.tsx
new file mode 100644
index 0000000000..aefa936709
--- /dev/null
+++ b/web/packages/agenta-entity-ui/src/gatewayTrigger/drawers/TriggerDeliveriesDrawer.tsx
@@ -0,0 +1,162 @@
+import {useMemo} from "react"
+
+import {
+ deliveriesDrawerAtom,
+ useTriggerDeliveries,
+ type TriggerDelivery,
+} from "@agenta/entities/gatewayTrigger"
+import {Editor} from "@agenta/ui/editor"
+import {Alert, Drawer, Empty, Spin, Table, Tag, Tooltip, Typography} from "antd"
+import type {ColumnsType} from "antd/es/table"
+import {useAtom} from "jotai"
+
+// ---------------------------------------------------------------------------
+// TriggerDeliveriesDrawer — read-only delivery history for one subscription.
+//
+// One audit row per inbound event dispatched to the bound workflow: status,
+// event_id, result/error, timestamps. The inbound dual of webhook deliveries.
+// ---------------------------------------------------------------------------
+
+function statusColor(type?: string | null): string {
+ switch ((type ?? "").toLowerCase()) {
+ case "success":
+ case "delivered":
+ case "ok":
+ return "green"
+ case "error":
+ case "failed":
+ case "failure":
+ return "red"
+ case "pending":
+ case "running":
+ return "blue"
+ default:
+ return "default"
+ }
+}
+
+export default function TriggerDeliveriesDrawer() {
+ const [state, setState] = useAtom(deliveriesDrawerAtom)
+ const open = !!state
+
+ const {deliveries, isLoading} = useTriggerDeliveries(state?.subscriptionId)
+
+ const columns: ColumnsType = useMemo(
+ () => [
+ {
+ title: "Status",
+ key: "status",
+ onHeaderCell: () => ({style: {minWidth: 120}}),
+ render: (_, record) => {
+ const type = record.status?.type ?? record.status?.code
+ return (
+
+ {type ?? "unknown"}
+
+ )
+ },
+ },
+ {
+ title: "Event ID",
+ dataIndex: "event_id",
+ key: "event_id",
+ onHeaderCell: () => ({style: {minWidth: 180}}),
+ render: (value: string) => (
+
+ {value}
+
+ ),
+ },
+ {
+ title: "Result",
+ key: "result",
+ onHeaderCell: () => ({style: {minWidth: 200}}),
+ render: (_, record) => {
+ if (record.data?.error) {
+ return (
+
+ {record.data.error}
+
+ )
+ }
+ const result = record.data?.result
+ if (!result || Object.keys(result).length === 0) {
+ return -
+ }
+ return (
+
+ {JSON.stringify(result)}
+
+ )
+ },
+ },
+ {
+ title: "When",
+ key: "timestamp",
+ onHeaderCell: () => ({style: {minWidth: 160}}),
+ render: (_, record) => {
+ const ts = record.status?.timestamp ?? record.created_at
+ return (
+
+ {ts ? new Date(ts).toLocaleString() : "-"}
+
+ )
+ },
+ },
+ ],
+ [],
+ )
+
+ return (
+ setState(null)}
+ title={`Deliveries${state?.subscriptionName ? ` · ${state.subscriptionName}` : ""}`}
+ width={720}
+ destroyOnClose
+ >
+ {isLoading ? (
+
+
+
+ ) : (
+
+ columns={columns}
+ dataSource={deliveries}
+ rowKey={(record) => record.id ?? record.event_id}
+ bordered
+ size="small"
+ pagination={false}
+ locale={{emptyText: }}
+ expandable={{
+ expandedRowRender: (record) =>
+ record.data?.error ? (
+
+ ) : (
+
+
+
+ ),
+ rowExpandable: (record) => !!record.data?.result || !!record.data?.error,
+ }}
+ />
+ )}
+
+ )
+}
diff --git a/web/packages/agenta-entity-ui/src/gatewayTrigger/drawers/TriggerSubscriptionDrawer.tsx b/web/packages/agenta-entity-ui/src/gatewayTrigger/drawers/TriggerSubscriptionDrawer.tsx
new file mode 100644
index 0000000000..edb631f5d7
--- /dev/null
+++ b/web/packages/agenta-entity-ui/src/gatewayTrigger/drawers/TriggerSubscriptionDrawer.tsx
@@ -0,0 +1,340 @@
+import {useCallback, useEffect, useMemo, useRef, useState} from "react"
+
+import {
+ subscriptionDrawerAtom,
+ useTriggerConnectionsQuery,
+ useTriggerEvent,
+ useTriggerSubscription,
+ type TriggerConnection,
+ type TriggerSubscriptionCreate,
+ type TriggerSubscriptionData,
+ type TriggerSubscriptionEdit,
+} from "@agenta/entities/gatewayTrigger"
+import {Editor} from "@agenta/ui/editor"
+import {Lightning} from "@phosphor-icons/react"
+import {Button, Divider, Drawer, Form, Input, Select, Spin, Switch, Typography, message} from "antd"
+import {useAtom} from "jotai"
+
+import SchemaForm, {type SchemaFormHandle} from "../../gatewayTool/components/SchemaForm"
+import {
+ EntityPicker,
+ workflowRevisionAdapter,
+ type WorkflowRevisionSelectionResult,
+} from "../../selection"
+
+const DEFAULT_PROVIDER = "composio"
+
+// ---------------------------------------------------------------------------
+// TriggerSubscriptionDrawer (root) — create or edit a subscription.
+//
+// Binds a provider event (catalog) on a connected integration to a workflow
+// revision. Edits are full-PUT: the body is sourced from the freshly-fetched
+// subscription and only owned fields are overridden.
+// ---------------------------------------------------------------------------
+
+export default function TriggerSubscriptionDrawer() {
+ const [state, setState] = useAtom(subscriptionDrawerAtom)
+ const open = !!state
+ const isEdit = !!state?.subscriptionId
+
+ const handleClose = useCallback(() => setState(null), [setState])
+
+ return (
+
+ {state && (
+
+ )}
+
+ )
+}
+
+// ---------------------------------------------------------------------------
+// Subscription form
+// ---------------------------------------------------------------------------
+
+function SubscriptionForm({onClose}: {onClose: () => void}) {
+ const [state] = useAtom(subscriptionDrawerAtom)
+ const subscriptionId = state?.subscriptionId
+ const isEdit = !!subscriptionId
+
+ const {connections, isLoading: connectionsLoading} = useTriggerConnectionsQuery()
+ const {
+ subscription,
+ isLoading: subLoading,
+ isMutating,
+ create,
+ edit,
+ } = useTriggerSubscription(subscriptionId)
+
+ const [name, setName] = useState("")
+ const [connectionId, setConnectionId] = useState(state?.connectionId)
+ const [eventKey, setEventKey] = useState("")
+ const [enabled, setEnabled] = useState(true)
+ const [workflowRevId, setWorkflowRevId] = useState(null)
+ const [workflowLabel, setWorkflowLabel] = useState(null)
+ const [inputsText, setInputsText] = useState("{}")
+ const [inputsError, setInputsError] = useState(null)
+
+ const [configForm] = Form.useForm()
+ const configFormRef = useRef(null)
+
+ // Prefill from the freshly-fetched subscription (edit mode).
+ useEffect(() => {
+ if (!isEdit || !subscription) return
+ setName(subscription.name ?? "")
+ setConnectionId(subscription.connection_id)
+ setEventKey(subscription.data?.event_key ?? "")
+ setEnabled(subscription.enabled ?? true)
+ const wfId = subscription.data?.references?.workflow_revision?.id ?? null
+ setWorkflowRevId(wfId)
+ setWorkflowLabel(wfId)
+ setInputsText(JSON.stringify(subscription.data?.inputs_fields ?? {}, null, 2))
+ }, [isEdit, subscription])
+
+ const selectedConnection = useMemo(
+ () => connections.find((c) => c.id === connectionId),
+ [connections, connectionId],
+ )
+
+ const integrationKey = selectedConnection?.integration_key ?? ""
+
+ // trigger_config schema for the chosen event (catalog detail).
+ const {event: eventDetail, isLoading: eventLoading} = useTriggerEvent(integrationKey, eventKey)
+ const triggerConfigSchema = (eventDetail?.trigger_config ?? null) as Record<
+ string,
+ unknown
+ > | null
+
+ // Seed the config form with existing trigger_config on edit.
+ useEffect(() => {
+ if (isEdit && subscription?.data?.trigger_config) {
+ configForm.setFieldsValue(subscription.data.trigger_config)
+ }
+ }, [isEdit, subscription, configForm])
+
+ const handleSubmit = useCallback(async () => {
+ if (!connectionId) {
+ message.error("Select a connection")
+ return
+ }
+ if (!eventKey) {
+ message.error("Select an event")
+ return
+ }
+ if (!workflowRevId) {
+ message.error("Bind a workflow")
+ return
+ }
+
+ let inputsFields: Record = {}
+ try {
+ inputsFields = inputsText.trim() ? JSON.parse(inputsText) : {}
+ setInputsError(null)
+ } catch {
+ setInputsError("Invalid JSON")
+ message.error("inputs mapping is not valid JSON")
+ return
+ }
+
+ let triggerConfig: Record | undefined
+ try {
+ triggerConfig = (await configFormRef.current?.getValues()) ?? undefined
+ } catch {
+ // form validation failed
+ return
+ }
+
+ const data: TriggerSubscriptionData = {
+ event_key: eventKey,
+ trigger_config: triggerConfig,
+ inputs_fields: inputsFields,
+ references: {workflow_revision: {id: workflowRevId}},
+ }
+
+ try {
+ if (isEdit && subscription) {
+ // Full PUT — carry the whole entity, override owned fields.
+ const body: TriggerSubscriptionEdit = {
+ id: subscription.id as string,
+ name: name || null,
+ description: subscription.description ?? null,
+ flags: subscription.flags ?? null,
+ tags: subscription.tags ?? null,
+ meta: subscription.meta ?? null,
+ connection_id: connectionId,
+ data: {...subscription.data, ...data},
+ enabled,
+ valid: subscription.valid ?? true,
+ }
+ const result = await edit(body)
+ if (!result) {
+ message.error("Failed to update subscription")
+ return
+ }
+ message.success("Subscription updated")
+ } else {
+ const body: TriggerSubscriptionCreate = {
+ name: name || null,
+ connection_id: connectionId,
+ data,
+ }
+ const result = await create(body)
+ if (!result) {
+ message.error("Failed to create subscription")
+ return
+ }
+ message.success("Subscription created")
+ }
+ onClose()
+ } catch {
+ message.error("Failed to save subscription")
+ }
+ }, [
+ connectionId,
+ eventKey,
+ workflowRevId,
+ inputsText,
+ isEdit,
+ subscription,
+ name,
+ enabled,
+ edit,
+ create,
+ onClose,
+ ])
+
+ if (isEdit && subLoading) {
+ return (
+
+
+
+ )
+ }
+
+ return (
+
+
+
+ setName(e.target.value)}
+ />
+
+
+
+
+
+
+ }
+ value={eventKey}
+ onChange={(e) => setEventKey(e.target.value)}
+ disabled={!connectionId}
+ />
+
+ Provider: {DEFAULT_PROVIDER}
+ {integrationKey ? ` · ${integrationKey}` : ""}
+
+
+
+
+
+
+ variant="popover-cascader"
+ adapter={workflowRevisionAdapter}
+ onSelect={(selection) => {
+ setWorkflowRevId(selection.id)
+ setWorkflowLabel(selection.label)
+ }}
+ size="small"
+ placeholder={workflowLabel ?? "Select workflow revision"}
+ />
+ {workflowLabel && (
+
+ {workflowLabel}
+
+ )}
+
+
+
+
+
+
+ Trigger configuration
+
+
+ {eventLoading ? (
+
+
+
+ ) : (
+
+ )}
+
+
+
+
+ setInputsText(textContent)}
+ codeOnly
+ showToolbar={false}
+ language="json"
+ dimensions={{width: "100%", height: 120}}
+ disabled={isMutating}
+ />
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ )
+}
diff --git a/web/packages/agenta-entity-ui/src/gatewayTrigger/index.ts b/web/packages/agenta-entity-ui/src/gatewayTrigger/index.ts
index e7b4348ef1..4ea0d0551d 100644
--- a/web/packages/agenta-entity-ui/src/gatewayTrigger/index.ts
+++ b/web/packages/agenta-entity-ui/src/gatewayTrigger/index.ts
@@ -8,3 +8,5 @@
*/
export {default as TriggerEventsDrawer} from "./drawers/TriggerEventsDrawer"
+export {default as TriggerSubscriptionDrawer} from "./drawers/TriggerSubscriptionDrawer"
+export {default as TriggerDeliveriesDrawer} from "./drawers/TriggerDeliveriesDrawer"