Pular para o conteúdo principal

Webhook

Quando uma análise assíncrona é finalizada, um webhook é enviado com o resultado da análise. Para isso, é necessário configurar um endereço onde vamos notificar as atualizações e também uma signature_key que será utilizada para assinar a requisição. Caso ainda não tenha um webhook configurado, fale com a equipe de suporte.

Assinatura do Webhook

Para garantir que a requisição recebida no seu endpoint partiu dos nossos servidores, enviamos uma assinatura HMAC no header Signature. Você recalcula essa assinatura do seu lado e compara com a recebida — se forem iguais, a requisição é confiável.

Como a assinatura é calculada

Signature = HMAC-SHA1(signature_key, endpoint + method + payload) → hexadecimal

Os três componentes são concatenados nesta ordem, sem separador:

ComponenteO que é
endpointA URL completa do seu webhook, exatamente como foi configurada com o suporte (incluindo https:// e eventual query string).
methodO verbo HTTP em letras maiúsculasPOST nestas notificações.
payloadO corpo da requisição exatamente como recebido, byte a byte.
signature_keyO segredo que você combinou com o suporte (também chamado de secret_token). É a chave do HMAC, não parte da mensagem.
Use o corpo bruto, nunca o JSON reserializado

A assinatura é calculada sobre os bytes exatos do corpo. Se você desserializar o JSON e serializar de novo antes de validar, a ordem das chaves e o espaçamento mudam, e a assinatura nunca vai bater.

Leia o corpo como string/bytes brutos primeiro, valide a assinatura, e só depois faça o parse. Nos exemplos abaixo isso aparece como request.get_data(), file_get_contents('php://input'), express.raw() etc.

Acentuação no payload

O corpo é serializado com ensure_ascii=False, ou seja, caracteres acentuados vão como UTF-8 literal ("João"), e não escapados ("João"). Trate o corpo como UTF-8 ao calcular o HMAC — é o comportamento padrão em todas as linguagens abaixo, mas é a causa mais comum de assinatura divergente quando algum campo tem acento.

Exemplos de validação

import hashlib
import hmac

SIGNATURE_KEY = "YOUR_SECRET_TOKEN"
WEBHOOK_URL = "https://seu-dominio.com/webhooks/qitech"
HTTP_METHOD = "HTTP_VERB" # veja a tabela acima


def calculate_signature(endpoint: str, method: str, payload: str) -> str:
hmac_obj = hmac.new(
SIGNATURE_KEY.encode("utf-8"),
(endpoint + method + payload).encode("utf-8"),
hashlib.sha1,
)
return hmac_obj.hexdigest()


def is_valid(received_signature: str, raw_body: str) -> bool:
expected = calculate_signature(WEBHOOK_URL, HTTP_METHOD, raw_body)
# compare_digest evita ataques de temporização
return hmac.compare_digest(expected, received_signature)


# Exemplo com Flask
from flask import Flask, request

app = Flask(__name__)


@app.route("/webhooks/qitech", methods=[HTTP_METHOD])
def receive_webhook():
raw_body = request.get_data(as_text=True) # corpo bruto, sem parse
received = request.headers.get("Signature", "")

if not is_valid(received, raw_body):
return "", 401

event = request.get_json() # parse só depois de validar
print(event)
return "", 200
Assinatura não bate? Verifique nesta ordem
  1. O corpo foi reserializado? É a causa mais frequente. Use o corpo bruto.
  2. A URL está idêntica? Uma barra final a mais ou a menos muda a assinatura. Use exatamente a URL configurada com o suporte.
  3. O método está correto e em maiúsculas? Nestas notificações é POST.
  4. A ordem da concatenação está certa? É endpoint + method + payload, nessa ordem.
  5. O digest está em hexadecimal minúsculo? Não é Base64.

Requisição

A requisição possui o formato abaixo e notifica que a análise foi finalizada. A requisição utiliza o método HTTP POST e o corpo da requisição é enviado como texto codificado em UTF-8.

Webhook de Sucesso

curl --location 'YOUR-ENDPOINT-HERE' \
--header 'Signature: CALCULATED-HASH-HMAC' \
--data '{"id": "e314ffci-14f3-41a1-ad5d-c9c18782jhfe", "document": {"analysis_result": {...}, "validation_status": "valid"}, "status": "successful", "status_reason": "", "status_description": "Sucessfull Analysis"}'

Webhook de Erro

curl --location 'YOUR-ENDPOINT-HERE' \
--header 'Signature: CALCULATED-HASH-HMAC' \
--data '{"id": "e314ffci-14f3-41a1-ad5d-c9c18782jhfe", "status": "bad_request", "status_reason": "missing_information
", "status_description": "The document is missing required information."}'