> ## Knowledge Base Index
> Fetch the complete knowledge base index at: https://ajuda.themembers.com.br/sitemap.xml
> Use this file to discover available pages before exploring further.
> Pure-Markdown content can be obtained by appending a '.md' suffix to the content URLs listed in the sitemap (without the trailing slash).

# Como configurar webhooks na Área de Membros / Plataforma

Os webhooks permitem que sistemas externos recebam notificações em tempo real sobre eventos importantes da área de membros, como conclusão de aulas, publicação de conteúdo, emissão de certificados, inatividade de usuários, aprovação ou reprovação em avaliações.

Acesse a documentação completa em - [Webhook - Área de Membros](https://documentation.themembers.dev.br/webhooks/webhooks-da-area-de-membros)

## ${color}[#000000](Visão Geral)

A plataforma TheMembers envia requisições **HTTP POST** para os endpoints de webhook configurados pelo cliente sempre que um evento ocorre.

**Características principais:**
* **Protocolo:** HTTP POST
* **Formato:** JSON
* **Autenticação:** HMAC-SHA256
* **Timeout:** 30 segundos
* **Tentativas:** até 3 em caso de falha
* **Intervalo entre tentativas:** 30s, 60s e 120s

# PASSO 1.
Para realizar o acesso ao Painel no Dashboard vá em Plataformas - Configurações - Webhook

![](https://storage.crisp.chat/users/helpdesk/website/-/d/d/8/4/dd84fe263ed8d000/image_145rfos.png =834xauto)
* Após isso clique no botão de **Criar Webhook **para realizar a criação das configurações

![](https://storage.crisp.chat/users/helpdesk/website/-/d/d/8/4/dd84fe263ed8d000/image_ks59bh.png =671xauto)

![](https://storage.crisp.chat/users/helpdesk/website/-/d/d/8/4/dd84fe263ed8d000/image_12ztvno.png =766xauto)
## ${color}[#000000](Autenticação e Segurança)

Todas as requisições de webhook incluem um mecanismo de autenticação baseado em **HMAC-SHA256**, garantindo a integridade e a autenticidade do payload recebido.

**Cabeçalhos enviados:**

* `X-Webhook-Signature`: assinatura HMAC-SHA256 do corpo da requisição
* `X-Webhook-Event`: nome do evento disparado
A assinatura é gerada a partir do **corpo bruto do JSON**, utilizando o `client_token` do cliente como chave secreta.

### Como validar a assinatura

1. Obtenha o corpo bruto da requisição (JSON sem qualquer modificação).
2. Calcule o HMAC-SHA256 utilizando o `client_token` como chave.
3. Gere o hash em formato hexadecimal.
4. Compare o resultado com o valor enviado no cabeçalho `X-Webhook-Signature`.

⚠️ **Importante**: a validação só funcionará corretamente se o JSON for serializado exatamente conforme o padrão abaixo:

* Barras (`/`) **não** são escapadas
* Caracteres Unicode **não** são escapados (ex: `á` ao invés de `\u00e1`)

### Exemplo em PHP

```
<?php

$body = file_get_contents('php://input');
$clientToken = 'seu_client_token_aqui';

$expectedSignature = hash_hmac('sha256', $body, $clientToken);
$providedSignature = $_SERVER['HTTP_X_WEBHOOK_SIGNATURE'] ?? '';

if (!hash_equals($expectedSignature, $providedSignature)) {
    http_response_code(401);
    exit('Assinatura inválida');
}

$payload = json_decode($body, true);
```

### Exemplo em Node.js

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

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

app.post('/webhook', (req, res) => {
  const clientToken = 'seu_client_token_aqui';
  const providedSignature = req.headers['x-webhook-signature'];

  const expectedSignature = crypto
    .createHmac('sha256', clientToken)
    .update(req.rawBody)
    .digest('hex');

  if (expectedSignature !== providedSignature) {
    return res.status(401).send('Assinatura inválida');
  }

  console.log('Evento recebido:', req.body.event);
  res.status(200).send('OK');
});
```

## ${color}[#000000](Exemplo de Assinatura)

Abaixo está um exemplo de corpo de requisição de webhook e o valor esperado para o cabeçalho X-Webhook-Signature, assumindo que o client_token seja mySecretKey123.

Corpo da Requisição de Exemplo:

```
{
  "type": "class_history",
  "event": "class.completed",
  "created": "2025-01-13T14:30:00.000000Z",
  "data": {
    "datetime": "2025-01-13T14:25:00.000000Z",
    "platform_id": 1,
    "platform_name": "Minha Plataforma",
    "student": {
      "id": 12345,
      "reference_id": "EXT-001",
      "name": "João da Silva",
      "email": "joao@exemplo.com",
      "phone": "+5511999999999",
      "document": "12345678900",
      "gender": "male"
    },
    "course": {
      "id": 100,
      "name": "Curso de Programação",
      "module": {
        "id": 10,
        "name": "Módulo 1 - Fundamentos",
        "class": {
          "id": 1,
          "name": "Introdução ao PHP"
        }
      }
    }
  }
}
```

Assinatura Esperada

Para o corpo acima e client_token mySecretKey123, o valor do cabeçalho X-Webhook-Signature será (exemplo ilustrativo):
4a3d8c1e2f5b6a7c8d9e0f1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c

**Nota:** A assinatura acima é ilustrativa. O valor real dependerá da serialização exata do JSON e da chave secreta.

**Importante:** A assinatura só corresponderá se o JSON for serializado exatamente como acima, sem barras ou unicode escapados.

## ${color}[#000000](Eventos Disponíveis)

### Conclusão de Aula (`class.completed`)

Disparado quando o aluno conclui uma aula (progresso 100% ou aula finalizada).

* **Tipo:** `class_history`
* **Evento:** `class.completed`

Inclui dados da plataforma, aluno, curso, módulo e aula.

```
{
  "type": "class_history",
  "event": "class.completed",
  "created": "2025-01-13T14:30:00.000000Z",
  "data": {
    "datetime": "2025-01-13T14:25:00.000000Z",
    "platform_id": 1,
    "platform_name": "Minha Plataforma",
    "student": {
      "id": 12345,
      "reference_id": "EXT-001",
      "name": "João da Silva",
      "email": "joao@exemplo.com",
      "phone": "+5511999999999",
      "document": "12345678900",
      "gender": "male"
    },
    "course": {
      "id": 100,
      "name": "Curso de Programação",
      "module": {
        "id": 10,
        "name": "Módulo 1 - Fundamentos",
        "class": {
          "id": 1,
          "name": "Introdução ao PHP"
        }
      }
    }
  }
}
```

### Publicação de Aula (`class.published`)

Enviado quando uma aula é criada ou publicada manualmente na plataforma.

* **Tipo:** `class`
* **Evento:** `class.published`

Inclui dados do curso, módulo e aula publicada.

```
{
  "type": "class",
  "event": "class.published",
  "created": "2025-01-13T14:30:00.000000Z",
  "data": {
    "datetime": "2025-01-13T14:30:00.000000Z",
    "platform_id": 1,
    "platform_name": "Minha Plataforma",
    "course": {
      "id": 100,
      "name": "Curso de Programação",
      "module": {
        "id": 10,
        "name": "Módulo 1 - Fundamentos",
        "class": {
          "id": 1,
          "name": "Introdução ao PHP"
        }
      }
    }
  }
}
```

### Certificado Emitido (`certificate.issued`)

Disparado no momento da emissão de um certificado para um aluno.

* **Tipo:** `certificate`
* **Evento:** `certificate.issued`

Inclui dados do aluno, curso e certificado emitido.

```
{
  "type": "certificate",
  "event": "certificate.issued",
  "created": "2025-01-13T14:30:00.000000Z",
  "data": {
    "datetime": "2025-01-13T14:30:00.000000Z",
    "platform_id": 1,
    "platform_name": "Minha Plataforma",
    "student": {
      "id": 12345,
      "reference_id": "EXT-001",
      "name": "João da Silva",
      "email": "joao@exemplo.com",
      "phone": "+5511999999999",
      "document": "12345678900",
      "gender": "male"
    },
    "course": {
      "id": 100,
      "name": "Curso de Programação"
    },
    "certificate": {
      "id": 50,
      "name": "Certificado de Conclusão"
    }
  }
}
```

### Usuário Inativo por 7 Dias (`user.inactive_for_7_days`)

Enviado quando um usuário permanece **exatamente 7 dias sem login**.

📌 Observação: enquanto o usuário continuar inativo, o evento será reenviado a cada 7 dias.

* **Tipo:** `login_history`
* **Evento:** `user.inactive_for_7_days`

```
{
  "id": "550e8400-e29b-41d4-a716-446655440000",
  "type": "login_history",
  "event": "user.inactive_for_7_days",
  "created": "2025-01-13T14:30:00.000000Z",
  "data": {
    "datetime": "2025-01-06T14:30:00.000000Z",
    "inactive_days": 7,
    "platform_id": 1,
    "platform_name": "Minha Plataforma",
    "student": {
      "id": 12345,
      "reference_id": "EXT-001",
      "name": "João da Silva",
      "email": "joao@exemplo.com",
      "phone": "+5511999999999",
      "document": "12345678900",
      "gender": "female"
    }
  }
}
```

### Aluno finalizou o quiz (`quiz.completed`)
Enviado quando o aluno finaliza uma avaliação.

* **Listener:** DispatchQuizWebhooksListener
* **Trigger:** QuizUserAnswerService após salvar respostas

{
  "id": "uuid",
  "type": "quiz",
  "event": "quiz.completed",
  "created": "2026-06-19T12:02:00.000000Z",
  "data": {
    "datetime": "2026-06-19T12:02:00.000000Z",
    "platform_id": 2654,
    "platform_name": "Nome da Plataforma",
    "student": {
      "id": 12345,
      "reference_id": "ID-EXTERNO",
      "name": "João da Silva",
      "email": "joao@exemplo.com",
      "phone": "+5511999999999",
      "document": "12345678900",
      "gender": "male"
    },
    "quiz": {
      "id": 1,
      "name": "Nome do Quiz",
      "type": "tipo-do-quiz",
      "approved": true,
      "performance": 85.5,
      "awaiting_evaluation": false,
      "class_id": 50
    },
    "course": {
      "id": 100,
      "name": "Nome do Curso",
      "course_type": "livre",
      "module": {
        "id": 10,
        "name": "Nome do Módulo",
        "class": {
          "id": 50,
          "name": "Nome da Aula"
        }
      }
    
### Aluno aprovado (`quiz.approved`)
Enviado quando o aluno é aprovado em uma avaliação. 
* **Quando dispara:** junto com quiz.completed, quando o aluno atinge a nota mínima
* **Payload:** mesmo formato do quiz.completed, com "event": "quiz.approved" e "approved": true

### Aluno reprovado (`quiz.reproved`)
Enviado quando o aluno é reprovado em uma avaliação.
* **Quando dispara:** junto com quiz.completed, quando o aluno não atinge a nota mínima
* **Payload:** mesmo formato do quiz.completed, com "event": "quiz.reproved" e "approved": false

### Regra dos 3 eventos de quiz:

* Sempre dispara quiz.completed
* Se tem nota de corte: dispara também quiz.approved ou quiz.reproved
* Se não tem nota de corte: só quiz.completed
### 
Curso criado (`course.created`)
Enviado quando um novo curso é adicionado na plataforma.

* **Listener:** DispatchCourseCreatedWebhooksListener
* **Trigger:** Evento CourseCreatedEvent no created do model Courses
{
  "id": "uuid",
  "type": "course",
  "event": "course.created",
  "created": "2026-06-19T12:00:00.000000Z",
  "data": {
    "datetime": "2026-06-19T12:00:00.000000Z",
    "platform_id": 2654,
    "platform_name": "Nome da Plataforma",
    "course": {
      "id": 100,
      "name": "Nome do Curso",
      "course_type": "livre | extensao | pos_graduacao",
      "status": "published | unpublished"
    }
  }

Módulo criado (`module.created`) 
Enviado quando um novo módulo é adicionado na plataforma

* **Listener:** DispatchModuleCreatedWebhooksListener
* **Trigger:** evento ModuleCreatedEvent no created do model Modules

Obs.: 1 webhook por disciplina — não vem array no course.created

{
  "id": "uuid",
  "type": "module",
  "event": "module.created",
  "created": "2026-06-19T12:01:00.000000Z",
  "data": {
    "datetime": "2026-06-19T12:01:00.000000Z",
    "platform_id": 2654,
    "platform_name": "Nome da Plataforma",
    "course": {
      "id": 100,
      "name": "Nome do Curso",
      "course_type": "pos_graduacao",
      "status": "published",
      "module": {
        "id": 10,
        "name": "Nome da Disciplina",
        "status": "published"
      }
    }
# Formato do Payload

* `Content-Type: application/json`
* Datas no padrão **ISO 8601**
* Campos podem retornar `null`
* Telefones no padrão **E.164** (ex: `+5511999999999`)
* CPF/CNPJ enviados apenas com números

# Boas Práticas

### Segurança

* Valide sempre a assinatura HMAC antes de processar o evento
* Não compartilhe o `client_token`
* Utilize HTTPS no endpoint de webhook

### Performance e Resposta

* Responda rapidamente com **HTTP 200**
* Evite processamentos pesados na requisição
* Utilize filas e processamento assíncrono

### Confiabilidade

* Implemente **idempotência** (eventos podem ser reenviados)
* Registre logs para auditoria e troubleshooting

### Tratamento de Erros

* Até 3 tentativas automáticas em caso de falha
* Intervalos: 30s, 60s e 120s
* Após falha definitiva, o evento não será reenviado

| Histórico: Também é possível visualizar todo o seu histórico de envios clicando em ver histórico. Dentro do histórico, você pode clicar em Ver payload e ter acesso à todo o corpo de requisição enviado, como na imagem demonstrativa abaixo.
###  

![](https://storage.crisp.chat/users/helpdesk/website/-/d/d/8/4/dd84fe263ed8d000/image_c8l52t.png =616xauto)

|| **Considerações Finais:** Os webhooks permitem integrações robustas e em tempo real com sistemas externos.  Seguindo as boas práticas de segurança e performance, é possível construir integrações confiáveis, escaláveis e seguras.