Developers
POST a charge.Get a webhook.
REST API, per-environment keys and signed webhooks. Test it all in sandbox.
curl -X POST https://api.yuvexpay.com/v1/payments \
-H "Authorization: Bearer ypk_test_ab12cd34ef_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
-H "Content-Type: application/json" \
-H "X-Idempotency-Key: order-1234-attempt-1" \
-d '{
"amount": 49.90,
"methods": ["PIX"],
"currency": "BRL",
"mode": "headless",
"description": "Order #1234",
"externalId": "order-1234",
"expiresInMinutes": 30,
"customer": {
"name": "Pedro Álvares Cabral",
"email": "[email protected]"
}
}'Verify the signature.Reject the rest.
HMAC-SHA256 over the timestamp and raw body, compared in constant time.
- Event in the header
X-Webhook-Event. The body has no envelope. - At-least-once deliveryDeduplicate by
X-Webhook-Delivery-Id. - 300 secondsRecommended replay window.
import crypto from "crypto";
const SKEW_SECONDS = 300;
function verifyWebhook(rawBody, headers, secret) {
const ts = headers["x-webhook-timestamp"];
const sig = headers["x-webhook-signature"];
if (!ts || !sig) return false;
const drift = Math.abs(Math.floor(Date.now() / 1000) - parseInt(ts, 10));
if (drift > SKEW_SECONDS) return false;
const expected = "v1=" + crypto
.createHmac("sha256", secret)
.update(`${ts}.${rawBody}`)
.digest("hex");
const a = Buffer.from(sig);
const b = Buffer.from(expected);
return a.length === b.length && crypto.timingSafeEqual(a, b);
}
app.post("/webhook", express.raw({ type: "application/json" }), (req, res) => {
const rawBody = req.body.toString("utf8");
if (!verifyWebhook(rawBody, req.headers, process.env.WEBHOOK_SECRET)) {
return res.status(401).send("Invalid signature");
}
const eventType = req.headers["x-webhook-event"];
const deliveryId = req.headers["x-webhook-delivery-id"];
const body = JSON.parse(rawBody);
if (alreadyProcessed(deliveryId)) return res.status(200).send("OK");
switch (eventType) {
case "PAYMENT_PAID":
console.log(body.txId, body.amount, body.payer?.document ?? null);
break;
case "WITHDRAWAL_SENT":
console.log(body.txId, body.recipient?.document ?? null);
break;
case "WITHDRAWAL_FAILED":
console.log(body.txId, body.failureReason);
break;
}
res.status(200).send("OK");
});Test without real money.The cents decide.
With a ypk_test_ key, the cents of the amount pick the outcome for Pix, card and boleto.
| Cents | Outcome |
|---|---|
| .01 | Paid, about 5 s later |
| .02 | Stays open |
| .03 | Cancelled |
| .04 | Expired |
| .05 | Paid |
| Others | Paid |
| Cents | Outcome |
|---|---|
| .01 | Completed |
| .02 | Failed: insufficient funds at the provider |
| .03 | Failed: invalid Pix key |
| .04 | Stays processing |
| Others | Completed |
To force a status right away, use POST /v1/payments/{id}/simulate.
Sandbox sends no webhooks. Poll the status or send a test event from the dashboard.
The API contract.Same in production.
What holds on every call.
Authentication
Bearer with
ypk_test_orypk_live_. The prefix picks the environment; the URL stays the same.Scopes
Each key does only what it may:
payments:write,withdrawals:read,balance:readand more.Idempotency
X-Idempotency-Keyon POSTs that create resources. Valid for 24 h; a different body under the same key gets 409.Errors
A stable
error.code, a readable message and per-fielddetails.Rate limits
1,000 GET, 100 POST and 2,000 total per minute, per company, with
X-RateLimit-*headers.Versioning
The API version is pinned per key. You upgrade when you choose.
The charge lifecycle.No guesswork.
The main path and the possible detours. The relevant ones become webhooks.
Main path
- NEW
- PROCESSING
- CONFIRMED
- PAID
- PARTIAL_REFUND
- REFUNDED
Before payment
- PENDING_METHOD_SELECTION
- EXPIRED
- CANCELLED
After payment
- CHARGEBACK
- MED_FROZEN
PENDING_METHOD_SELECTIONonly on hosted checkout.- An expired boleto can still become
PAID.
Integrate your way.With or without code.
Write it yourself, ask an AI or connect blocks. One API for all of it.
REST API with signed webhooks.
- Amounts in BRL, like
49.90 X-Idempotency-Key: a retry never charges twice- Webhooks signed with
HMAC-SHA256 - Sandbox with a
ypk_test_key
Works with
- Claude Code
- Cursor
- Codex
- Windsurf
- GitHub Copilot
- v0
- Replit
- n8n
- Make
- Zapier
Your next salecan land here.
No monthly fee. No setup. Just a fee per sale.
Every account starts in sandbox, with test keys.