Triglit
Blog

Como conectar workflows no seu SaaS em minutos

João Pedro
João Pedro
4 min de leitura
29 de nov. de 25

Se você é CTO ou Tech Lead de um SaaS B2B, provavelmente já viveu este cenário:

O Product Manager chega com uma "funcionalidade simples": “Precisamos permitir que o usuário crie uma regra onde, se um ticket chegar fora do horário comercial, ele seja atribuído a um atendente de plantão e envie um SMS.”

Sua mente de engenheiro já começa a calcular o débito técnico. Não é apenas uma regra if/else. Você precisa de uma interface drag-and-drop, gestão de retentativas, logs de execução, validação de segurança e escalabilidade para milhares de tenants.

O que parecia uma sprint de 3 dias vira um roadmap de 3 meses construindo infraestrutura, e não core business.

Aqui no Triglit, nós nascemos justamente para eliminar esse "trabalho de encanador". Hoje, quero mostrar na prática — código na tela — como você pode integrar uma engine de workflows completa no seu SaaS usando Next.js em menos de uma hora.

Vamos construir um caso de uso real: um sistema de distribuição de atendentes e envio de mensagens.

O que vamos construir

Neste tutorial, vamos integrar o Triglit em uma aplicação Next.js 14+ (App Router).

  • Frontend: Usaremos o @triglit/react-sdk para dar ao seu usuário final uma interface visual de workflows.

  • Backend: Usaremos o SDK TypeScript para criar Custom Nodes (nós personalizados) que executam a lógica de negócio do seu SaaS.

Passo 1: Preparando o terreno

Primeiro, vamos instalar os pacotes necessários no seu projeto. A beleza aqui é que o overhead é mínimo.

pnpm add triglit @triglit/react-sdk

Configure suas variáveis de ambiente no .env. Você vai precisar das chaves de API do painel do Triglit.

# .env.local
TRIGLIT_SECRET_KEY=sk_live_xxx          # Sua chave secreta (Backend)
TRIGLIT_PUBLIC_KEY=pk_live_xxx          # Chave pública (Frontend)
TRIGLIT_CNP_SECRET=cnp_secret_xxx       # Secret para validar que a requisição vem do Triglit
NEXT_PUBLIC_TRIGLIT_PUBLIC_KEY=pk_live_xxx

Passo 2: A Lógica de Negócio (Seu "Molho Secreto")

O maior erro das ferramentas de automação antigas é tentar substituir sua lógica de negócio. O Triglit faz o oposto: nós orquestramos, você executa.

Vamos criar um serviço (lib/triglit-service.ts) que simula duas ações do seu SaaS: enviar uma mensagem e escolher um agente para atendimento.

// lib/triglit-service.ts
export class TriglitService {
  /**
   * Envia mensagem para um usuário final (simulação)
   */
  async sendMessage(
    tenantId: string,
    entityId: string,
    content: string
  ): Promise<void> {
    console.log(`[Tenant: ${tenantId}] Enviando mensagem para ${entityId}:`, content);
    // Aqui entraria sua chamada real para Twilio, WhatsApp, etc.
  }

  /**
   * Distribui um agente disponível para atendimento
   */
  async distributeAgent(
    tenantId: string,
    entityId: string,
    agentIds: string[],
    fallbackAgentId?: string
  ): Promise<{ selectedAgentId: string; wasFallbackSelected: boolean }> {
    // Lógica real: buscar no seu DB quem está online, quem tem menos tickets, etc.
    const availableAgents = agentIds; 

    if (availableAgents.length > 0) {
      // Exemplo simples: pega o primeiro. Poderia ser Round-Robin.
      const selectedAgentId = availableAgents[0];
      return {
        selectedAgentId,
        wasFallbackSelected: false,
      };
    }
    
    if (fallbackAgentId) {
      return {
        selectedAgentId: fallbackAgentId,
        wasFallbackSelected: true,
      };
    }
    
    throw new Error("Nenhum agente disponível");
  }
}

export const triglitService = new TriglitService();

Passo 3: Criando os "Custom Nodes"

Agora, a mágica acontece. Vamos expor essas funções para o motor de workflows do Triglit através de uma API Route do Next.js.

É aqui que garantimos a segurança. O Triglit assina cada requisição, garantindo que ninguém externo possa acionar suas funções de automação indevidamente.

Crie o arquivo app/api/triglit/route.ts:

// app/api/triglit/route.ts
import { NextRequest, NextResponse } from "next/server";
import Triglit from "triglit"; // Assumindo cliente inicializado em lib/triglit-client
import { triglitService } from "@/lib/triglit-service";

