Skip to main content
Custom nodes permitem executar lógica personalizada nos seus próprios servidores, integrando o Triglit completamente com sua infraestrutura.

O que são Custom Nodes?

Custom nodes são nodes que executam em seus próprios servidores via webhooks. Quando um custom node é executado:
  1. O Triglit faz uma requisição HTTP para seu endpoint
  2. Seu servidor processa a requisição
  3. Você retorna o resultado
  4. O Triglit continua a execução do workflow

Configuração

1. Registrar Custom Node

No dashboard do Triglit, registre seu custom node:
{
  "id": "custom-payment-processor",
  "name": "Processar Pagamento",
  "endpoint": "https://api.seudominio.com/triglit/custom-nodes",
  "description": "Processa pagamentos via gateway"
}

2. Configurar Endpoint

Seu endpoint deve aceitar requisições POST:
// Express.js example
app.post('/triglit/custom-nodes', async (req, res) => {
  const { nodeId, context, config } = req.body;
  
  // Processar lógica customizada
  const result = await processPayment(context.data.input);
  
  // Retornar resultado
  res.json({
    success: true,
    data: result
  });
});

Estrutura da Requisição

Quando o Triglit chama seu custom node:
{
  "nodeId": "custom-payment-processor",
  "context": {
    "data": {
      "input": { "orderId": "order_123", "amount": 100.00 },
      "node-1": { "result": "validated" }
    },
    "metadata": {
      "runId": "run_456",
      "entityId": "order_123",
      "tenantId": "tenant_abc"
    }
  },
  "config": {
    "action": "process-payment",
    "gateway": "stripe"
  }
}

Estrutura da Resposta

Seu endpoint deve retornar:
{
  "success": true,
  "data": {
    "paymentId": "pay_123",
    "status": "succeeded",
    "amount": 100.00
  }
}

Resposta de Erro

{
  "success": false,
  "error": {
    "message": "Payment failed",
    "code": "PAYMENT_DECLINED"
  }
}

Autenticação

O Triglit envia autenticação no header:
X-Triglit-Signature: signature_hash
X-Triglit-Timestamp: 1640995200
Valide a assinatura:
const crypto = require('crypto');

function validateSignature(body, signature, timestamp, secret) {
  const payload = `${timestamp}.${JSON.stringify(body)}`;
  const hash = crypto
    .createHmac('sha256', secret)
    .update(payload)
    .digest('hex');
  
  return hash === signature;
}

app.post('/triglit/custom-nodes', express.raw({ type: 'application/json' }), (req, res) => {
  const signature = req.headers['x-triglit-signature'];
  const timestamp = req.headers['x-triglit-timestamp'];
  
  if (!validateSignature(req.body, signature, timestamp, process.env.TRIGLIT_WEBHOOK_SECRET)) {
    return res.status(401).json({ error: 'Invalid signature' });
  }
  
  // Processar requisição
});

Exemplo Completo

Node de Processamento de Pagamento

// Node: Processar Pagamento
app.post('/triglit/custom-nodes', async (req, res) => {
  try {
    const { nodeId, context, config } = req.body;
    
    // Validar autenticação
    if (!validateSignature(req.body, req.headers['x-triglit-signature'], 
                          req.headers['x-triglit-timestamp'], 
                          process.env.TRIGLIT_WEBHOOK_SECRET)) {
      return res.status(401).json({ error: 'Invalid signature' });
    }
    
    // Extrair dados do contexto
    const orderData = context.data.input;
    
    // Processar pagamento
    const payment = await stripe.charges.create({
      amount: orderData.amount * 100, // em centavos
      currency: 'usd',
      source: orderData.paymentToken,
      description: `Order ${orderData.orderId}`
    });
    
    // Retornar resultado
    res.json({
      success: true,
      data: {
        paymentId: payment.id,
        status: payment.status,
        amount: payment.amount / 100
      }
    });
    
  } catch (error) {
    res.status(500).json({
      success: false,
      error: {
        message: error.message,
        code: 'PAYMENT_ERROR'
      }
    });
  }
});

Usar no Workflow

{
  "nodes": [
    {
      "id": "process-payment",
      "type": "custom",
      "config": {
        "nodeId": "custom-payment-processor",
        "endpoint": "https://api.seudominio.com/triglit/custom-nodes",
        "method": "POST",
        "params": {
          "action": "process-payment",
          "gateway": "stripe"
        }
      }
    }
  ]
}

Timeout e Retries

Timeout

Configure timeout no node:
{
  "type": "custom",
  "config": {
    "timeout": 30000  // 30 segundos
  }
}

Retries

O Triglit retenta automaticamente em caso de falha:
{
  "type": "custom",
  "config": {
    "retry": {
      "maxAttempts": 3,
      "backoff": "exponential",
      "initialDelay": 1000
    }
  }
}

Idempotência

Torne seus custom nodes idempotentes:
app.post('/triglit/custom-nodes', async (req, res) => {
  const { context, config } = req.body;
  const orderId = context.data.input.orderId;
  
  // Verificar se já foi processado
  const existing = await db.payments.findOne({ orderId });
  if (existing) {
    return res.json({
      success: true,
      data: existing,
      idempotent: true
    });
  }
  
  // Processar pagamento
  const payment = await processPayment(orderId);
  
  // Salvar resultado
  await db.payments.create(payment);
  
  res.json({
    success: true,
    data: payment
  });
});

Boas Práticas

Sempre valide dados de entrada antes de processar.
Torne nodes idempotentes para permitir retries seguros.
Configure timeouts realistas baseados na complexidade da operação.
Sempre retorne erros estruturados e informativos.
Logue todas as execuções para debugging e auditoria.
Valide assinaturas e use HTTPS sempre.

Limitações

  • Timeout máximo: 5 minutos por node
  • Tamanho de payload: 1MB
  • Retries: Máximo de 3 tentativas (configurável)
Para operações longas, considere retornar imediatamente e processar de forma assíncrona, notificando o Triglit via webhook quando concluído.