> ## Documentation Index
> Fetch the complete documentation index at: https://docs.triglit.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Custom Nodes

> Crie nodes personalizados para executar lógica customizada nos seus servidores

**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:

```json theme={null}
{
  "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:

```javascript theme={null}
// 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:

```json theme={null}
{
  "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:

```json theme={null}
{
  "success": true,
  "data": {
    "paymentId": "pay_123",
    "status": "succeeded",
    "amount": 100.00
  }
}
```

### Resposta de Erro

```json theme={null}
{
  "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:

```javascript theme={null}
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

```javascript theme={null}
// 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

```json theme={null}
{
  "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:

```json theme={null}
{
  "type": "custom",
  "config": {
    "timeout": 30000  // 30 segundos
  }
}
```

### Retries

O Triglit retenta automaticamente em caso de falha:

```json theme={null}
{
  "type": "custom",
  "config": {
    "retry": {
      "maxAttempts": 3,
      "backoff": "exponential",
      "initialDelay": 1000
    }
  }
}
```

## Idempotência

Torne seus custom nodes idempotentes:

```javascript theme={null}
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

<AccordionGroup>
  <Accordion title="Validação de Entrada">
    Sempre valide dados de entrada antes de processar.
  </Accordion>

  <Accordion title="Idempotência">
    Torne nodes idempotentes para permitir retries seguros.
  </Accordion>

  <Accordion title="Timeouts Apropriados">
    Configure timeouts realistas baseados na complexidade da operação.
  </Accordion>

  <Accordion title="Tratamento de Erros">
    Sempre retorne erros estruturados e informativos.
  </Accordion>

  <Accordion title="Logging">
    Logue todas as execuções para debugging e auditoria.
  </Accordion>

  <Accordion title="Segurança">
    Valide assinaturas e use HTTPS sempre.
  </Accordion>
</AccordionGroup>

## Limitações

* **Timeout máximo**: 5 minutos por node
* **Tamanho de payload**: 1MB
* **Retries**: Máximo de 3 tentativas (configurável)

<Tip>
  Para operações longas, considere retornar imediatamente e processar de forma assíncrona, notificando o Triglit via webhook quando concluído.
</Tip>
