Webhook Simulator
Test your webhook integration without real payments
This is a simulator for our webhook service that allows our users in the development environment to emulate all different conditions any pix can take, for both pay-ins and pay-outs.
This is specifically useful also to adjust the authentication process, as simulated webhooks are signed with your signature_key, which comes with your credentials, exactly as real ones are. See Authentication.
Payments in the development environment (sandbox) are real. Sandbox QR codes are real Pix, paid from real bank accounts, and each charge's USDt settles to your wallet on Polygon mainnet. That is why this simulator exists: use it to test your webhook handling without moving real money.
How it works?
Before you start:
- Your
notif_endpointmust be configured with integrations support. The simulator delivers each webhook there; without one, the request is rejected with 400. - Set
pix_idto one of your ownoperation_ids (ptxr_…), as returned when you created the charge, so your handler can match the webhook to its charge.
Pick any webhook example from the documentation, you can edit the fields and post it to this endpoint. kamiPay delivers it to your notif_endpoint, signed with your signature_key as a real webhook is.
For instance, if you are testing pay-ins, you will generate a QR Code and then you can self-send the different status updates, like processing, done, failed without needing to make a real payment to that QR code.
This endpoint allows you to test all webhook handling flows without waiting for actual payment processing or making real transactions.
Test webhook
const url = `${baseURL}/v1/emulator/push_webhook`;
// Use any webhook JSON example from the documentation
const body = {
"pix_id": "ptxr_01kr3m9p7n2s4d8h6e5b3t7w1k",
"status": "done",
"tx_id": "0xa3f9b2c1e7d4865094bd28fa1c3e6b85907df42a3b9c1de80f5a672bc41e9d3f",
"timestamp": "2026-04-27 15:51:40.171821-03:00",
"type": "charge",
"qr_type": "dynamic",
"kamipay_id": "dqr_01kr3m9q5h7w2v4n6b8s3d5e9p",
"data": {
"bank_txid": "E29384756202604271548aB7cD3xY9mZ",
"bank_account_nr": "29384756-0001-0001349872",
"internal_pix_id": "8a3f1c9d72b46e08fd5a912ec47b3f06",
"amount_brl": "5.28",
"amount_usdt": "1.033355",
"address_out": "0x7a3F9b2C1e8D5462bA9c7F3e6D85907df41A2c3B",
"name": "Satoshi Nakamoto"
}
};
const response = await fetch(url, {
method: "POST",
headers: {
Authorization: `Bearer ${access_token}`,
"Content-Type": "application/json",
},
body: JSON.stringify(body),
});import requests
import json
url = f"{base_url}/v1/emulator/push_webhook"
# Use any webhook JSON example from the documentation
body = {
"pix_id": "ptxr_01kr3m9p7n2s4d8h6e5b3t7w1k",
"status": "done",
"tx_id": "0xa3f9b2c1e7d4865094bd28fa1c3e6b85907df42a3b9c1de80f5a672bc41e9d3f",
"timestamp": "2026-04-27 15:51:40.171821-03:00",
"type": "charge",
"qr_type": "dynamic",
"kamipay_id": "dqr_01kr3m9q5h7w2v4n6b8s3d5e9p",
"data": {
"bank_txid": "E29384756202604271548aB7cD3xY9mZ",
"bank_account_nr": "29384756-0001-0001349872",
"internal_pix_id": "8a3f1c9d72b46e08fd5a912ec47b3f06",
"amount_brl": "5.28",
"amount_usdt": "1.033355",
"address_out": "0x7a3F9b2C1e8D5462bA9c7F3e6D85907df41A2c3B",
"name": "Satoshi Nakamoto"
}
}
headers = {
'Authorization': f'Bearer {access_token}',
'Content-Type': 'application/json',
}
response = requests.post(url, json=body, headers=headers)package main
import (
"bytes"
"fmt"
"io/ioutil"
"net/http"
)
func main() {
url := fmt.Sprintf("%s/v1/emulator/push_webhook", baseURL)
// Use any webhook JSON example from the documentation, sent as raw JSON.
// Don't build it from a map: json.Marshal sorts a map's keys, so the
// simulated webhook would not arrive in the order a real one does.
requestBody := []byte(`{
"pix_id": "ptxr_01kr3m9p7n2s4d8h6e5b3t7w1k",
"status": "done",
"tx_id": "0xa3f9b2c1e7d4865094bd28fa1c3e6b85907df42a3b9c1de80f5a672bc41e9d3f",
"timestamp": "2026-04-27 15:51:40.171821-03:00",
"type": "charge",
"qr_type": "dynamic",
"kamipay_id": "dqr_01kr3m9q5h7w2v4n6b8s3d5e9p",
"data": {
"bank_txid": "E29384756202604271548aB7cD3xY9mZ",
"bank_account_nr": "29384756-0001-0001349872",
"internal_pix_id": "8a3f1c9d72b46e08fd5a912ec47b3f06",
"amount_brl": "5.28",
"amount_usdt": "1.033355",
"address_out": "0x7a3F9b2C1e8D5462bA9c7F3e6D85907df41A2c3B",
"name": "Satoshi Nakamoto"
}
}`)
// Create request
req, _ := http.NewRequest("POST", url, bytes.NewBuffer(requestBody))
req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", access_token))
req.Header.Add("Content-Type", "application/json")
// Make the request
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
fmt.Println("Error making request:", err)
return
}
defer resp.Body.Close()
// Read the response body
body, _ := ioutil.ReadAll(resp.Body)
fmt.Println(string(body))
}Response
A 200 from the simulator means that the webhook reached your endpoint, not that your handler accepted the webhook. response is the HTTP status that your endpoint answered, an error included:
{
"response": 200,
"payload": {
"pix_id": "ptxr_01kr3m9p7n2s4d8h6e5b3t7w1k",
"status": "done",
"tx_id": "0xa3f9b2c1e7d4865094bd28fa1c3e6b85907df42a3b9c1de80f5a672bc41e9d3f",
"timestamp": "2026-04-27 15:51:40.171821-03:00",
"type": "charge",
"qr_type": "dynamic",
"kamipay_id": "dqr_01kr3m9q5h7w2v4n6b8s3d5e9p",
"data": {
"bank_txid": "E29384756202604271548aB7cD3xY9mZ",
"bank_account_nr": "29384756-0001-0001349872",
"internal_pix_id": "8a3f1c9d72b46e08fd5a912ec47b3f06",
"amount_brl": "5.28",
"amount_usdt": "1.033355",
"address_out": "0x7a3F9b2C1e8D5462bA9c7F3e6D85907df41A2c3B",
"name": "Satoshi Nakamoto"
}
}
}Your notif_endpoint is not configured, so the simulator sent nothing:
{
"detail": "No notif_endpoint configured: ask integrations support to set one"
}With an invalid or expired token:
{
"detail": "Could not validate credentials"
}The simulator received no answer from your notif_endpoint, for example because of an invalid URL, a refused connection, or a timeout:
{
"detail": "Could not reach your notif_endpoint"
}The webhook simulator is only available in the development environment and should be used for testing purposes only.