Pular para o conteúdo principal

Webhook

Atualizações nos tópicos de monitoramento serão notificadas por meio do envio de webhooks. Para tanto, é necessário, por meio da equipe do suporte, configurar um endereço do endpoint por onde vamos notificar as atualizações e também uma signature_key que será utilizada para assinar a requisição. Vale ressaltar que todos os envios de webhook serão feitos para um único endpoint.

Atenção

Por questões de segurança, todas as requisições de Webhook serão somente realizadas em endpoints servidos por HTTPS.

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úsculasPUT 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 é PUT.
  4. A ordem da concatenação está certa? É endpoint + method + payload, nessa ordem.
  5. O digest está em hexadecimal minúsculo? Não é Base64.

Webhook de Atualização de Evento

Request Body
{
"person_type" : "natural_person",
"account_type" : "natural_person_account",
"person_id" : "22f5d028-0ce7-46f7-9b63-e7e38171b485",
"account_id" : "e49ac344-f941-4668-9afb-a52ce4e5754a",
"monitoring_topic" : "OFAC",
"event" : "entered"
}

Abaixo está o significado de cada campo:

NomeTipoDescrição
person_typestringTipo de pessoa (natural_person ou legal_person).
account_typestringTipo de conta (natural_person_account ou legal_person_account).
person_idstrinIdentificador único da pessoa, passado na requisição de criação.
account_idstringIdentificador único da conta, passado na requisição de criação.
monitoring_topicstringTópico monitorado em que ocorreu a mudança.
eventstringTipo de evento ocorrido, como "entered" (entrada) ou "exited" (saída) para os tópicos de listas restritivas.

A requisição de atualização do tópico de monitoramento possui o formato acima e notifica a mudança no status de um dos tópico de monitoramento dentro da conta. O método utilizado é um PUT e o endereço do endpoint pode conter também o id do evento, de acordo com a necessidade do cliente. É importante ressaltar que o corpo da requisição é enviado como texto codificado em UTF-8.

Retentativas

A notificação é considerada realizada quando recebe como resposta um HTTP Status 200. Caso as notificações falhem, serão feitas 7 retentativas, com os seguintes intervalos, até que um 200 seja retornado ou as tentativas terminem:

  • 10 segundos
  • 40 segundos
  • 160 segundos
  • 640 segundos
  • 2560 segundos
  • 10240 segundos
  • 40960 segundos