Skip to main content

Visão Geral

Os eventos de webhook de entidades permitem que você receba notificações em tempo real quando entidades (pessoas, empresas, dispositivos, etc.) são criadas, atualizadas ou seu status muda na plataforma Gu1. Esses eventos permitem que você mantenha seus sistemas sincronizados com Gu1 e automatize fluxos de trabalho baseados em mudanças no ciclo de vida das entidades.

Por Que Usar Eventos de Entidades?

Sincronização em Tempo Real

Mantenha seu banco de dados sincronizado com dados de entidades Gu1

Fluxos de Trabalho Automatizados

Acione ações quando o status da entidade mudar

Trilha de Auditoria

Rastreie todas as mudanças de entidades para conformidade

Eficiente

Não é necessário consultar a API para atualizações

Eventos Disponíveis

entity.created

Acionado quando uma nova entidade é criada no Gu1. Quando dispara:
  • Uma nova pessoa, empresa, dispositivo ou outra entidade é criada via POST /entities
Filtros disponíveis:
  • entityTypes: Receber apenas eventos para tipos de entidades específicos (por exemplo, ["person", "company"])

entity.updated

Acionado quando os dados de uma entidade são atualizados (excluindo mudanças de status). Quando dispara:
  • Informações da entidade são atualizadas via PATCH /entities/:id
  • Mudanças em nome, atributos, dados da entidade, ID fiscal, etc.
Nota: Mudanças de status acionam entity.status_changed em vez disso. Filtros disponíveis:
  • entityTypes: Receber apenas eventos para tipos de entidades específicos

entity.status_changed

Acionado quando o status de uma entidade muda. Quando dispara:
  • Transições de status de entidade (por exemplo, under_reviewactive, activeblocked)
  • Atualizações de status via PATCH /entities/:id ou ações de conformidade automatizadas
Filtros disponíveis:
  • entityTypes: Filtrar por tipo de entidade
  • statusChanges.from: Acionar apenas quando mudar DE um status específico
  • statusChanges.to: Acionar apenas quando mudar PARA um status específico

Exemplos de Payload de Eventos

entity.created

{
  "event": "entity.created",
  "timestamp": "2025-01-07T10:00:00.000Z",
  "payload": {
    "entity": {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "externalId": "customer_abc123",
      "name": "John Doe",
      "type": "person",
      "taxId": "123-45-6789",
      "countryCode": "US",
      "status": "under_review",
      "isClient": false,
      "attributes": {
        "email": "john.doe@example.com",
        "phone": "+1234567890",
        "customerTier": "premium"
      },
      "entityData": {
        "person": {
          "firstName": "John",
          "lastName": "Doe",
          "dateOfBirth": "1990-01-15",
          "nationality": "US",
          "occupation": "Software Engineer"
        }
      },
      "createdAt": "2025-01-07T10:00:00.000Z",
      "updatedAt": "2025-01-07T10:00:00.000Z"
    },
    "createdBy": "user_xyz789",
    "metadata": {
      "source": "api",
      "userAgent": "Mozilla/5.0...",
      "ipAddress": "192.168.1.100"
    }
  }
}
Campos Chave:
  • entity: Objeto de entidade completo com todos os dados
  • entity.externalId: Seu identificador único para a entidade
  • entity.type: Tipo de entidade (person, company, device, etc.)
  • entity.status: Status atual (under_review, active, blocked, etc.)
  • createdBy: ID do usuário que criou a entidade
  • metadata: Contexto adicional sobre a criação

entity.updated

