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

# Eventos de Webhook de Transações

> Receba notificações em tempo real quando transações forem criadas ou atualizadas — com eventos webhook gu1 para integração downstream em tempo real.

## Visão Geral

Os eventos de webhook de transações permitem que você receba notificações em tempo real quando transações são criadas ou atualizadas em sua organização. Gu1 envia automaticamente solicitações HTTP POST para seu endpoint de webhook configurado, permitindo que você automatize fluxos de trabalho de monitoramento de transações, detecção de fraude e conformidade regulatória.

## Por Que Usar Webhooks de Transações?

<CardGroup cols={2}>
  <Card title="Monitoramento em Tempo Real" icon="bolt">
    Receba notificações instantâneas sobre transações novas ou atualizadas
  </Card>

  <Card title="Detecção de Fraude" icon="shield-halved">
    Implemente verificações de segurança adicionais em tempo real
  </Card>

  <Card title="Automação de Workflows" icon="robot">
    Acione processos automáticos baseados em atividade transacional
  </Card>

  <Card title="Auditoria e Conformidade" icon="clipboard-check">
    Mantenha registros de auditoria sincronizados em todos os seus sistemas
  </Card>
</CardGroup>

## Eventos Disponíveis

Gu1 envia webhooks para os seguintes eventos de transações:

| Tipo de Evento               | Descrição            | Quando Acionado                                                                                                      |
| ---------------------------- | -------------------- | -------------------------------------------------------------------------------------------------------------------- |
| `transaction.created`        | Transação criada     | Quando uma nova transação é registrada no sistema                                                                    |
| `transaction.updated`        | Transação atualizada | Quando o status ou outros campos são atualizados (instantâneo completo no payload)                                   |
| `transaction.status_changed` | Mudança de status    | Quando o **status** da transação muda (painel, API ou outros fluxos). Payload focado em `previousStatus` → `status`. |

<Note>
  Os eventos `transaction.created` e `transaction.updated` estão atualmente em desenvolvimento e serão ativados em breve. A documentação está disponível para preparar sua integração.

  **Disponível hoje:** `transaction.status_changed` é emitido quando o status muda. O Gu1 também pode enviar **`transaction.updated`** na mesma alteração (payload mais rico com valores, origem/destino, etc.). Inscreva-se em um ou em ambos conforme precise do instantâneo completo ou apenas da transição.
</Note>

## Estrutura do Payload do Evento

Todos os eventos de webhook de transações seguem esta estrutura padrão:

```json theme={null}
{
  "event": "transaction.created",
  "timestamp": "2025-01-29T15:30:00Z",
  "organizationId": "org-123",
  "payload": {
    "transactionId": "550e8400-e29b-41d4-a716-446655440000",
    "externalId": "txn_abc123",
    "type": "payment",
    "status": "CREATED",
    "amount": 5000.00,
    "currency": "USD"
    // ... campos específicos do evento
  }
}
```

### Campos Comuns do Payload

<ResponseField name="event" type="string">
  O tipo de evento (por exemplo, `transaction.created`)
</ResponseField>

<ResponseField name="timestamp" type="string">
  Timestamp ISO 8601 quando o evento ocorreu
</ResponseField>

<ResponseField name="organizationId" type="string">
  Seu ID de organização
</ResponseField>

<ResponseField name="payload.transactionId" type="string">
  O ID UUID da transação no Gu1
</ResponseField>

<ResponseField name="payload.externalId" type="string">
  Seu ID externo para a transação
</ResponseField>

<ResponseField name="payload.type" type="string">
  Tipo de transação: `payment`, `transfer`, `withdrawal`, etc.
</ResponseField>

<ResponseField name="payload.status" type="string">
  Status atual da transação: `CREATED`, `PROCESSING`, `SUSPENDED`, `SENT`, `SUCCESSFUL`, `DECLINED`, `REFUNDED`, `EXPIRED`
</ResponseField>

<ResponseField name="payload.amount" type="number">
  Valor da transação na moeda original
</ResponseField>

<ResponseField name="payload.currency" type="string">
  Código de moeda ISO 4217 (por exemplo, `USD`, `EUR`, `BRL`)
</ResponseField>

## Payloads Específicos de Eventos

### transaction.created

Enviado quando uma nova transação é registrada no sistema.

