# Authentication (https://docs.weetpay.com/en/docs/authentication)
Client ID and client secret in the headers of every request.
Every API call carries the credentials generated under **Developers** in the dashboard. There is no session token or login step: each request is authenticated on its own, always over HTTPS.
## Accepted formats [#accepted-formats]
Use **one** of the three formats (they are equivalent):
```http
vspi: weetpay-100123
vsps: 3f1c9a52-7b0e-4d61-9a8c-2e5f4b7d1a90
```
Same names as the compatible gateway API: migrating requires no code changes, only the URL and keys.
```http
x-client-id: weetpay-100123
x-client-secret: 3f1c9a52-7b0e-4d61-9a8c-2e5f4b7d1a90
```
```http
Authorization: Basic d2VldHBheS0xMDAxMjM6M2YxYzlhNTItN2IwZS00ZDYxLTlhOGMtMmU1ZjRiN2QxYTkw
```
The value is `base64(":")`.
## Examples [#examples]
```bash
curl https://api.weetpay.com/api/v1/gateway/walletBalance \
-H "vspi: $WEETPAY_CLIENT_ID" \
-H "vsps: $WEETPAY_CLIENT_SECRET"
```
```js
const res = await fetch('https://api.weetpay.com/api/v1/gateway/walletBalance', {
headers: { vspi: process.env.WEETPAY_CLIENT_ID, vsps: process.env.WEETPAY_CLIENT_SECRET },
});
console.log(await res.json()); // { currentBalance: "397.70" }
```
```php
true,
CURLOPT_HTTPHEADER => ['vspi: ' . getenv('WEETPAY_CLIENT_ID'), 'vsps: ' . getenv('WEETPAY_CLIENT_SECRET')],
]);
var_dump(json_decode(curl_exec($ch), true));
```
```python
import os, requests
r = requests.get(
"https://api.weetpay.com/api/v1/gateway/walletBalance",
headers={"vspi": os.environ["WEETPAY_CLIENT_ID"], "vsps": os.environ["WEETPAY_CLIENT_SECRET"]},
timeout=15,
)
print(r.json())
```
## Authentication responses [#authentication-responses]
Messages are returned in Portuguese.
| HTTP | `message` | `code` | What to do |
| ----- | -------------------------------------------------------------- | ----------------- | ----------------------------------------------------------------------- |
| `401` | Credenciais ausentes | — | send the client ID and client secret |
| `401` | Credenciais inválidas | — | check your keys; the secret may have been updated in the dashboard |
| `403` | Verificação de identidade pendente | `KYC_REQUIRED` | complete identity verification |
| `403` | Conta bloqueada. Fale com o suporte. | `ACCOUNT_BLOCKED` | contact support |
| `429` | Limite de requisições por minuto atingido | `RATE_LIMITED` | wait and retry ([limits](/en/docs/limits-and-rules)) |
| `429` | Muitas tentativas com credenciais inválidas. Tente mais tarde. | `RATE_LIMITED` | the IP sent too many wrong credentials; fix them and wait a few minutes |
## Best practices [#best-practices]
The client secret gives access to your balance. **Never** put it in a mobile app, website (browser JavaScript) or repository. Call the API from your server only.
* Keep the keys in environment variables or a secrets vault.
* If you suspect a leak, use **Update credentials** under Developers: the old secret stops working immediately (the client ID stays the same).
* Keep **API payouts** disabled if you only receive payments — even with leaked keys, nobody can withdraw money.
* The client secret also signs your transaction callbacks: updating it changes the validation key ([Callbacks](/en/docs/webhooks)).
# Errors (https://docs.weetpay.com/en/docs/errors)
Single error format, HTTP status codes and the most common messages.
Every API error has the same shape:
```json
{ "response": "ERROR", "message": "amount inválido (em reais, ex.: 10.50)" }
```
Some authentication errors also carry `code` (`RATE_LIMITED`, `ACCOUNT_BLOCKED`, `KYC_REQUIRED`). `message` is in Portuguese and ready to log; to decide what to do in code, use the **HTTP status** (and `code`, when present).
## HTTP status [#http-status]
| HTTP | Meaning | Retry? |
| --------------------- | ------------------------------------------------------------------------- | ----------------------------------------------------------------------------------- |
| `200` | success | — |
| `400` | invalid data or business rule (balance, limit, duplicate `requestNumber`) | no, fix the request |
| `401` | missing or invalid credentials | no |
| `403` | account blocked, KYC pending or operation not enabled | no |
| `404` | transaction or endpoint not found | no |
| `409` | idempotency key reused with a different request | no |
| `429` | rate limit | yes, with backoff |
| `500` / `502` / `503` | temporary failure | yes, with growing backoff — for payouts, **always with the same `Idempotency-Key`** |
## Common messages [#common-messages]
### Charges [#charges]
| `message` | Cause |
| ---------------------------------------------------------------- | ---------------------------------------------------------------- |
| `requestNumber é obrigatório` | field missing or empty |
| `requestNumber já utilizado` | a charge with this `requestNumber` already exists on the account |
| `amount inválido (em reais, ex.: 10.50)` | zero, negative, non-numeric or more than 2 decimals |
| `client.name é obrigatório` | name shorter than 3 characters |
| `client.document inválido (CPF ou CNPJ)` | check digits do not match |
| `client.email inválido` | malformed e-mail |
| `callbackUrl inválida` | not http(s) or longer than 500 characters |
| `Split não está liberado nesta conta. Fale com o suporte.` (403) | `split` without Compliance approval |
| `split.account inválida para split` | destination account missing, inactive or your own |
| `split.percentageSplit deve ser um inteiro entre 1 e 100` | percentage out of range |
Amounts below the platform charge minimum or above its maximum also return `400`, with the limit in the message.
### Payouts [#payouts]
| `message` | Cause |
| ------------------------------------------------------------------------------- | ------------------------------------------------ |
| `Saque pela API não está liberado nesta conta. Ative em Desenvolvedores.` (403) | enable API payouts in the dashboard |
| `typeKey inválido (use CPF, CNPJ, EMAIL, PHONE ou EVP)` | unknown key type |
| `key inválida para o typeKey informado` | key does not match its type |
| `document deve ser o mesmo CPF/CNPJ da chave` | CPF/CNPJ key with a different `document` |
| `Saque só para a chave CPF/CNPJ do titular da conta…` (403) | account not enabled for third-party payouts |
| `insufficient balance` | balance lower than amount + payout fee |
| `O valor mínimo para saque é …` / `O valor máximo por saque é …` | outside per-payout limits |
| `Limite diário de saque atingido…` | the last 24 h total would exceed the daily limit |
| `Esta chave de idempotência já foi usada em outro saque` (409) | same `Idempotency-Key` with another amount/key |
| `Pagamento de PIX copia e cola (QRCODE) ainda não está disponível` | `typeKey: "QRCODE"` is not supported |
### Lookup [#lookup]
| `message` | Cause |
| -------------------------------------------------- | -------------------------------------- |
| `Informe requestNumber, idTransaction ou endToEnd` | body without any identifier |
| `Transação não encontrada` (404) | not found on your account |
| `Endpoint não encontrado` (404) | wrong path (check `/api/v1/gateway/…`) |
# Quickstart (https://docs.weetpay.com/en/docs/getting-started)
From sign-up to your first paid charge, in five steps.
### Create and verify your account [#create-and-verify-your-account]
Sign up at [app.weetpay.com](https://app.weetpay.com) and submit the identity verification documents. **The API only works after approval** — until then every call returns `403` with `code: "KYC_REQUIRED"`.
### Generate credentials [#generate-credentials]
In the dashboard, open **Developers** and reveal your keys with your transaction PIN. You get:
* **Client ID** — `weetpay-`, never changes;
* **Client secret** — a UUID. Keep it in a secrets vault; **Update credentials** issues a new one and revokes the old one immediately.
### Create a charge [#create-a-charge]
```bash
curl -X POST https://api.weetpay.com/api/v1/gateway/request-qrcode \
-H "Content-Type: application/json" \
-H "vspi: $WEETPAY_CLIENT_ID" \
-H "vsps: $WEETPAY_CLIENT_SECRET" \
-d '{
"requestNumber": "order-12345",
"amount": 49.90,
"callbackUrl": "https://yourstore.com/webhooks/weetpay",
"client": { "name": "Maria da Silva", "document": "12345678909", "email": "maria@email.com" }
}'
```
The response contains the PIX code (`paymentCode`) and the QR Code image (`paymentCodeBase64`):
```json
{
"idTransaction": "cmh1x2y3z0001ab12cd34ef56",
"paymentCode": "00020101021226850014br.gov.bcb.pix…",
"paymentCodeBase64": "iVBORw0KGgoAAAANSUhEUgAA…",
"response": "OK",
"status": "WAITING_FOR_APPROVAL"
}
```
Show the customer the image `data:image/png;base64,{paymentCodeBase64}` and a "copy" button with `paymentCode`.
### Receive the payment notice [#receive-the-payment-notice]
When the PIX is paid, WeetPay sends a `POST` to `callbackUrl`:
```json
{
"requestNumber": "order-12345",
"statusTransaction": "PAID_OUT",
"idTransaction": "cmh1x2y3z0001ab12cd34ef56",
"typeTransaction": "PIX",
"value": 49.9,
"debtorName": "MARIA DA SILVA",
"debtorDocument": "12345678909",
"date": "2026-10-30T13:03:40.000Z",
"endToEnd": "E18236120202610301303s0123456789"
}
```
Check the `X-WeetPay-Signature`, release the order and reply `200`. Details in [Callbacks and webhooks](/en/docs/webhooks).
### Check anytime [#check-anytime]
Use [Get transaction](/en/docs/transactions/get-transaction) to check the status at any time (for example, when the customer comes back to the order page) and [Get balance](/en/docs/account/balance) to see what is available.
## Reference clients [#reference-clients]
Copy-paste ready, no dependencies:
* [weetpay-client.mjs](/examples/weetpay-client.mjs) — Node.js 18+ (native `fetch`), with callback validation;
* [weetpay-client.php](/examples/weetpay-client.php) — PHP 7.4+ with cURL, with callback validation.
```js
import { weetpay } from './weetpay-client.mjs';
const { status, data } = await weetpay('POST', '/api/v1/gateway/request-qrcode', {
requestNumber: 'order-12345',
amount: 49.9,
client: { name: 'Maria da Silva', document: '12345678909' },
});
```
# Idempotency (https://docs.weetpay.com/en/docs/idempotency)
Retry requests safely — a charge or payout is never created twice.
Network failures happen: the request reaches WeetPay but the response is lost. The API is designed so you can **repeat the same request without fear**.
## Payouts (`pix-payment`) [#payouts-pix-payment]
Send a unique key per payout — ideally the payment ID in your system:
```http
POST /api/v1/gateway/pix-payment
Idempotency-Key: payout-2026-10-30-0001
```
The `x-idempotency-key` header is also accepted or, without a header, the `requestNumber` body field.
| Situation | Result |
| ------------------------------------- | ------------------------------------------------------------------------------------------------------------------- |
| first time | payout created, `200 { id, status: "processing" }` |
| same key, same amount and PIX key | **the same payout** — `200` with the same `id` and the `Idempotent-Replayed: true` header. Nothing is debited again |
| same key, different amount or PIX key | `409` — "Esta chave de idempotência já foi usada em outro saque" |
| no key at all | the same amount to the same PIX key within **1 minute** returns the previous payout |
The key is valid forever on your account (it does not expire) and has at most 100 characters.
Generate the key **once** per payment and store it before calling the API. Generating a new key on every retry defeats the protection.
## Charges (`request-qrcode`) [#charges-request-qrcode]
`requestNumber` is unique per account: a second charge with the same `requestNumber` is rejected with `400 requestNumber já utilizado`. If the creation response is lost, recover the charge with [Get transaction](/en/docs/transactions/get-transaction) using the `requestNumber` — the PIX code comes back in `pixCopiaECola` while it is pending.
## Callbacks [#callbacks]
On your side, notices may also arrive more than once (a retry after a timeout, for example). Process each `idTransaction` / `id` only once. See [Callbacks and webhooks](/en/docs/webhooks#delivery-and-retries).
# Introduction (https://docs.weetpay.com/en/docs)
Welcome to the WeetPay API documentation
Download OpenAPI (Postman/Insomnia)
AI guide (llms.txt)
Quickstart
**WeetPay** is a PIX payment gateway. Through the API your application creates dynamic QR Code charges, gets real-time payment notices, sends PIX to any key and looks up transactions and balance — with a handful of HTTP calls and amounts in BRL reais.
## What you can do [#what-you-can-do]
Incoming payments (**cash-in**) become PIX "copia e cola" charges with a ready-to-display QR Code. When the customer pays, the amount is credited to your balance and WeetPay notifies your system at `callbackUrl`. Outgoing payments (**cash-out**) leave your balance to a PIX key, protected against duplicate sending, with the result also delivered by callback.
## Why integrate? [#why-integrate]
four endpoints, plain JSON and amounts in reais (
`49.90`
)
same paths, fields and responses as the most widely used gateway API — migrate by swapping the URL and keys
payment and payout notices with HMAC-SHA256 and automatic retries
Idempotency-Key guarantees a PIX is never sent twice
PIX code and base64 PNG image in the same response
share incoming payments with another WeetPay account (upon approval)
## Modules [#modules]
## Base URLs [#base-urls]
| Environment | URL |
| ---------------- | ------------------------------------------------------------ |
| API (production) | `https://api.weetpay.com` |
| Dashboard | `https://app.weetpay.com` — credentials under **Developers** |
| Documentation | `https://docs.weetpay.com` |
Every request carries the **client ID** and **client secret** in the `vspi` and `vsps` headers. Errors always look like `{ "response": "ERROR", "message": "…" }`. See [Authentication](/en/docs/authentication) and [Errors](/en/docs/errors).
## Next steps [#next-steps]
Follow the [Quickstart](/en/docs/getting-started) to create your first charge in minutes. For assistants and coding agents, use [llms.txt](/llms.txt?lang=en) (index) or [llms-full.txt](/llms-full.txt?lang=en) (full content).
# Limits and rules (https://docs.weetpay.com/en/docs/limits-and-rules)
Request limits, amount limits and the security rules for payouts and split.
## Requests [#requests]
| Limit | Value | Response |
| -------------------------- | ------------------------------------------------- | ----------------------------- |
| Per account (client ID) | **600 requests per minute** | `429`, `code: "RATE_LIMITED"` |
| Invalid credentials per IP | 30 failures in 5 minutes block the IP for a while | `429`, `code: "RATE_LIMITED"` |
On `429`, wait a few seconds and retry with growing backoff. To track payments, use [callbacks](/en/docs/webhooks) instead of polling.
## Amounts [#amounts]
* Amounts are always in **reais**, with up to 2 decimals (`49.9`, `"49.90"` or `"49,90"`).
* Charges and payouts have a **per-operation minimum and maximum** set by WeetPay; payouts also have a **daily limit** (rolling 24 h window). When a limit is exceeded, the error message states the allowed value.
* **QR Code validity** is set by the platform (default: 1 hour). After that the charge becomes `EXPIRED`.
* Incoming and payout fees follow your contract and are shown in the dashboard. Incoming payments are credited net of fees; the payout fee is debited together with the amount.
## API payouts [#api-payouts]
**Enabled by you.** API payouts are **off** by default. Turn them on under **Developers** in the dashboard (requires your transaction PIN). While off, `pix-payment` returns `403`.
**Destination.** Unless WeetPay enables it, payouts can only go to the **account holder's CPF/CNPJ key**. To pay third parties (suppliers, affiliates, customers), ask support to enable it.
**Approval.** Payouts above the platform's auto-approval amount are reviewed by operations before being sent. Meanwhile the payout stays `PROCESSING` and the amount stays reserved.
**Result.** Paid or not, the result arrives at `callbackUrl`. A payout that is not paid (rejected on review, unknown key, bank failure) has its amount and fee **refunded** automatically.
## Split [#split]
The `split` field on [Create PIX charge](/en/docs/cash-in/create-charge) shares the received amount with another WeetPay account. Since it moves money between accounts, it only works for accounts **enabled by Compliance**. The destination account must be active with approved verification; the percentage is an integer from 1 to 100.
## Identity verification [#identity-verification]
The API only answers for accounts with **approved** identity verification (KYC) that are not blocked. Until then, every call returns `403` with `code: "KYC_REQUIRED"`.
# Callbacks and webhooks (https://docs.weetpay.com/en/docs/webhooks)
Signed payment and payout notices — the right way to reconcile without polling the API.
WeetPay notifies your system in two ways, which can be used together:
| | **Transaction callback** | **Dashboard webhook** |
| --------------- | ------------------------------------------------------- | ---------------------------------------------------------------- |
| Where to set it | `callbackUrl` field on each charge or payout in the API | **Webhooks** in the dashboard, one URL per event |
| Format | the compatible API payload (amounts in **reais**) | envelope `{ id, event, createdAt, data }` (amounts in **cents**) |
| Signed with | the API **client secret** | the webhook secret (`whsec_…`), shown in the dashboard |
| Events | charge paid; payout result | charge created, paid, refunded; payout completed, failed |
Both: `POST` with `Content-Type: application/json`, signature in the `X-WeetPay-Signature` header and automatic retries on failure.
## Charge paid callback [#charge-paid-callback]
Sent to the `callbackUrl` from [Create PIX charge](/en/docs/cash-in/create-charge) when the PIX is paid:
```json
{
"requestNumber": "order-12345",
"statusTransaction": "PAID_OUT",
"idTransaction": "cmh1x2y3z0001ab12cd34ef56",
"typeTransaction": "PIX",
"value": 49.9,
"debtorName": "MARIA DA SILVA",
"debtorDocument": "12345678909",
"date": "2026-10-30T13:03:40.000Z",
"endToEnd": "E18236120202610301303s0123456789"
}
```
| Field | Description |
| ------------------------------ | --------------------------------------------------- |
| `requestNumber` | the identifier you sent on creation |
| `statusTransaction` | always `PAID_OUT` (only payments trigger callbacks) |
| `idTransaction` | WeetPay charge ID |
| `value` | paid amount, in reais (number) |
| `debtorName`, `debtorDocument` | who paid, as reported by the bank |
| `date` | payment date/time (ISO 8601, UTC) |
| `endToEnd` | PIX End-to-End ID |
## Payout callback [#payout-callback]
Sent to the `callbackUrl` from [Send PIX](/en/docs/cash-out/pix-payment) when the payout finishes:
```json
{
"id": "cmh1x9k2m0004ab12xy98zt76",
"status": "sucess",
"date": "2026-10-30T14:10:05.000Z",
"message": "",
"value": 150,
"endToEnd": "E00416968202610301410abcdEFGH1234"
}
```
```json
{
"id": "cmh1x9k2m0004ab12xy98zt76",
"status": "not paid",
"date": "2026-10-30T14:10:05.000Z",
"message": "Chave PIX não encontrada",
"value": 150,
"endToEnd": ""
}
```
`status` is `"sucess"` (spelled this way for compatibility with the original gateway API) or `"not paid"`. A payout that is not paid has its **amount and fee refunded** to your balance automatically; the reason is in `message`.
## Dashboard webhooks [#dashboard-webhooks]
Register a URL for each event you want under **Webhooks** in the dashboard. The body comes in an envelope:
```json
{
"id": "cmh1xa0b10007ab12qq11ww22",
"event": "TRANSACTION_PAID",
"createdAt": "2026-10-30T13:03:41.000Z",
"data": {
"id": "cmh1x2y3z0001ab12cd34ef56",
"externalId": "order-12345",
"status": "PAID",
"amount": 4990,
"fee": 99,
"netAmount": 4891,
"description": "Basic t-shirt",
"pixCopiaECola": "00020101021226850014br.gov.bcb.pix…",
"endToEndId": "E18236120202610301303s0123456789",
"payer": { "name": "MARIA DA SILVA", "document": "12345678909" },
"expiresAt": "2026-10-30T14:02:11.000Z",
"paidAt": "2026-10-30T13:03:40.000Z",
"createdAt": "2026-10-30T13:02:11.000Z"
}
}
```
| `event` | When | `data` |
| ---------------------- | ------------------------------------ | ---------------------------------------------- |
| `TRANSACTION_CREATED` | charge created | charge |
| `TRANSACTION_PAID` | charge paid | charge |
| `TRANSACTION_REFUNDED` | charge refunded / disputed | charge + `refund { id, kind, amount, reason }` |
| `WITHDRAWAL_COMPLETED` | payout paid | payout |
| `WITHDRAWAL_FAILED` | payout rejected or failed (refunded) | payout |
On charges, `externalId` is your `requestNumber`; `amount`, `fee` and `netAmount` are in **cents**. Payouts carry `id`, `transactionId`, `status` (`PAID`, `FAILED`, `REJECTED`), `amount`, `fee`, `pixKeyType`, `pixKey`, `endToEndId`, `reason`, `paidAt` and `createdAt`.
## Headers [#headers]
| Header | Value |
| --------------------- | --------------------------------------------------------------------- |
| `X-WeetPay-Signature` | `t=,v1=` — see below |
| `X-WeetPay-Event` | event (`TRANSACTION_PAID`, `WITHDRAWAL_COMPLETED`…) |
| `X-WeetPay-Delivery` | delivery ID — the same across all attempts; use it to drop duplicates |
| `User-Agent` | `WeetPay-Webhooks/1.0` |
## Validating the signature [#validating-the-signature]
`v1` is the hex HMAC-SHA256 of `"."`, keyed with the **client secret** (transaction callbacks) or the **webhook secret** (dashboard webhooks). Compute it over the body **exactly as received**, before any parsing, compare in constant time and reject notices whose `t` is too old (e.g. over 5 minutes).
```js
import { createHmac, timingSafeEqual } from 'node:crypto';
export function validWeetPaySignature(rawBody, header, secret, toleranceSec = 300) {
const parts = Object.fromEntries((header ?? '').split(',').map((p) => p.split('=')));
const t = Number(parts.t);
if (!t || !parts.v1 || Math.abs(Date.now() / 1000 - t) > toleranceSec) return false;
const expected = createHmac('sha256', secret).update(`${t}.${rawBody}`).digest('hex');
return expected.length === parts.v1.length && timingSafeEqual(Buffer.from(expected), Buffer.from(parts.v1));
}
```
```php
$tolerance) return false;
return hash_equals(hash_hmac('sha256', $t . '.' . $rawBody, $secret), $parts['v1']);
}
```
```python
import hmac, hashlib, time
def valid_weetpay_signature(raw_body: bytes, header: str, secret: str, tolerance: int = 300) -> bool:
parts = dict(p.split("=", 1) for p in (header or "").split(",") if "=" in p)
t = int(parts.get("t", 0))
if not t or "v1" not in parts or abs(time.time() - t) > tolerance:
return False
expected = hmac.new(secret.encode(), f"{t}.".encode() + raw_body, hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, parts["v1"])
```
## Delivery and retries [#delivery-and-retries]
* Reply with any **2xx within 10 seconds**. Any other status, a timeout or a connection error counts as a failure.
* Failures are retried after **1, 5, 15, 60, 180, 360, 720 and 1440 minutes** (up to 9 attempts, about 2 days). After that the delivery is marked as failed in the history; you can resend it manually from the dashboard.
* The same notice may arrive more than once: key on `idTransaction` / `id` (or `X-WeetPay-Delivery`) and make processing **idempotent**.
* The URL must be **public http(s)**. Redirects are not followed and internal network addresses (localhost, 10.x, 192.168.x…) are refused.
* Reply fast and process later (queue): the notice does not wait for your system to finish the order.
Do not rely on the notice body alone to release a high-value order: validate the signature or confirm with [Get transaction](/en/docs/transactions/get-transaction).
# Get balance (https://docs.weetpay.com/en/docs/account/balance)
{/* */}
# Account (https://docs.weetpay.com/en/docs/account)
Available WeetPay wallet balance.
`GET /api/v1/gateway/walletBalance` returns the available balance in reais, as a string with two decimals: `{ "currentBalance": "397.70" }`.
* Paid charges are credited **net** of the incoming fee.
* Payouts reserve the amount **and** the fee when requested; if the payout is not paid, both return to the balance.
* Before a payout, check that `currentBalance` covers the amount plus the payout fee — otherwise the API returns `insufficient balance`.
Statements, reports and settlements are available in the [dashboard](https://app.weetpay.com).
# Create PIX charge (https://docs.weetpay.com/en/docs/cash-in/create-charge)
{/* */}
# Cash-in (https://docs.weetpay.com/en/docs/cash-in)
Receive payments with PIX "copia e cola" and dynamic QR Codes.
`POST /api/v1/gateway/request-qrcode` creates a PIX charge. The response contains the PIX code (`paymentCode`) and the QR Code PNG image in base64 (`paymentCodeBase64`) for you to show the customer.
## Flow [#flow]
1. Your server calls [Create PIX charge](/en/docs/cash-in/create-charge) with the order's `requestNumber`.
2. You display the QR Code and a "copy code" button.
3. The customer pays in their banking app.
4. WeetPay credits the amount (net of fees) to your balance and sends a `POST` to `callbackUrl` with `statusTransaction: "PAID_OUT"`.
5. Your system validates the signature, releases the order and replies `200`.
## Displaying the QR Code [#displaying-the-qr-code]
```html
```
## Charge status [#charge-status]
| `processingStatus` | Meaning |
| ---------------------- | ------------------------------------- |
| `WAITING_FOR_APPROVAL` | awaiting payment |
| `PAID_OUT` | paid — amount credited to the balance |
| `EXPIRED` | QR expired unpaid (default: 1 hour) |
| `CANCELED` | canceled by operations |
| `REFUNDED` | refunded / disputed (MED) |
## Tips [#tips]
* Use one `requestNumber` per payment attempt. If the QR expires, create another charge with a new `requestNumber` (e.g. `order-12345-2`).
* Send the payer's real CPF/CNPJ in `client.document`: it is validated and forwarded to the bank.
* Pass `utm_source`, `utm_campaign`… in the query string to store the sale's origin with the charge.
# Cash-out (https://docs.weetpay.com/en/docs/cash-out)
Send PIX from your balance to any key, protected against duplicate sending.
`POST /api/v1/gateway/pix-payment` debits the amount and fee from your balance and sends a PIX to the given key. The response is immediate (`status: "processing"`); the result arrives at `callbackUrl`.
Before the first payout, turn on **API payouts** under **Developers** in the dashboard. Unless WeetPay enables it, the destination must be the account holder's own CPF/CNPJ key. See [Limits and rules](/en/docs/limits-and-rules#api-payouts).
## Key types [#key-types]
| `typeKey` | `key` | `document` |
| --------- | -------------------------------------------------------------- | ------------------------ |
| `CPF` | 11 digits | the same CPF as the key |
| `CNPJ` | 14 digits | the same CNPJ as the key |
| `EMAIL` | e-mail | key holder's CPF/CNPJ |
| `PHONE` | mobile with area code (e.g. `11999998888` or `+5511999998888`) | key holder's CPF/CNPJ |
| `EVP` | random key (UUID) | key holder's CPF/CNPJ |
## Flow [#flow]
1. Generate and store an `Idempotency-Key` for the payment ([Idempotency](/en/docs/idempotency)).
2. Call [Send PIX](/en/docs/cash-out/pix-payment). On network errors or `5xx`, retry **with the same key**.
3. The payout stays `PROCESSING` (operations review, when applicable, and bank submission).
4. `callbackUrl` receives `status: "sucess"` with the `endToEnd`, or `status: "not paid"` with the reason in `message` — in which case amount and fee return to the balance.
## Payout status [#payout-status]
| `processingStatus` (lookup) | Callback `status` | Meaning |
| --------------------------- | ----------------- | -------------------------------- |
| `PROCESSING` | — | under review or sent to the bank |
| `PAID_OUT` | `sucess` | paid |
| `NOT_PAID` | `not paid` | rejected or failed; refunded |
# Send PIX (payout) (https://docs.weetpay.com/en/docs/cash-out/pix-payment)
{/* */}
# Get transaction (https://docs.weetpay.com/en/docs/transactions/get-transaction)
{/* */}
# Transactions (https://docs.weetpay.com/en/docs/transactions)
Look up charges and payouts by any identifier.
`POST /api/v1/gateway/walletTransaction` returns a charge (`transactionType: "PIX_IN"`) or a payout (`"PIX_OUT"`) from your account, in the same format.
## Identifiers [#identifiers]
| Field | Finds |
| --------------- | -------------------------------------- |
| `requestNumber` | charge, by the identifier you sent |
| `idTransaction` | charge or payout, by WeetPay ID |
| `endToEnd` | charge or payout, by PIX End-to-End ID |
## Status [#status]
| `processingStatus` | Type | Meaning |
| ---------------------- | --------- | ----------------------------------------------- |
| `WAITING_FOR_APPROVAL` | `PIX_IN` | awaiting payment |
| `PAID_OUT` | both | paid |
| `EXPIRED` | `PIX_IN` | expired unpaid |
| `CANCELED` | `PIX_IN` | canceled |
| `REFUNDED` | `PIX_IN` | refunded |
| `PROCESSING` | `PIX_OUT` | under review or being sent |
| `NOT_PAID` | `PIX_OUT` | not paid (reason in `message`), amount refunded |
## When to look up [#when-to-look-up]
* The customer is back on the order page and you have not received the callback yet.
* Reconciliation or fallback (your server was down).
* To recover the `pixCopiaECola` of a pending charge.
Pending charges are re-checked with the bank on lookup (at most once every 10 s). Do not poll in a loop: use [callbacks](/en/docs/webhooks).