feat: add automation api bindings

This commit is contained in:
Matthew Meszaros
2026-06-08 06:55:24 +02:00
parent b28fc9e957
commit f08464ce10
10 changed files with 189 additions and 0 deletions
@@ -0,0 +1,11 @@
import type { Automation, AutomationWrite } from "@/lib/api/models/app/automations/Automation";
import Request from "../../Request";
export default async function createAutomation(w: AutomationWrite): Promise<{ automation: Automation }> {
return await Request<{ automation: Automation }>({
method: "POST",
url: "/automations",
data: w,
authorization: true,
});
}
@@ -0,0 +1,9 @@
import Request from "../../Request";
export default async function deleteAutomation(id: string): Promise<{ deleted: boolean }> {
return await Request<{ deleted: boolean }>({
method: "DELETE",
url: `/automations/${id}`,
authorization: true,
});
}
@@ -0,0 +1,10 @@
import type { Automation } from "@/lib/api/models/app/automations/Automation";
import Request from "../../Request";
export default async function getAutomation(id: string): Promise<{ automation: Automation }> {
return await Request<{ automation: Automation }>({
method: "GET",
url: `/automations/${id}`,
authorization: true,
});
}
@@ -0,0 +1,10 @@
import type { Automation } from "@/lib/api/models/app/automations/Automation";
import Request from "../../Request";
export default async function listAutomations(): Promise<{ automations: Automation[] }> {
return await Request<{ automations: Automation[] }>({
method: "GET",
url: "/automations",
authorization: true,
});
}
@@ -0,0 +1,11 @@
import type { Automation, AutomationWrite } from "@/lib/api/models/app/automations/Automation";
import Request from "../../Request";
export default async function updateAutomation(id: string, w: AutomationWrite): Promise<{ automation: Automation }> {
return await Request<{ automation: Automation }>({
method: "PATCH",
url: `/automations/${id}`,
data: w,
authorization: true,
});
}
@@ -0,0 +1,11 @@
import { useQuery } from "@tanstack/react-query";
import getAutomation from "@/lib/api/client/app/automations/getAutomation";
export function useAutomation(id: string, enabled = true) {
return useQuery({
queryKey: ["automations", id],
queryFn: () => getAutomation(id),
enabled: enabled && !!id,
staleTime: 15_000,
});
}
@@ -0,0 +1,32 @@
import { useMutation, useQueryClient } from "@tanstack/react-query";
import createAutomation from "@/lib/api/client/app/automations/createAutomation";
import updateAutomation from "@/lib/api/client/app/automations/updateAutomation";
import deleteAutomation from "@/lib/api/client/app/automations/deleteAutomation";
import type { AutomationWrite } from "@/lib/api/models/app/automations/Automation";
export function useCreateAutomation() {
const qc = useQueryClient();
return useMutation({
mutationFn: (w: AutomationWrite) => createAutomation(w),
onSuccess: () => void qc.invalidateQueries({ queryKey: ["automations"] }),
});
}
export function useUpdateAutomation() {
const qc = useQueryClient();
return useMutation({
mutationFn: ({ id, w }: { id: string; w: AutomationWrite }) => updateAutomation(id, w),
onSuccess: (_res, vars) => {
void qc.invalidateQueries({ queryKey: ["automations"] });
void qc.invalidateQueries({ queryKey: ["automations", vars.id] });
},
});
}
export function useDeleteAutomation() {
const qc = useQueryClient();
return useMutation({
mutationFn: (id: string) => deleteAutomation(id),
onSuccess: () => void qc.invalidateQueries({ queryKey: ["automations"] }),
});
}
@@ -0,0 +1,10 @@
import { useQuery } from "@tanstack/react-query";
import listAutomations from "@/lib/api/client/app/automations/listAutomations";
export function useAutomations() {
return useQuery({
queryKey: ["automations"],
queryFn: listAutomations,
staleTime: 15_000,
});
}
@@ -0,0 +1,32 @@
import type { IntegrationAction } from "@/lib/api/models/app/integrations/Integration";
// One action node of an automation: run `action` on `connection_id` with config.
export interface AutomationStep {
id?: string;
connection_id: string;
action: IntegrationAction;
config?: Record<string, unknown>;
}
// An automation = one trigger event -> a set of action steps. Execution reuses
// the integration event-subscription dispatcher (each step is a subscription).
export interface Automation {
id: string;
organization_id: string;
name: string;
enabled: boolean;
trigger_event: string;
filter?: Record<string, unknown>;
created_at: string;
updated_at: string;
steps: AutomationStep[];
}
// Create/update payload from the flow builder.
export interface AutomationWrite {
name: string;
enabled: boolean;
trigger_event: string;
filter?: Record<string, unknown>;
steps: AutomationStep[];
}
@@ -0,0 +1,53 @@
// Trigger + action vocabulary the Automations flow builder renders from. Kept in
// sync with the backend's subscribable events (catalog crmEvents/notifyEvents)
// and the per-provider action handlers.
import { EVENT_LABELS } from "@/lib/api/models/app/integrations/Integration";
// Warmbly events that can trigger an automation. Order = how they're listed.
export const TRIGGER_EVENTS: string[] = [
"campaign.reply_received",
"meeting.booked",
"meeting.rescheduled",
"meeting.canceled",
"campaign.email_bounced",
"campaign.unsubscribed",
"warmup.health_changed",
"deliverability.complaint",
];
export function triggerLabel(ev: string): string {
return EVENT_LABELS[ev] ?? ev;
}
// Human labels for the provider action handlers (the action a step performs).
export const ACTION_LABELS: Record<string, string> = {
"slack.notify": "Send a Slack message",
"discord.notify": "Send a Discord message",
"hubspot.upsert_contact": "Create / update HubSpot contact",
"pipedrive.upsert_person": "Create / update Pipedrive person",
"salesforce.upsert_contact": "Create / update Salesforce contact",
"close.upsert_lead": "Create / update Close lead",
"webhook.ping": "Send a webhook",
};
export function actionLabel(a: string): string {
return ACTION_LABELS[a] ?? a;
}
// Per-action config field needs, so the node editor shows the right inputs.
export function actionNeedsChannel(action: string): boolean {
return action === "slack.notify";
}
export function actionNeedsURL(action: string): boolean {
return action === "webhook.ping";
}
// Notify-style actions accept an optional custom message template.
export function actionSupportsTemplate(action: string): boolean {
return action === "slack.notify" || action === "discord.notify" || action === "webhook.ping";
}
// Only the reply trigger carries intent / confidence filters.
export function triggerSupportsIntentFilter(ev: string): boolean {
return ev === "campaign.reply_received";
}