```json theme={null}
{
  "event": "transaction.created",
  "timestamp": "2025-01-29T15:30:00Z",
  "organizationId": "org-123",
  "payload": {
    "transactionId": "550e8400-e29b-41d4-a716-446655440000",
    "externalId": "txn_abc123",
    "type": "payment",
    "status": "CREATED",
    "amount": 5000.00,
    "currency": "USD",
    "amountInUsd": 5000.00,
    "origin": {
      "entityId": "123e4567-e89b-12d3-a456-426614174001",
      "externalId": "customer_john",
      "name": "John Doe",
      "country": "US"
    },
    "destination": {
      "entityId": "123e4567-e89b-12d3-a456-426614174002",
      "externalId": "merchant_acme",
      "name": "ACME Corp",
      "country": "US"
    },
    "transactedAt": "2025-01-29T15:30:00Z",
    "createdAt": "2025-01-29T15:30:00Z"
  }
}
```

**Caso de uso**: Acione verificações de fraude adicionais, atualize saldos de conta em tempo real, ou inicie processos de conformidade.

### transaction.updated

Enviado quando uma transação existente é atualizada (por exemplo, mudança de status).

```json theme={null}
{
  "event": "transaction.updated",
  "timestamp": "2025-01-29T15:35:00Z",
  "organizationId": "org-123",
  "payload": {
    "transactionId": "550e8400-e29b-41d4-a716-446655440000",
    "externalId": "txn_abc123",
    "type": "payment",
    "status": "SUCCESSFUL",
    "amount": 5000.00,
    "currency": "USD",
    "amountInUsd": 5000.00,
    "origin": {
      "entityId": "123e4567-e89b-12d3-a456-426614174001",
      "externalId": "customer_john",
      "name": "John Doe",
      "country": "US"
    },
    "destination": {
      "entityId": "123e4567-e89b-12d3-a456-426614174002",
      "externalId": "merchant_acme",
      "name": "ACME Corp",
      "country": "US"
    },
    "previousStatus": "PROCESSING",
    "newStatus": "SUCCESSFUL",
    "updatedAt": "2025-01-29T15:35:00Z"
  }
}
```

**Caso de uso**: Notifique clientes sobre o status de sua transação, atualize dashboards em tempo real, ou acione fluxos de trabalho pós-transação.

### transaction.status\_changed

Enviado quando o **status** de uma transação muda (por exemplo após atualização manual em **Monitoramento de transações** ou pela API). O payload é compacto: status novo e anterior mais identificadores em `transaction`. Após a mudança o motor de regras pode ser reexecutado; quando aplicável, **`rulesExecutionSummary`** segue o mesmo tipo de resumo da API de transações para essa avaliação.

```json theme={null}
{
  "event": "transaction.status_changed",
  "timestamp": "2025-01-29T15:35:00Z",
  "organizationId": "org-123",
  "payload": {
    "status": "SUCCESSFUL",
    "previousStatus": "PROCESSING",
    "transaction": {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "externalId": "txn_abc123",
      "type": "payment",
      "status": "SUCCESSFUL"
    },
    "rulesExecutionSummary": {}
  }
}
```

<ResponseField name="payload.status" type="string">
  Novo status após a mudança (ex.: `SUCCESSFUL`, `DECLINED`, `REFUNDED`).
</ResponseField>

<ResponseField name="payload.previousStatus" type="string">
  Status antes da mudança.
</ResponseField>

<ResponseField name="payload.transaction" type="object">
  Identificadores: `id` (UUID no Gu1), `externalId`, `type` e `status` atual.
</ResponseField>

<ResponseField name="payload.rulesExecutionSummary" type="object">
  Opcional. Presente quando há metadados da avaliação de regras nesta transição (mesma forma conceitual das respostas da API).
</ResponseField>

**Caso de uso**: Fluxos que dependem só da transição de status, auditoria ou integrações que precisam de webhook leve. Prefira **`transaction.updated`** se precisar de valores, origem/destino e demais campos na mesma notificação.

## Exemplos de Código

### Node.js - Lidando com Eventos de Transações

