Skip to main content

Webhook

Fraud status updates (for Orders routed to manual analysis or answered as Pending) and blocked Sellers are notified via Webhook. To enable this, you must configure — through our support team — an endpoint address where we will send the notifications, as well as a signature_key that will be used to sign the request.

For order status updates, you may alternatively use the polling technique. In that case, simply do not configure a webhook endpoint and use the Order retrieval endpoints to poll instead.

Attention

For security reasons, all Webhook requests are only made to endpoints served over HTTPS.

Webhook Signature

To guarantee that the request received at your endpoint came from our servers, we send an HMAC signature in the Signature header. You recompute that signature on your side and compare it with the one received — if they match, the request is trustworthy.

How the signature is computed

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

The three components are concatenated in this order, with no separator:

ComponentWhat it is
endpointThe full URL of your webhook, exactly as configured with support (including https:// and any query string).
methodThe HTTP verb in uppercasePUT for these notifications.
payloadThe request body exactly as received, byte for byte.
signature_keyThe secret you agreed on with support (also called secret_token). It is the HMAC key, not part of the message.
Use the raw body, never re-serialized JSON

The signature is computed over the exact bytes of the body. If you deserialize the JSON and serialize it again before validating, key order and whitespace change, and the signature will never match.

Read the body as a raw string/bytes first, validate the signature, and only then parse it. In the examples below this appears as request.get_data(), file_get_contents('php://input'), express.raw() and so on.

Accented characters in the payload

The body is serialized with ensure_ascii=False, meaning accented characters are sent as literal UTF-8 ("João") rather than escaped. Treat the body as UTF-8 when computing the HMAC — this is the default in every language below, but it is the most common cause of a mismatched signature when a field contains an accent.

Validation examples

import hashlib
import hmac

SIGNATURE_KEY = "YOUR_SECRET_TOKEN"
WEBHOOK_URL = "https://seu-dominio.com/webhooks/qitech"
HTTP_METHOD = "HTTP_VERB" # see the table above


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 prevents timing attacks
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) # raw body, no parsing
received = request.headers.get("Signature", "")

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

event = request.get_json() # parse only after validating
print(event)
return "", 200
Signature not matching? Check in this order
  1. Was the body re-serialized? This is the most frequent cause. Use the raw body.
  2. Is the URL identical? A trailing slash added or removed changes the signature. Use exactly the URL configured with support.
  3. Is the method correct and uppercase? For these notifications it is PUT.
  4. Is the concatenation order right? It is endpoint + method + payload, in that order.
  5. Is the digest lowercase hexadecimal? It is not Base64.

Order Update Webhook

Request Body
{
"order_id": "123456",
"fraud_status": "automatically_approved",
"event_date": "2019-10-01T10:37:25-03:00"
}

The order analysis status update request has the format above and notifies the change in fraud status. The method used is a PUT, and the endpoint address may also contain the order id, according to your needs. Note that the request body is sent as UTF-8 encoded text.

Examples of endpoints for order updates:

The event_date field indicates the date and time the notification was created, and may be in the past if previous notification attempts failed.

Seller Update Webhook

Example of a settlement block request

Request Body
{
"document_number": "000.000.000-00",
"settlement_status": "blocked",
"event_date": "2019-10-01T10:37:25-03:00"
}

Example of a transactional block request

Request Body
{
"document_number": "000.000.000-00",
"transactional_status": "blocked",
"event_date": "2019-10-01T10:37:25-03:00"
}

When blocking or unblocking a seller is required, the QI Tech system sends a request with the format above. The method used is a PUT to a configurable endpoint and may contain, at your discretion, the document number in the endpoint address.

Attention

It is the presence of the settlement_status field or the transactional_status field that determines which type of block or unblock should be applied to the seller.

Examples of endpoints for seller updates:

The following settlement statuses may be notified:

enumeratordescription
blockedThe seller's settlement must be blocked
unblockedThe seller's settlement must be released

The event_date field indicates the date and time the notification was created, and may be in the past if previous notification attempts failed.

Retries

A notification is considered delivered when it receives an HTTP Status 200 in response. If notifications fail, 7 retries are performed at the following intervals, until a 200 is returned or the attempts are exhausted:

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