Get paid by Brazilians. Automatically.
One API to collect Pix, Pix Automático and credit cards in up to 21 installments, settled straight to your BRL account. Card installments are paid to you upfront, so you never wait months for your money.
https://api.payjoia.com/v1Keysjoia_test_… sandboxjoia_live_… live150000 = R$1,500.00How money moves
- Create a charge with your order ID and amount. You get a hosted payer page and, for Pix, a copy-and-paste code.
- The payer pays by Pix, or by card in as many installments as you allow.
- joia settles it. Pix is credited instantly. Card installments are paid to you upfront, usually within 2 business days.
- joia pays you out by Pix to your registered BRL account, after every payment or once a day.
- You're notified at every step by signed webhooks, and you can pull a daily report that reconciles to the centavo.
Authentication
Send your secret key as a bearer token on every request. Keys are separate for each environment, and a sandbox key never touches live money.
Keep keys on your server and never ship them to a browser or app. You can roll a key from the dashboard at any time.
Platforms that operate several merchants use their own key and add a Joia-Account header to act on a connected account. See Platforms.
curl https://api.payjoia.com/v1/balance \
-H "Authorization: Bearer joia_test_your_key"curl https://api.payjoia.com/v1/balance \
-H "Authorization: Bearer joia_test_your_key" \
-H "Joia-Account: mer_inflowpay"Sandbox
Sandbox behaves exactly like live, with the same API, webhooks and reports, and moves no real money. Use the simulators to play the payer:
POST /v1/sandbox/charges/{id}/paywith{"method":"pix"}or{"method":"card","installments":12}POST /v1/sandbox/pix-automatic/authorizations/{id}/next-cycledebits the next recurring payment- On the hosted page, use the test card
5162 3062 1937 8829·05/30· CVV318
# Pix paid (also approves a Pix Automático first payment)
curl -X POST https://api.payjoia.com/v1/sandbox/charges/chg_…/pay \
-H "Authorization: Bearer joia_test_your_key" \
-H "Content-Type: application/json" \
-d '{ "method": "pix" }'
# Card paid in 12 installments
curl -X POST https://api.payjoia.com/v1/sandbox/charges/chg_…/pay \
-H "Authorization: Bearer joia_test_your_key" \
-H "Content-Type: application/json" \
-d '{ "method": "card", "installments": 12 }'
# Next Pix Automático cycle debited
curl -X POST https://api.payjoia.com/v1/sandbox/pix-automatic/authorizations/SUB-4471/next-cycle \
-H "Authorization: Bearer joia_test_your_key"Create a charge
/v1/chargesCreates a one-time charge. Sending the same order_id again returns the same charge, so retrying is always safe.
Parameters
order_idstringrequiredamountintegerrequiredmethodsarraypix and card. Default: both.max_installmentsintegerpayerobjectname, document (CPF or CNPJ), email, phone. Required when Pix is enabled.descriptionstringexpires_in_minutesintegermetadataobjectfees_paid_bystringmerchant (default) or payer. With payer, the installment cost is added to the payer's total and you receive the full amount. See Pricing.Sending the same order_id again returns the original charge with 200. A new charge returns 201. Reusing an order_id with a different amount or methods returns 409.
curl -X POST https://api.payjoia.com/v1/charges \
-H "Authorization: Bearer joia_test_your_key" \
-H "Content-Type: application/json" \
-d '{
"order_id": "INV-10293",
"amount": 150000,
"methods": ["pix", "card"],
"max_installments": 12,
"description": "Order INV-10293",
"payer": {
"name": "Maria Souza",
"document": "24971563792",
"email": "maria@example.com"
},
"metadata": { "merchant_ref": "ACME-77" }
}'{
"id": "chg_8f2kq0x7m1v3c9b4n6z2",
"object": "charge",
"type": "one_time",
"order_id": "INV-10293",
"status": "pending",
"amount": 150000,
"currency": "BRL",
"methods": ["pix", "card"],
"max_installments": 12,
"pay_url": "https://pay.payjoia.com/pay/chg_8f2kq0x7m1v3c9b4n6z2",
"pix": {
"copy_paste": "00020101021226820014br.gov.bcb.pix...",
"qr_code_png_base64": "iVBORw0KGgoAAAANSUhEUg...",
"expires_at": "2026-11-05 23:59:59"
},
"card_checkout_url": "https://...",
"payer": { "name": "Maria Souza", "document": "24971563792" },
"metadata": { "merchant_ref": "ACME-77" },
"created_at": "2026-11-04T18:20:11.204Z"
}Retrieve & list charges
/v1/charges/{id | order_id}/v1/charges?status=paid&limit=50A charge moves from pending to paid. It can also end as expired, failed, refunded, partially_refunded or chargeback.
Once paid, settlement shows what reached your balance. provider_net is the amount after processing and installment costs, and net_to_balance is what's available to pay out.
curl https://api.payjoia.com/v1/charges/INV-10293 \
-H "Authorization: Bearer joia_test_your_key"{
"id": "chg_8f2kq0x7m1v3c9b4n6z2",
"status": "paid",
"amount": 150000,
"installments": 12,
"settlement": {
"provider_net": 128298,
"intermediation_fee": 0,
"reserve_held": 0,
"refunds_and_chargebacks": 0,
"net_to_balance": 128298
},
"paid_at": "2026-11-04T18:22:40.118Z"
}Pricing & installments
Your contract sets a price for each method: Pix, cards paid upfront (all installments settled to you within 2 business days), or cards settled month by month. For every payment you receive exactly gross × (1 − your rate), so the amount you get never depends on processing costs.
- fees_paid_by: merchant: the payer sees the same total in any number of installments (“12x sem juros”), and you pay the installment rate.
- fees_paid_by: payer: each installment option shows its own total, as Brazilian law requires, and you receive the full
amount.amount_paidon the charge shows what the payer actually paid.
Each installment must be at least R$ 5.00, so max_installments is capped automatically for small amounts. Up to 21x on Visa and Mastercard, and up to 12x on other brands.
Refunds & cancellation
/v1/charges/{id}/refundRefunds the payer in full, or partially if you send amount in cents. The response is 202. Your balance is debited when the refund settles, and charge.refunded follows.
/v1/charges/{id}Cancels a pending charge, so its Pix code and any open card attempt stop working, and sends charge.cancelled. Charges also expire on their own at expires_at, which sends charge.expired.
Payer page
Every charge has a hosted page at pay_url. It's in Portuguese, built for phones, and loads in under a second. The payer picks Pix or card, and the page confirms the payment the moment it lands.
Once a charge is paid one way, the other way closes automatically, so it can never be paid twice. You can also skip the hosted page and show pix.copy_paste and the QR image in your own checkout.
Pix Automático
/v1/pix-automatic/authorizationsBrazil's recurring Pix. The payer authorizes once in their bank app, and every later payment is debited automatically, with no card on file and no failed-card churn.
The first payment and the authorization happen together: send the payer first_payment.pay_url or show its Pix code. The authorization becomes active when they approve it in their bank.
Parameters
referencestringrequiredpayerobjectrequiredname, document (CPF/CNPJ), email.frequencystringrequiredweekly, monthly, quarterly, semiannually or annually.first_payment_amountintegerrequiredstart_datedaterequiredamountintegerend_datedateretry_on_failurebooleandescriptionstringcurl -X POST https://api.payjoia.com/v1/pix-automatic/authorizations \
-H "Authorization: Bearer joia_test_your_key" \
-H "Content-Type: application/json" \
-d '{
"reference": "SUB-4471",
"payer": { "name": "Maria Souza", "document": "24971563792" },
"frequency": "monthly",
"amount": 4990,
"first_payment_amount": 4990,
"start_date": "2026-12-04",
"description": "Premium plan"
}'{
"id": "pxa_3m9v0c2k7q1x8b4n6z5w",
"object": "pix_automatic_authorization",
"reference": "SUB-4471",
"status": "pending",
"frequency": "monthly",
"amount": 4990,
"amount_type": "fixed",
"first_payment": {
"charge_id": "chg_1x8b4n6z5w3m9v0c2k7q",
"amount": 4990,
"status": "pending",
"pay_url": "https://pay.payjoia.com/pay/chg_1x8b4n6z5w3m9v0c2k7q",
"pix": { "copy_paste": "00020101021226810014br.gov.bcb.pix..." }
},
"start_date": "2026-12-04"
}Cycles & cancellation
/v1/pix-automatic/authorizations/{id}/chargesFor variable amounts, create each cycle 2 to 10 business days before due_date. The payer's bank schedules the debit, and you receive charge.scheduled, then charge.paid or charge.failed.
Fixed-amount authorizations create their cycles automatically. Each cycle is a regular charge: same settlement, payouts, reports and webhooks.
/v1/pix-automatic/authorizations/{id}Cancels the authorization, so no future cycle is debited. Payers can also cancel from their bank app, and you'll receive pix_automatic.authorization.cancelled.
/v1/pix-automatic/authorizations/{id}curl -X POST https://api.payjoia.com/v1/pix-automatic/authorizations/SUB-4471/charges \
-H "Authorization: Bearer joia_test_your_key" \
-H "Content-Type: application/json" \
-d '{ "order_id": "SUB-4471-2026-12", "amount": 5230, "due_date": "2026-12-10" }'curl -X DELETE https://api.payjoia.com/v1/pix-automatic/authorizations/SUB-4471 \
-H "Authorization: Bearer joia_test_your_key"Payouts
Your account has a payout mode, set at onboarding:
- per_payment (default): your available balance is sent by Pix right after each payment settles.
- daily: everything available is sent once a day.
- manual: nothing moves until you call the endpoint below.
/v1/payoutsParameters
Idempotency-Keyheaderrequiredamountintegerbrcodestringdestination_idstringGET /v1/destinations). Default: your default account.referencestringA payout goes from processing to paid, or to failed, in which case the money returns to your balance. A paid payout carries the Pix end_to_end_id and a receipt_url.
/v1/payouts/{id}Recipe: a fresh brCode per collection
If your BRL account issues a new brCode for every deposit, keep the account in manual payout mode and drive payouts yourself:
- Receive
charge.paid. Itsdata.amount,order_idandmetadatatell you which order settled. - Generate a brCode for that amount at your account provider.
- Call
POST /v1/payoutswithbrcode,amount,referenceset to your order id, andIdempotency-Keyset to your order id plus-payout. - Receive
payout.paidwith the Pixend_to_end_id. That's your proof of delivery.
joia only pays brCodes whose receiver is an account holder registered for you at onboarding, so a leaked key can't redirect funds. Each payout must fit your available balance, which you can check with GET /v1/balance.
curl -X POST https://api.payjoia.com/v1/payouts \
-H "Authorization: Bearer joia_test_your_key" \
-H "Idempotency-Key: settle-2026-11-04" \
-H "Content-Type: application/json" \
-d '{
"brcode": "00020101021226...6304A1B2",
"amount": 120000,
"reference": "Settlement 2026-11-04"
}'{
"id": "po_7c2k9q1x8b4n6z5w3m0v",
"object": "payout",
"status": "processing",
"method": "brcode",
"amount": 120000,
"currency": "BRL",
"fee": 0,
"destination": { "id": null, "pix_key": null, "holder_name": "INFLOWPAY BRL ACCOUNT" },
"reference": "Settlement 2026-11-04",
"end_to_end_id": null,
"receipt_url": null,
"created_at": "2026-11-04T19:02:11.482Z"
}Balance
/v1/balanceavailable can be paid out now. reserved is held for a set period, if your account has a reserve.
curl https://api.payjoia.com/v1/balance \
-H "Authorization: Bearer joia_test_your_key"{
"object": "balance",
"currency": "BRL",
"available": 1294310,
"reserved": 0,
"total": 1294310,
"payout_mode": "per_payment"
}Daily report
/v1/reports/daily?date=YYYY-MM-DDOpening and closing balance, settled payments, fees, refunds, chargebacks and payouts for one day in São Paulo time, with a line for every charge and payout. The opening balance plus movements always equals the closing balance. Add &format=csv for a spreadsheet.
curl https://api.payjoia.com/v1/reports/daily?date=2026-11-04 \
-H "Authorization: Bearer joia_test_your_key"{
"object": "daily_report",
"date": "2026-11-04",
"timezone": "America/Sao_Paulo",
"opening_balance": 0,
"credits": { "settled_payments": 1284310, "reserve_released": 0 },
"debits": { "intermediation_fee": 0, "refunds": 0, "chargebacks": 0, "payouts": 1284310, "transfer_fees": 0 },
"closing_balance": 0,
"charges": [ { "charge_id": "chg_…", "order_id": "INV-10293", "method": "card", "installments": 12, "gross": 150000, "provider_net": 128298 } ],
"payouts": [ { "payout_id": "po_…", "amount": 128298, "status": "paid", "end_to_end_id": "E1954…" } ]
}Limits
- API: 600 requests per minute per account. Every response carries
RateLimit-Limit,RateLimit-RemainingandRateLimit-Reset. A429includesRetry-After. - Charges: from R$ 5.00 to R$ 1,000,000.00. Installments of at least R$ 5.00 each.
- Payouts: from R$ 1.00. Pix payouts follow the daily and overnight (8 pm–6 am) Pix limits of the settlement account. Payouts above the overnight limit are sent the next morning automatically.
- Payer's bank statement: the payer's bank shows the receiving institution, Kodano, which collects on behalf of the merchant of record. The hosted payer page explains this to the payer.
Webhooks
joia sends an HTTPS POST to the URL you set in the dashboard, signed with your webhook secret in the Joia-Signature header. Verify it, and answer 2xx within 10 seconds. If you don't, we retry with backoff for about 24 hours.
Events are delivered at least once and ordered per object. Deduplicate on id.
/v1/webhook_endpointSet or change your endpoint URL with { "url": "https://…" }. Rotate the signing secret with POST /v1/webhook_endpoint/rotate_secret. The new secret is returned once.
/v1/events?after={event_id}&type=charge.paidPull events oldest first, using the last id you processed as after. has_more tells you to keep paging. GET /v1/events/{id} returns a single event. Polling and webhooks carry the same payloads.
{
"id": "evt_q1x8b4n6z5w3m0v7c2k9",
"type": "payout.paid",
"created_at": "2026-11-04T19:02:19.003Z",
"data": {
"payout_id": "po_7c2k9q1x8b4n6z5w3m0v",
"amount": 120000,
"end_to_end_id": "E19540550202611041902abcdEFGH",
"receipt_url": "https://..."
}
}# Header sent with every webhook:
Joia-Signature: t=1793790139,v1=5f0c…e81a
# v1 = hex(HMAC_SHA256(webhook_secret, t + "." + raw_body))Event types
Events
charge.paidcharge.scheduledcharge.failedcharge.expiredcharge.cancelledcharge.refundedcharge.chargebackpix_automatic.authorization.activepix_automatic.authorization.refusedpix_automatic.authorization.cancelledpix_automatic.authorization.expiredpayout.processingpayout.paidpayout.failedReliability
- Request-Id on every response. Quote it to support and we can trace the exact call.
- Joia-Version on every response. Breaking changes ship only in a new version, and the API stays the same if we change banking partners.
- Idempotency: charges by
order_id, Pix Automático byreference, payouts byIdempotency-Key. Retry any request safely. - Retries: on
502,503or a timeout, retry with the same key after a short backoff.503 provider_unavailablemeans retry in about 30 seconds. - OpenAPI 3.1 spec at /openapi.json, for generating a typed client. System status at /api/health.
Errors
Status codes
400invalid_request401unauthorized403account_not_allowed404not_found409order_id_conflict · reference_conflict · idempotency_key_reused409charge_not_pending · charge_expired422insufficient_funds · receiver_not_allowed · …502provider_unavailable{
"error": {
"code": "insufficient_funds",
"message": "Amount exceeds the available balance.",
"details": { "available": 1200 }
}
}Platforms
/v1/accountsA platform such as an FX or stablecoin company operates many merchants under one key. Each merchant is a connected account with its own balance, payouts, reports and webhooks. Add Joia-Account: mer_… to any request to act on one of them. New connected accounts are onboarded by joia after KYB.
The API is the contract. joia can change banking partners behind it without changing a single endpoint, field or webhook.