```javascript theme={null}
const express = require('express');
const crypto = require('crypto');

const app = express();

app.use(express.json({
  verify: (req, res, buf) => {
    req.rawBody = buf.toString('utf8');
  }
}));

app.post('/webhooks/transactions', async (req, res) => {
  try {
    // Verificar assinatura de webhook (veja guia de segurança)
    const signature = req.headers['x-webhook-signature'];
    const webhookSecret = process.env.GU1_WEBHOOK_SECRET;

    if (!verifySignature(req.rawBody, signature, webhookSecret)) {
      console.error('Invalid webhook signature');
      return res.status(401).json({ error: 'Invalid signature' });
    }

    // Extrair dados do webhook
    const { event, timestamp, organizationId, payload } = req.body;

    console.log('Received transaction webhook:', {
      event,
      transactionId: payload.transactionId,
      status: payload.status
    });

    // Processar o webhook baseado no tipo de evento
    await handleTransactionWebhook(event, payload);

    // Retornar 200 para confirmar recebimento
    res.status(200).json({
      success: true,
      message: 'Webhook received'
    });
  } catch (error) {
    console.error('Webhook error:', error);
    res.status(500).json({
      error: error.message
    });
  }
});

async function handleTransactionWebhook(event, data) {
  const { transactionId, externalId, status, amount, currency } = data;

  // Atualizar seu banco de dados com a transação do Gu1
  await db.updateTransaction(externalId, {
    gu1TransactionId: transactionId,
    status: status,
    lastUpdated: new Date()
  });

  // Realizar ações baseadas no tipo de evento
  switch (event) {
    case 'transaction.created':
      console.log('New transaction created:', externalId);

      // Verificações de fraude adicionais
      if (amount > 10000) {
        await triggerHighValueReview(transactionId);
      }

      // Notificar cliente
      await notifyCustomer(data.origin.externalId, 'transaction-created', {
        amount,
        currency,
        recipient: data.destination.name
      });
      break;

    case 'transaction.updated':
      console.log('Transaction status changed:', {
        externalId,
        from: data.previousStatus,
        to: data.newStatus
      });

      // Atualizar status no seu sistema
      await db.updateTransaction(externalId, {
        status: data.newStatus,
        statusChangedAt: new Date()
      });

      // Notificar sobre transação completada
      if (data.newStatus === 'SUCCESSFUL') {
        await notifyCustomer(data.origin.externalId, 'transaction-completed', {
          amount,
          currency,
          recipient: data.destination.name
        });
      }

      // Lidar com transações recusadas
      if (data.newStatus === 'DECLINED') {
        await notifyCustomer(data.origin.externalId, 'transaction-declined', {
          amount,
          currency
        });
        await logDeclinedTransaction(transactionId, externalId);
      }
      break;

    case 'transaction.status_changed':
      console.log('Transaction status transition:', {
        gu1Id: data.transaction?.id,
        externalId: data.transaction?.externalId,
        from: data.previousStatus,
        to: data.status,
      });
      await db.updateTransaction(data.transaction.externalId, {
        status: data.status,
        statusChangedAt: new Date(),
      });
      break;
  }
}

function verifySignature(rawBody, signature, secret) {
  const expectedSignature = crypto
    .createHmac('sha256', secret)
    .update(rawBody)
    .digest('hex');

  return signature === expectedSignature;
}

app.listen(3000);
```

### Python - Lidando com Eventos de Transações

```python theme={null}
from flask import Flask, request, jsonify
import hmac
import hashlib
from datetime import datetime

app = Flask(__name__)

@app.route('/webhooks/transactions', methods=['POST'])
def handle_transaction_webhook():
    try:
        # Verificar assinatura de webhook
        signature = request.headers.get('X-Webhook-Signature')
        webhook_secret = os.getenv('GU1_WEBHOOK_SECRET')

        if not verify_signature(request.data, signature, webhook_secret):
            return jsonify({'error': 'Invalid signature'}), 401

        # Processar webhook
        data = request.json
        event = data['event']
        payload = data['payload']

        print(f'Received transaction webhook: {event}')

        # Lidar com evento
        handle_transaction_event(event, payload)

        return jsonify({
            'success': True,
            'message': 'Webhook received'
        }), 200

    except Exception as e:
        print(f'Webhook error: {str(e)}')
        return jsonify({'error': str(e)}), 500

def handle_transaction_event(event, data):
    transaction_id = data['transactionId']
    external_id = data['externalId']

    # Atualizar banco de dados
    db.update_transaction(
        external_id=external_id,
        gu1_transaction_id=transaction_id,
        status=data['status'],
        last_updated=datetime.now()
    )

    # Lidar com diferentes eventos
    if event == 'transaction.created':
        # Verificações de fraude
        if data['amount'] > 10000:
            trigger_high_value_review(transaction_id)

        # Notificar cliente
        notify_customer(
            data['origin']['externalId'],
            'transaction-created',
            data
        )

    elif event == 'transaction.updated':
        # Registrar mudança de status
        log_status_change(
            external_id,
            data.get('previousStatus'),
            data['status']
        )

        # Notificar sobre completada
        if data['status'] == 'SUCCESSFUL':
            notify_customer(
                data['origin']['externalId'],
                'transaction-completed',
                data
            )

        # Lidar com recusadas
        elif data['status'] == 'DECLINED':
            notify_customer(
                data['origin']['externalId'],
                'transaction-declined',
                data
            )

    elif event == 'transaction.status_changed':
        tx = data['transaction']
        log_status_change(
            tx['externalId'],
            data['previousStatus'],
            data['status'],
        )

def verify_signature(raw_body, signature, secret):
    expected = hmac.new(
        secret.encode('utf-8'),
        raw_body,
        hashlib.sha256
    ).hexdigest()
    return signature == expected

if __name__ == '__main__':
    app.run(port=3000)
```