{
  "event": "entity.updated",
  "timestamp": "2025-01-07T10:05:00.000Z",
  "payload": {
    "entity": {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "externalId": "customer_abc123",
      "name": "John Doe",
      "type": "person",
      "taxId": "123-45-6789",
      "countryCode": "US",
      "status": "active",
      "isClient": false,
      "attributes": {
        "email": "john.doe@newemail.com",
        "phone": "+1234567890",
        "customerTier": "premium"
      },
      "entityData": {
        "person": {
          "firstName": "John",
          "lastName": "Doe",
          "dateOfBirth": "1990-01-15",
          "nationality": "US",
          "occupation": "Senior Software Engineer"
        }
      },
      "updatedAt": "2025-01-07T10:05:00.000Z"
    },
    "changes": {
      "attributes.email": {
        "old": "john.doe@example.com",
        "new": "john.doe@newemail.com"
      },
      "entityData.person.occupation": {
        "old": "Software Engineer",
        "new": "Senior Software Engineer"
      }
    },
    "updatedBy": "user_xyz789",
    "reason": "Customer provided updated information"
  }
}
Campos Chave:
  • entity: Objeto de entidade completo com dados atualizados
  • changes: Objeto mostrando o que mudou (valores antigos vs novos)
  • updatedBy: ID do usuário que atualizou a entidade
  • reason: Motivo opcional para a atualização

entity.status_changed

{
  "event": "entity.status_changed",
  "timestamp": "2025-01-07T10:10:00.000Z",
  "payload": {
    "status": "active",
    "previousStatus": "under_review",
    "reason": "KYC verification completed successfully",
    "entity": {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "externalId": "customer_abc123",
      "name": "John Doe",
      "type": "person",
      "taxId": "123-45-6789",
      "countryCode": "US",
      "status": "active",
      "updatedAt": "2025-01-07T10:10:00.000Z"
    }
  }
}
Campos Chave:
  • status: Novo status
  • previousStatus: Status anterior
  • reason: Por que o status mudou
  • entity: Objeto de entidade completo

Configuração de Filtros

Filtrar por Tipo de Entidade

Receber apenas eventos para tipos de entidades específicos:
{
  "eventTypes": ["entity.created", "entity.updated"],
  "filters": {
    "entityTypes": ["person", "company"]
  }
}
Esta configuração acionará apenas webhooks para entidades de pessoa e empresa, ignorando dispositivos e outros tipos.

Filtrar por Mudança de Status

Receber apenas eventos quando o status da entidade mudar para valores específicos:
{
  "eventTypes": ["entity.status_changed"],
  "filters": {
    "entityTypes": ["person"],
    "statusChanges": {
      "to": "blocked"
    }
  }
}
Isso acionará apenas quando uma entidade de pessoa for mudada PARA status blocked. Filtrar quando mudar DE um status específico:
{
  "eventTypes": ["entity.status_changed"],
  "filters": {
    "statusChanges": {
      "from": "active",
      "to": "suspended"
    }
  }
}
Isso acionará apenas quando o status mudar de active para suspended.

Exemplos de Código

Node.js - Lidando com Eventos de Entidades

const express = require('express');
const app = express();

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

app.post('/webhooks/entity', async (req, res) => {
  try {
    // Verificar assinatura (veja guia de segurança)
    if (!verifySignature(req.rawBody, req.headers['x-webhook-signature'])) {
      return res.status(401).json({ error: 'Invalid signature' });
    }

    const { event, payload } = req.body;

    // Rotear para o manipulador apropriado
    switch (event) {
      case 'entity.created':
        await handleEntityCreated(payload);
        break;

      case 'entity.updated':
        await handleEntityUpdated(payload);
        break;

      case 'entity.status_changed':
        await handleEntityStatusChanged(payload);
        break;

      default:
        console.warn('Unknown event type:', event);
    }

    res.status(200).json({ success: true });
  } catch (error) {
    console.error('Webhook error:', error);
    res.status(500).json({ error: error.message });
  }
});

async function handleEntityCreated(payload) {
  const { entity, createdBy, metadata } = payload;

  console.log(`Entity created: ${entity.name} (${entity.type})`);

  // Sincronizar com seu banco de dados
  await db.entities.create({
    data: {
      gu1Id: entity.id,
      externalId: entity.externalId,
      name: entity.name,
      type: entity.type,
      status: entity.status,
      data: entity.entityData,
      attributes: entity.attributes,
      createdBy,
      metadata
    }
  });

  // Acionar fluxos de trabalho baseados no tipo de entidade
  if (entity.type === 'person') {
    await sendWelcomeEmail(entity.attributes.email);
  }
}

