Payments
Creating charges
A charge is a single request for money from a buyer. Create it on your server, send the buyer to its checkout_url, and follow its status until it is paid, failed or expired.
On this page
Every payment in KrokoPay is a charge: an amount in BRL, a description, the methods the buyer may use and, optionally, the customer it belongs to. You create the charge with your secret key, KrokoPay hosts the payment page, and the charge moves through a small set of statuses that you can read from the API or receive as webhooks.
Information: Provisional contract
fetch. Use only test keys (kp_test_sk_…).The charge object
A charge carries its amount, what has happened to it so far and the link to its hosted checkout. Amounts are integers in centavos (minor units): 49700 means R$497.00.
Attributes
idstring- Unique identifier, prefixed with
chg_. objectstring- Always
charge. amountinteger- Amount to collect, in centavos.
currencystring- Three-letter currency code. Currently only
BRL. statusstring- One of
pending,paid,failed,expired,refunded,partially_refunded. See Statuses. methodstring | nullpixorcardonce the buyer chooses;nullwhile a pending charge has no method yet.cardobject | null- For card payments only:
brand(visa,mastercard,elo,amex) andlast4. Full card numbers are never returned. descriptionstring- Shown to the buyer on the checkout and on the receipt.
customer_idstring | null- The customer the charge belongs to.
product_idstring | null- Catalog product, when the charge comes from a price or payment link.
price_idstring | null- Catalog price, when the charge comes from a price or payment link.
link_idstring | null- The payment link that created the charge, if any.
subscription_idstring | null- The subscription this charge bills, if any.
feeinteger | null- Processing fee in centavos. Commercial terms are to be announced, so the examples on this site show
null. In the demo dashboard (Developers → Logs) it carries demo values. netinteger | nullamount − fee − refunded_amount.nullin these examples while fees are to be announced.refunded_amountinteger- Total refunded so far, in centavos.
failure_codestring | null- Why a card attempt failed:
card_declined,insufficient_funds,expired_card,incorrect_cvcorprocessing_error. created_atstring- ISO 8601 timestamp.
paid_atstring | null- When the payment was confirmed.
expires_atstring | null- For Pix: the moment an unpaid charge expires.
available_atstring | null- When the net amount moves from pending to available balance. Release periods are to be announced, so the examples show
null. checkout_urlstring- The hosted checkout for this charge. See Checkout.
{ "id": "chg_4Fp9KzW1xQm7Rt", "object": "charge", "amount": 49700, "currency": "BRL", "status": "paid", "method": "pix", "card": null, "description": "Curso Identidade Visual do Zero", "customer_id": "cus_8Hq2LmZr4TxN1a", "product_id": "prod_3Vb7QpK9sWd2Ee", "price_id": "price_5Kd1NcR8vYq3Hm", "link_id": "plink_2Wn6HsT9cVb4Lq", "subscription_id": null, "fee": null, "net": null, "refunded_amount": 0, "failure_code": null, "created_at": "2026-11-03T14:22:05.000Z", "paid_at": "2026-11-03T14:23:41.000Z", "expires_at": "2026-11-03T14:37:05.000Z", "available_at": null, "checkout_url": "https://krokopay.app/pay/chg_4Fp9KzW1xQm7Rt"}Create a charge
/v1/chargesCreate the charge from your server when the buyer is ready to pay, for example when they confirm an order. The response is a pending charge with its checkout_url.
Body parameters
amountintegerRequired- Positive integer in centavos.
1000is R$10.00. currencystringRequiredBRL.descriptionstringRequired- What the buyer is paying for. Keep it short and recognisable: it appears on the checkout and receipt.
customer_idstringOptional- Attach an existing customer (
cus_…). If omitted, the checkout collects the buyer's details and matches or creates a customer by email. methodsstring[]Optional- Methods offered on the checkout:
pix,cardor both. The buyer picks one.
curl -X POST https://api.krokopay.app/v1/charges \ -H "Authorization: Bearer kp_test_sk_4f9a2c7e1b8d4a6f9c3e5d7b" \ -H "Idempotency-Key: order_1042" \ -H "Content-Type: application/json" \ -d '{ "amount": 49700, "currency": "BRL", "description": "Curso Identidade Visual do Zero", "customer_id": "cus_8Hq2LmZr4TxN1a", "methods": [ "pix", "card" ] }'Warning: Always send an Idempotency-Key
Idempotency-Key header with a value tied to your order, so a retry returns the same charge instead of creating a second one. See Idempotency.There is no metadata field in the current proposal. Keep the mapping between your order and the charge on your side: store the returned id with the order. When the buyer chooses Pix, the charge receives an expires_at; in the test environment that is 15 minutes after the Pix code is generated, and the production window is to be defined.
Send the buyer to the checkout
Return the checkout_url to the browser and redirect. The secret key stays on your server; the browser only ever sees the URL.
// A fetch-style route handler on your server (Request in, Response out).// The secret key never reaches the browser.export async function POST(request: Request): Promise<Response> { const order = (await request.json()) as { id: string; amount: number; customerId: string }; const response = await fetch("https://api.krokopay.app/v1/charges", { method: "POST", headers: { Authorization: `Bearer ${process.env.KROKOPAY_SECRET_KEY}`, // kp_test_sk_… "Idempotency-Key": `order_${order.id}`, "Content-Type": "application/json", }, body: JSON.stringify({ amount: order.amount, // centavos currency: "BRL", description: `Order ${order.id}`, customer_id: order.customerId, methods: ["pix", "card"], }), }); if (!response.ok) return new Response("Could not create the charge", { status: 502 }); const charge = (await response.json()) as { id: string; checkout_url: string }; // Store charge.id with the order, then hand the URL to the browser. return Response.json({ checkout_url: charge.checkout_url });}Retrieve a charge
/v1/charges/{id}Fetch the current state of a charge by id. Use it to confirm a status after a webhook, or when the buyer returns to your site and you want to show the right page.
curl https://api.krokopay.app/v1/charges/chg_4Fp9KzW1xQm7Rt \ -H "Authorization: Bearer kp_test_sk_4f9a2c7e1b8d4a6f9c3e5d7b"404. A missing or invalid key answers 401. Both are described in Errors.List charges
/v1/chargesReturns your charges, newest first, wrapped in a list object.
Query parameters
limitintegerOptional- How many charges to return. The maximum is to be defined.
curl https://api.krokopay.app/v1/charges?limit=20 \ -H "Authorization: Bearer kp_test_sk_4f9a2c7e1b8d4a6f9c3e5d7b"Information: Filters and pagination
Statuses and transitions
A charge starts as pending and ends in exactly one outcome. Only a paid charge can later be refunded.
| From | To | When | Event |
|---|---|---|---|
| — | pending | The charge is created. | charge.created |
pending | paid | The buyer pays by Pix or the card is approved. | charge.paid |
pending | failed | A card attempt is declined. failure_code says why. | charge.failed |
pending | expired | A Pix charge is not paid before expires_at. | charge.expired |
paid | partially_refunded | Part of the amount is refunded. | charge.refunded |
paid / partially_refunded | refunded | Refunds reach the full amount. | charge.refunded |
Warning: Failed and expired charges are closed
{ "id": "chg_7Lc3PvN8yHd2Ws", "object": "charge", "amount": 49700, "currency": "BRL", "status": "failed", "method": "card", "card": { "brand": "visa", "last4": "0002" }, "description": "Curso Identidade Visual do Zero", "customer_id": "cus_8Hq2LmZr4TxN1a", "product_id": "prod_3Vb7QpK9sWd2Ee", "price_id": "price_5Kd1NcR8vYq3Hm", "link_id": null, "subscription_id": null, "fee": null, "net": null, "refunded_amount": 0, "failure_code": "card_declined", "created_at": "2026-11-03T16:02:19.000Z", "paid_at": null, "expires_at": null, "available_at": null, "checkout_url": "https://krokopay.app/pay/chg_7Lc3PvN8yHd2Ws"}To reproduce each outcome without real money, use the test cards listed in Test environment.
Refunds
/v1/charges/{id}/refundsRefund all or part of a paid charge. You can refund several times until the refunded total reaches the charge amount. The response is the updated charge.
Body parameters
amountintegerRequired- Centavos to refund. Positive, and no more than
amount − refunded_amount.
curl -X POST https://api.krokopay.app/v1/charges/chg_4Fp9KzW1xQm7Rt/refunds \ -H "Authorization: Bearer kp_test_sk_4f9a2c7e1b8d4a6f9c3e5d7b" \ -H "Idempotency-Key: refund_1042" \ -H "Content-Type: application/json" \ -d '{ "amount": 9900 }'curl -X POST https://api.krokopay.app/v1/charges/chg_4Fp9KzW1xQm7Rt/refunds \ -H "Authorization: Bearer kp_test_sk_4f9a2c7e1b8d4a6f9c3e5d7b" \ -H "Idempotency-Key: refund_1042_full" \ -H "Content-Type: application/json" \ -d '{ "amount": 49700 }'- Only
paidandpartially_refundedcharges can be refunded. - Each refund lowers
netand emitscharge.refunded. Refunds are paid from your available balance; if it is not enough, the refund is refused. - Use a distinct
Idempotency-Keyper refund operation, so a retried request cannot refund twice.