import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query"; import { apiClient } from "../lib/apiClient"; import { Webhook, Plus, Play, Trash2, CheckCircle2, AlertCircle, Code, ExternalLink, Copy, Terminal } from "lucide-react"; import { useState } from "react"; import StatusBadge from "../components/StatusBadge"; import FormDrawer from "../components/FormDrawer"; import ConfirmModal from "../components/ConfirmModal"; import toast from "react-hot-toast"; const AVAILABLE_EVENTS = [ { id: "EMAIL_REPLIED", label: "Email Replied" }, { id: "EMAIL_OPENED", label: "Email Opened" }, { id: "EMAIL_CLICKED", label: "Link Clicked" }, { id: "EMAIL_BOUNCED", label: "Email Bounced" }, { id: "LEAD_CATEGORY_UPDATED", label: "Lead Category Updated" }, { id: "EMAIL_UNSUBSCRIBED", label: "Lead Unsubscribed" }, { id: "CAMPAIGN_COMPLETED", label: "Campaign Completed" }, ]; export default function Webhooks() { const queryClient = useQueryClient(); const [isCreateOpen, setIsCreateOpen] = useState(false); const [selectedForDelete, setSelectedForDelete] = useState(null); const [testExecutionResult, setTestExecutionResult] = useState(null); const { data: webhooks = [], isLoading, refetch } = useQuery({ queryKey: ["webhooks"], queryFn: async () => { const res = await apiClient.get("/webhooks"); return res.data; }, }); const { data: campaigns = [] } = useQuery({ queryKey: ["campaigns"], queryFn: async () => { const res = await apiClient.get("/campaigns"); return res.data; }, }); const testFireMutation = useMutation({ mutationFn: async (id: string) => { const res = await apiClient.post(`/webhooks/${id}/test`); return res.data; }, onSuccess: (data) => { toast.success("Webhook test payload dispatched!"); setTestExecutionResult(data); }, onError: () => { toast.error("Failed to fire test webhook"); }, }); const deleteMutation = useMutation({ mutationFn: async (id: string) => { const res = await apiClient.delete(`/webhooks/${id}`); return res.data; }, onSuccess: () => { toast.success("Webhook endpoint removed"); setSelectedForDelete(null); refetch(); }, }); return (
{/* Header */}

Event Webhooks & Integrations

Stream real-time outreach events directly to your CRM, Slack, Zapier, or backend server.

{/* Webhooks Table */}
{webhooks.map((hook: any) => ( ))} {webhooks.length === 0 && ( )}
Endpoint URL Campaign Scope Subscribed Events Status Actions
{hook.target_url}
{hook.campaign_id ? `Campaign: ${hook.campaign_id}` : "Global (All Campaigns)"}
{hook.event_types?.map((ev: string) => ( {ev} ))}
No webhooks configured yet. Create one to receive real-time JSON payloads.
{/* Test Fire Response Inspector */} {testExecutionResult && (

Live Webhook Delivery Simulation (200 OK)

            {JSON.stringify(testExecutionResult, null, 2)}
          
)} {/* Delete Confirmation */} setSelectedForDelete(null)} onConfirm={() => selectedForDelete && deleteMutation.mutate(selectedForDelete)} title="Delete Webhook" description="Are you sure you want to delete this webhook endpoint? No further notifications will be dispatched to this URL." /> {/* Create Webhook Drawer */} setIsCreateOpen(false)} title="Create Webhook Subscription" subtitle="Specify the destination HTTPS endpoint and target event triggers." >
{ e.preventDefault(); const form = e.target as HTMLFormElement; const target_url = (form.elements.namedItem("target_url") as HTMLInputElement).value; const campaign_id = (form.elements.namedItem("campaign_id") as HTMLSelectElement).value; const checkedEvents = AVAILABLE_EVENTS.filter( (ev) => (form.elements.namedItem(ev.id) as HTMLInputElement)?.checked ).map((ev) => ev.id); if (checkedEvents.length === 0) { toast.error("Please select at least one event trigger"); return; } try { await apiClient.post("/webhooks", { target_url, campaign_id: campaign_id === "ALL" ? null : campaign_id, event_types: checkedEvents, }); toast.success("Webhook configured successfully!"); setIsCreateOpen(false); refetch(); } catch (err: any) { toast.error("Failed to create webhook"); } }} className="space-y-4 text-xs" >
{AVAILABLE_EVENTS.map((ev) => ( ))}
); }