async function handleEntityUpdated(payload) {
  const { entity, changes, updatedBy, reason } = payload;

  console.log(`Entity updated: ${entity.name}`);
  console.log('Changes:', changes);

  // Atualizar seu banco de dados
  await db.entities.update({
    where: { gu1Id: entity.id },
    data: {
      name: entity.name,
      status: entity.status,
      data: entity.entityData,
      attributes: entity.attributes,
      lastUpdatedBy: updatedBy,
      updatedAt: new Date()
    }
  });

  // Registrar mudanças para trilha de auditoria
  await db.entityChanges.create({
    data: {
      entityId: entity.id,
      changes,
      changedBy: updatedBy,
      reason,
      timestamp: new Date()
    }
  });
}

async function handleEntityStatusChanged(payload) {
  const { entity, status, previousStatus, reason } = payload;

  console.log(`Entity status changed: ${previousStatus}${status}`);

  // Atualizar banco de dados
  await db.entities.update({
    where: { gu1Id: entity.id },
    data: {
      status,
      statusChangedAt: new Date()
    }
  });

  // Tomar ação baseada no novo status
  switch (status) {
    case 'active':
      await activateCustomerAccount(entity.externalId);
      await sendNotification(entity, 'Your account has been activated');
      break;

    case 'blocked':
      await deactivateCustomerAccount(entity.externalId);
      await sendNotification(entity, 'Your account has been blocked');
      break;

    case 'suspended':
      await suspendCustomerAccount(entity.externalId);
      await sendNotification(entity, 'Your account has been suspended');
      break;
  }

  // Registrar mudança de status
  await db.statusHistory.create({
    data: {
      entityId: entity.id,
      fromStatus: previousStatus,
      toStatus: status,
      reason,
      timestamp: new Date()
    }
  });
}

app.listen(3000);

Melhores Práticas

O entity.externalId é seu identificador único. Use-o para buscar entidades no seu banco de dados:
const customer = await db.customers.findUnique({
  where: { externalId: entity.externalId }
});
Sempre armazene o ID da entidade Gu1 no seu banco de dados para referência:
await db.customers.create({
  data: {
    externalId: entity.externalId,
    gu1Id: entity.id, // Armazenar isso
    name: entity.name
  }
});
Mesmo se você se inscrever apenas em eventos específicos, lide com todos os tipos de eventos graciosamente:
switch (event) {
  case 'entity.created':
    await handleEntityCreated(payload);
    break;

  default:
    console.warn('Unhandled event type:', event);
    // Ainda retornar 200
}
Mantenha uma trilha de auditoria de todas as mudanças de entidades:
await db.auditLog.create({
  data: {
    entityId: entity.id,
    event,
    changes,
    timestamp: new Date(),
    webhook: req.body
  }
});
Use o ID da entidade e timestamp para prevenir processamento duplicado:
const webhookId = `${entity.id}_${event}_${timestamp}`;
const processed = await db.webhookLog.findUnique({
  where: { webhookId }
});

if (processed) {
  return; // Pular duplicado
}
Configure filtros para receber apenas eventos relevantes:
{
  "eventTypes": ["entity.status_changed"],
  "filters": {
    "entityTypes": ["person"],
    "statusChanges": {
      "to": "active"
    }
  }
}

Solução de Problemas

Verificar:
  • Webhook está inscrito no evento entity.created
  • Tipo de entidade corresponde aos seus filtros (se configurados)
  • Webhook está habilitado no dashboard
  • Endpoint é publicamente acessível
Testar:
# Criar uma entidade de teste
curl -X POST https://api.gu1.io/entities \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -d '{"name":"Test","type":"person"}'
O objeto changes inclui apenas campos que realmente mudaram. Se você não vê um campo, significa que ele não foi atualizado.Exemplo:
{
  "changes": {
    "name": {
      "old": "John",
      "new": "John Doe"
    }
  }
}
Apenas name mudou, outros campos permanecem os mesmos.
Verificar:
  • Status realmente mudou (não apenas entidade atualizada)
  • Filtros correspondem à mudança de status (from/to)
  • Mudança de status não está sendo filtrada
Exemplo de filtro que pode bloquear eventos:
{
  "filters": {
    "statusChanges": {
      "to": "blocked"
    }
  }
}
Isso disparará APENAS quando o status mudar PARA blocked.

Próximos Passos