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

# Receber evento de plataforma

> A URL que se cola no painel da Hotmart, da Kiwify, da Eduzz, do Guru ou de um webhook genérico — grava o evento e responde antes de processar.



## OpenAPI

````yaml POST /webhooks/{platform}/{token}
openapi: 3.0.3
info:
  title: weve API
  description: >
    Contrato entre a API Go e o dashboard. **Este arquivo é a fonte de
    verdade**:

    os tipos e a interface de servidor em Go, e o client TypeScript, são todos

    gerados daqui (`make openapi`). Handler que divergir da interface não
    compila.


    A superfície interna (`/internal/*`) fica de fora de propósito: é acesso de

    máquina, protegido por segredo dedicado, e não é contrato de cliente.
  version: 0.2.0
servers:
  - url: https://api.weve.cx
    description: produção
  - url: http://localhost:8080
    description: desenvolvimento
security: []
tags:
  - name: health
  - name: session
  - name: account
  - name: clubs
  - name: members
  - name: webhooks
  - name: sales-platforms
  - name: catalog
  - name: content
  - name: classroom
  - name: students
  - name: auth
paths:
  /webhooks/{platform}/{token}:
    post:
      tags:
        - webhooks
      summary: Recebe um evento de uma plataforma de venda
      description: >
        É a URL que o gestor cola no painel da Hotmart, da Kiwify, da Eduzz, do

        Digital Manager Guru ou de qualquer sistema que fale o webhook genérico.
        A plataforma chama

        aqui a cada compra, reembolso, chargeback e renovação.


        A rota é **anônima por natureza** — não declara nenhum esquema de

        sessão, e isso é deliberado: quem chama é uma máquina de terceiro que

        só sabe repetir a URL que recebeu. O que a protege são duas coisas,

        conferidas pelo handler e não pelo middleware de sessão: o `token` do

        caminho, que identifica a conexão (e, por ela, o club), e a assinatura

        própria de cada plataforma, verificada com o segredo daquela conexão —

        `X-HOTMART-HOTTOK` na Hotmart, HMAC do corpo cru na Kiwify, o

        `origin_secret` no corpo da Eduzz, o `api_token` no corpo do Guru e

        `Authorization: Bearer` no genérico. Sem `{clubId}` no caminho de
        propósito: qualquer caminho com

        esse parâmetro exige sessão, e aqui não há nenhuma.


        **Receber não é processar.** O evento é gravado como chegou e a

        resposta é 202 antes de qualquer efeito; conceder ou revogar acesso é

        trabalho de um laço à parte, que tenta de novo e reprocessa. Por isso

        as regras de resposta são as de uma caixa de entrada: reentrega do

        mesmo corpo responde 202 igual (idempotente pelo hash do corpo, sem

        gravar de novo); produto ainda sem mapeamento responde 202 (a compra

        fica registrada e a matrícula é criada quando o mapeamento chegar);

        só assinatura inválida e token desconhecido são recusados — e a

        plataforma reenvia.


        O caminho fica FORA de `/v1` de propósito. Esta é a única URL da API

        que mora em painel de terceiro, colada uma vez por club, e que por

        isso nunca pode mudar: amarrá-la à versão do contrato faria um `/v2`

        futuro ter que carregar `/v1` no nome para sempre.


        O corpo é aceito como a plataforma o manda, sem esquema: JSON na

        Hotmart, na Kiwify, na Eduzz atual e no Guru, formulário na Eduzz

        legada. O limite é de 1 MiB.


        A Eduzz dispara um POST de validação ao cadastrar a URL — vazio ou com

        o placeholder literal do segredo. Ele responde 200 e não é gravado.


        O Guru manda dois webhooks que importam, e o gestor liga os dois no

        painel dele (Configurações › Webhooks): o de **vendas**, com os status

        Aprovada, Completa, Reembolsada, Reclamada e Atrasada marcados, e o de

        **assinaturas**, com Ativa, Atrasada, Cancelada e Expirada. A mesma

        transação volta a cada mudança de status — e às vezes sem mudança,

        quando o processador a atualiza —, o que é esperado: a compra é única

        por transação e a repetição vira "ignorado" no histórico. O Guru exige

        `200` como confirmação, e é o que recebe, no lugar do `202` das

        demais.
      operationId: receivePlatformWebhook
      parameters:
        - name: platform
          in: path
          required: true
          description: |
            Qual plataforma está chamando. Redundante com a conexão que o
            token identifica, e conferido contra ela: um token de Hotmart
            colado na Kiwify responde 404 em vez de tentar validar a
            assinatura errada.
          schema:
            $ref: '#/components/schemas/SalesPlatform'
        - name: token
          in: path
          required: true
          description: |
            O token da conexão, emitido quando o gestor liga a plataforma ao
            club. Identifica; não autentica — a assinatura faz isso.
          schema:
            type: string
            minLength: 1
            maxLength: 128
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              additionalProperties: true
          application/x-www-form-urlencoded:
            schema:
              type: object
              additionalProperties: true
      responses:
        '200':
          description: |
            Duas situações: o ping de validação da Eduzz, em que nada foi
            gravado; e o evento gravado (ou já existente) quando a plataforma é
            o Guru, que documenta `200` como a única confirmação e retenta —
            até desativar o webhook — o que recebe outra coisa.
        '202':
          description: |
            Evento gravado (ou já estava: a reentrega do mesmo corpo responde
            igual, sem duplicar). Sem corpo.
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          description: |
            A assinatura não confere com o segredo desta conexão
            (`invalid_signature`). O segredo colado no painel e o guardado
            aqui divergem; nada foi gravado.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'
        '404':
          description: |
            Token desconhecido, conexão desligada, ou a plataforma do caminho
            não é a da conexão (`connection_not_found`). Os três respondem
            igual.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '500':
          $ref: '#/components/responses/InternalError'
        '501':
          description: |
            O ambiente não tem a chave que cifra os segredos das conexões
            (`not_configured`): sem ela não há como conferir assinatura
            nenhuma.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'
components:
  schemas:
    SalesPlatform:
      type: string
      enum:
        - hotmart
        - kiwify
        - eduzz
        - guru
        - webhook
      description: |
        As plataformas de venda que a API entende. `guru` é o Digital Manager
        Guru; `webhook` é o formato genérico, para gateways e automatizadores
        sem integração nativa.
    ErrorEnvelope:
      type: object
      description: >-
        Envelope único de erro da API. O `code` é o contrato com o cliente — a
        `message` é diagnóstico, e pode mudar.
      required:
        - error
      properties:
        error:
          type: object
          required:
            - code
          properties:
            code:
              type: string
            message:
              type: string
  responses:
    BadRequest:
      description: Requisição malformada
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorEnvelope'
    TooManyRequests:
      description: Teto por IP atingido
      headers:
        Retry-After:
          schema:
            type: integer
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorEnvelope'
    InternalError:
      description: >-
        Falha inesperada. O corpo nunca traz o erro real — ele fica no log e no
        rastreamento.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorEnvelope'

````

## Related topics

- [Reprocessar evento](/api-reference/webhooks/events-reprocess.md)
- [Histórico de eventos](/api-reference/webhooks/events-list.md)
- [Ler um evento](/api-reference/webhooks/events-get.md)
- [Webhook genérico](/essentials/generic-webhook.md)
- [Ligar plataforma](/api-reference/webhooks/connections-create.md)
