Webhooks in the sandbox
Delivery only after the URL proves ownership: the X-Callback-Challenge, the pending, verified and failed statuses, and how to re-run the check.
In production, a webhook registered with POST /v1/user/webhooks receives deliveries immediately. In the sandbox anyone can generate a credential and point a webhook anywhere, so delivery only starts after the URL proves it is yours:
- On registration, the sandbox sends a
POSTto the URL with the token in theX-Callback-Challengeheader and body{}. - The endpoint answers
2xxechoing the token, either as the raw body or as{"challenge":"<token>"}. - Until it passes, the webhook stays
pendingand no delivery goes out.
The token travels only in the header, so a service that echoes the received body does not pass: only an endpoint that reads the header on purpose does. The URL must be public HTTPS; private addresses and redirects are refused.
app.post('/webhook', (req, res) => {
const challenge = req.get('X-Callback-Challenge');
if (challenge) return res.status(200).json({ challenge });
return handleCallback(req, res);
});Register the webhook
Register the URL with a secret to test the signature. The secret comes back only once in the response.
curl -X POST https://pix.sandbox.payzu.dev/v1/user/webhooks \
-H "Authorization: Bearer $SANDBOX_TOKEN" \
-H "Content-Type: application/json" \
-d '{"url":"https://sua-app.com.br/webhook","generateSecret":true}'Confirm ownership was verified before moving on: GET /sandbox/webhooks/{id}/challenge must answer "status": "verified".
Check and re-run the verification
When a delivery does not happen, check the reason:
curl https://pix.sandbox.payzu.dev/sandbox/webhooks/$WEBHOOK_ID/challenge \
-H "Authorization: Bearer $SANDBOX_TOKEN"{
"webhookId": "cmh2k1x9d0001s6bwjb5bez01",
"url": "https://sua-app.com.br/webhook",
"status": "failed",
"detail": "o endpoint respondeu 2xx sem ecoar o token, no corpo ou em {\"challenge\"}",
"checkedAt": "2026-08-23T12:00:00.000Z"
}status is pending, verified or failed. After fixing the endpoint, POST /sandbox/webhooks/{id}/challenge runs the check again. Deliveries follow the production format and signature: see HMAC verification.