// Inicialize o cliente (idealmente em lib/triglit-client.ts)
const triglitClient = new Triglit({ apiKey: process.env.TRIGLIT_SECRET_KEY });

export async function POST(req: NextRequest) {
  try {
    // 1. Segurança: Validação da assinatura Triglit
    const signature = req.headers.get("x-triglit-signature");
    if (!signature) {
      return NextResponse.json({ error: "Assinatura ausente" }, { status: 400 });
    }

    const rawBody = await req.text();
    const secret = process.env.TRIGLIT_CNP_SECRET!;
    
    const isValid = await triglitClient.customNodes.validateCNPSignature(
      rawBody,
      signature,
      secret
    );

    if (!isValid) {
      return NextResponse.json({ error: "Assinatura inválida" }, { status: 401 });
    }

    // 2. Processamento do Payload
    const body = JSON.parse(rawBody);

    // Heartbeat: O Triglit "pinga" sua URL para saber se ela está viva
    if (body.type === "heartbeat") {
      return NextResponse.json({ status: "ok" });
    }

    const { subTenantId: tenantId, entityId, nodeType, config } = body.payload;

    // 3. Roteamento da Execução
    switch (nodeType) {
      case "send-message": {
        await triglitService.sendMessage(tenantId, entityId, config.content);
        return NextResponse.json({ status: "completed", outputs: {} });
      }

      case "distribute-agent": {
        const result = await triglitService.distributeAgent(
          tenantId,
          entityId,
          config.agents,
          config.fallbackAgent
        );
        return NextResponse.json({
          status: "completed",
          outputs: {
            selectedAgentId: result.selectedAgentId,
            wasFallbackSelected: result.wasFallbackSelected,
          },
        });
      }
      
      default:
        return NextResponse.json({ status: "failed", error: "Node desconhecido" });
    }
  } catch (error) {
    console.error("Erro no Custom Node:", error);
    return NextResponse.json({ status: "failed", error: "Erro interno" }, { status: 500 });
  }
}

Passo 4: Entregando a interface para o usuário

Até agora fizemos o backend. Mas o valor percebido pelo seu cliente está na capacidade de ele mesmo montar o fluxo.

Com o @triglit/react-sdk, você embarca o editor visual diretamente na sua página Next.js. Diferente de soluções anteriores, você deve envolver o editor no TriglitProvider e garantir a importação dos estilos.

// app/workflows/page.tsx
"use client";
import { TriglitProvider, WorkflowEditor } from "@triglit/react-sdk";
import "@triglit/react-sdk/styles.css";

export default function WorkflowsPage() {
  return (
    <div className="container mx-auto p-6 h-screen flex flex-col">
      <h1 className="text-2xl font-bold mb-6 text-slate-800">
        Automações de Atendimento
      </h1>
      
      <div className="flex-1 border rounded-lg overflow-hidden shadow-sm">
        <TriglitProvider
          config={{
            apiKey: process.env.NEXT_PUBLIC_TRIGLIT_PUBLIC_KEY!,
          }}
        >
          <WorkflowEditor
            workflowId="wf_exemplo_123" // ID do workflow a ser editado
            onSave={(versionId) => {
              console.log("Nova versão salva:", versionId);
            }}
            className="h-full"
          />
        </TriglitProvider>
      </div>
    </div>
  );
}

O Resultado Final

Em menos de 200 linhas de código, você implementou:

  1. Orquestração de Eventos: O Triglit cuida do "quando" executar.

  2. UI No-Code: Seu usuário ganha um builder visual profissional.

  3. Segurança Enterprise: Validação de assinaturas e isolamento de tenants.

  4. Extensibilidade: Se amanhã você precisar de um bloco "Gerar Nota Fiscal", basta adicionar um novo case na API e um objeto no frontend.

Por que isso muda o jogo para SaaS?

Construir isso do zero ("in-house") exigiria lidar com filas (RabbitMQ/Kafka/SQS), gestão de estado de execução, retentativas exponenciais e construção de UI complexa (React Flow/D3).

Com a abordagem de Automação Embarcada do Triglit, você foca na regra de negócio (distributeAgent), e nós garantimos que ela rode na hora certa, na ordem certa, para o cliente certo.

Quer ver isso rodando no seu produto? A documentação completa está disponível em docs.triglit.com. Se você está construindo um SaaS e quer pular os meses de desenvolvimento de infraestrutura, peça acesso ao trial.

Estamos construindo a camada de automação para que você possa focar no seu produto. 🚀

Pronto para começar?

Junte-se a desenvolvedores que estão acelerando seus produtos com automação embedável.