## Melhores Práticas

<AccordionGroup>
  <Accordion title="Use externalId para Busca">
    O webhook inclui `externalId` que é o ID que você forneceu ao criar a transação. Use-o para buscar a transação no seu banco de dados.

    ```javascript theme={null}
    const transaction = await db.findTransaction({
      externalId: data.externalId
    });
    ```
  </Accordion>

  <Accordion title="Armazene IDs de Transação do Gu1">
    Salve o `transactionId` do Gu1 no seu banco de dados. Isso permite que você consulte detalhes da transação mais tarde se necessário.

    ```javascript theme={null}
    await db.updateTransaction(transaction.id, {
      gu1TransactionId: data.transactionId,
      status: data.status
    });
    ```
  </Accordion>

  <Accordion title="Lide com Idempotência">
    Você pode receber o mesmo webhook múltiplas vezes. Use o `transactionId` e o `event` para garantir que você processe cada evento apenas uma vez.

    ```javascript theme={null}
    async function handleWebhook(webhook) {
      const alreadyProcessed = await db.checkWebhookProcessed(
        webhook.payload.transactionId,
        webhook.event
      );

      if (alreadyProcessed) {
        return; // Pular duplicado
      }

      // Processar webhook
      await processTransaction(webhook.payload);

      // Marcar como processado
      await db.markWebhookProcessed(
        webhook.payload.transactionId,
        webhook.event
      );
    }
    ```
  </Accordion>

  <Accordion title="Retorne 200 Rapidamente">
    Sempre retorne um código de status 200 o mais rápido possível para confirmar o recebimento. Processe o webhook assincronamente se necessário.

    ```javascript theme={null}
    app.post('/webhooks/transactions', async (req, res) => {
      // Confirmar imediatamente
      res.status(200).send('OK');

      // Processar assincronamente
      processWebhook(req.body).catch(console.error);
    });
    ```
  </Accordion>

  <Accordion title="Verifique Assinaturas">
    Sempre verifique o header `X-Webhook-Signature` para garantir que o webhook seja autêntico. Veja o [guia de segurança](/pt/webhooks/security) para detalhes.

    ```javascript theme={null}
    const signature = req.headers['x-webhook-signature'];
    if (!verifySignature(req.rawBody, signature, secret)) {
      return res.status(401).json({ error: 'Invalid signature' });
    }
    ```
  </Accordion>
</AccordionGroup>

## Solução de Problemas

<AccordionGroup>
  <Accordion title="Não Recebo Webhooks">
    **Verificar estes itens:**

    * URL do webhook é publicamente acessível via HTTPS
    * Webhook está configurado e **habilitado** no dashboard
    * Inscrito nos tipos de eventos corretos
    * Endpoint retorna código de status 200 dentro de 30 segundos
    * Verificar logs do servidor para solicitações recebidas
    * Os eventos de transações estão atualmente em desenvolvimento - confirme que estão ativados para sua organização
  </Accordion>

  <Accordion title="Verificação de Assinatura Falhando">
    **Causas comuns:**

    * Usar secret errado (verificar dashboard para secret atual)
    * Verificar assinatura em JSON analisado em vez de corpo raw
    * Secret não salvo corretamente após criação do webhook
    * Problemas de codificação (garantir UTF-8)

    Veja o [guia de segurança](/pt/webhooks/security) para implementação adequada.
  </Accordion>

  <Accordion title="Recebendo Webhooks Duplicados">
    Este é um comportamento normal. Webhooks podem ser enviados múltiplas vezes devido a problemas de rede, timeouts ou tentativas.

    **Sempre implemente idempotência** usando o `transactionId` e tipo de `event` do webhook.
  </Accordion>
</AccordionGroup>

## Próximos Passos

<CardGroup cols={2}>
  <Card title="Eventos de Entidades" icon="user" href="/pt/webhooks/events/entity-events">
    Lidar com eventos de ciclo de vida de entidades
  </Card>

  <Card title="Eventos KYC" icon="id-card" href="/pt/webhooks/events/kyc-events">
    Processar atualizações de verificação KYC
  </Card>

  <Card title="Segurança de Webhooks" icon="shield" href="/pt/webhooks/security">
    Proteger seus endpoints de webhook
  </Card>

  <Card title="Configuração" icon="gear" href="/pt/webhooks/configuration">
    Configurar ajustes de webhook
  </Card>
</CardGroup>
