> ## Documentation Index
> Fetch the complete documentation index at: https://docs.wyzecash.com.br/llms.txt
> Use this file to discover all available pages before exploring further.

# SDK para Python

> O pacote oficial wyzecash para Python 3.10+: cobranças, saques, subcontas e webhooks com tipos e mensagens em português, sem dependências.

O `wyzecash` é o SDK oficial da WyzeCash para **Python 3.10+**. Ele fala com a mesma API da documentação, só que com tipos, listas que passam de página sozinhas, novas tentativas seguras e a conferência dos webhooks prontas.

> [card] Sem dependências
>
> Usa só a biblioteca padrão do Python. Nada de `requests` para instalar.

> [card] Tipos em português
>
> Cada método tem os campos da API, e as respostas têm tipo. O editor completa e mostra a descrição.

> [card] Seguro com dinheiro
>
> O que tira dinheiro nunca é repetido sozinho.

> [!WARNING]
>
> Use o SDK **só no servidor**. A chave de API vai em todo pedido: nunca a coloque em front-end, app ou repositório.

## Instalar

```bash pip theme={null}
pip install wyzecash
```

```bash uv theme={null}
uv add wyzecash
```

```bash poetry theme={null}
poetry add wyzecash
```

## Começar

**1. Crie a chave de API**

No painel, em **Integrações e API › Chaves de API**, crie uma chave `bk_live_…` só com as permissões que o seu sistema usa (ex.: `/payments/create` e `/payments/get`).

**2. Guarde a chave no ambiente**

```bash .env theme={null}
WYZECASH_API_KEY=bk_live_...
```

Sem passar a chave no código, o SDK lê a variável `WYZECASH_API_KEY`.

**3. Crie a primeira cobrança**

```python theme={null}
import os
from wyzecash import WyzeCash

wyzecash = WyzeCash(os.environ["WYZECASH_API_KEY"])

cobranca = wyzecash.payments.create(
    amount=15000,  # R$ 150,00, sempre em centavos
    description="Pedido 1052",
    idempotency_key="pedido-1052",
)

print(cobranca["id"])         # BKY-...
print(cobranca["copyPaste"])  # Pix Copia e Cola (vira QR Code)
```

**4. Receba o aviso de pago**

Pelo [webhook](#webhooks). Libere o pedido só no `payment.paid`.

> [!NOTE]
>
> Não existe ambiente de testes: todo pedido é de verdade. Para testar, faça uma cobrança pequena e pague com o seu banco.

## Campos e respostas

Os campos têm **os mesmos nomes da API** (`subaccountId`, `feePaidBy`, `cryptoCurrency`...), então os exemplos da [Referência da API](https://docs.wyzecash.com.br/referencia/visao-geral.md) valem direto. Só `from` vira `from_`, porque é palavra do Python.

* As respostas são dicionários com tipo (`TypedDict`): `cobranca["copyPaste"]`, com o editor completando o nome.
* Campo que você não passa não vai no pedido.
* `None` vai como `null`, que em alguns campos limpa o valor: `limits={"perWithdrawal": None}` tira o limite próprio da subconta.

## Configurar o cliente

```python theme={null}
from wyzecash import WyzeCash

wyzecash = WyzeCash(
    os.environ["WYZECASH_API_KEY"],
    timeout=60.0,   # tempo máximo de cada pedido, em segundos (padrão 60)
    max_retries=2,  # novas tentativas quando é seguro (padrão 2)
)
```

| Opção | Padrão | O que faz |
| - | - | - |
| `api_key` (o 1º parâmetro) | a variável `WYZECASH_API_KEY` | A chave `bk_live_…` |
| `timeout` | `60.0` | Tempo máximo de cada pedido, em segundos |
| `max_retries` | `2` | Quantas vezes tentar de novo quando é seguro |
| `base_url` | o endereço da API | Só para casos especiais (proxy) |
| `transport` | o `urllib` da biblioteca padrão | Outro jeito de fazer o pedido HTTP (testes ou proxy) |

Cada método também aceita `timeout` e `max_retries`, e os que pedem a `Idempotency-Key`, o `idempotency_key`.

## Recursos

Os métodos seguem as rotas da API. Cada um precisa da permissão daquela rota na chave.

| Recurso | Métodos | Rotas |
| - | - | - |
| `payments` | `create`, `methods`, `list`, `retrieve` | [Cobranças](https://docs.wyzecash.com.br/endpoints/cobrancas.md) |
| `payment_links` | `create`, `list`, `retrieve`, `update`, `delete`, `list_payments` | [Links de pagamento](https://docs.wyzecash.com.br/endpoints/links-de-pagamento.md) |
| `customers` | `create`, `list`, `retrieve`, `update`, `delete` | [Clientes](https://docs.wyzecash.com.br/endpoints/clientes.md) |
| `balance` | `retrieve` | [Saldo e tarifas](https://docs.wyzecash.com.br/endpoints/saldo.md) |
| `fees` | `list`, `preview` | [Saldo e tarifas](https://docs.wyzecash.com.br/endpoints/saldo.md) |
| `statement` | `list` | [Extrato](https://docs.wyzecash.com.br/endpoints/transacoes.md) |
| `transactions` | `list`, `retrieve` | [Extrato](https://docs.wyzecash.com.br/endpoints/transacoes.md) |
| `search` | `query` | [Extrato](https://docs.wyzecash.com.br/endpoints/transacoes.md) |
| `receipts` | `retrieve` | [Saques](https://docs.wyzecash.com.br/endpoints/saques.md) |
| `withdrawals` | `create`, `list`, `retrieve` | [Saques](https://docs.wyzecash.com.br/endpoints/saques.md) |
| `refunds` | `create`, `list`, `retrieve` | [Cobranças](https://docs.wyzecash.com.br/endpoints/cobrancas.md) |
| `meds` | `list`, `retrieve`, `respond` | [MED](https://docs.wyzecash.com.br/endpoints/med.md) |
| `subaccounts` | `create`, `list`, `retrieve`, `update`, `delete`, `retrieve_settings`, `update_settings`, `preview_settings`, `list_entries`, `fund`, `transfer`, `return_to_account`, `withdraw` | [Subcontas](https://docs.wyzecash.com.br/endpoints/subcontas.md) |

Os códigos vão antes dos campos: `wyzecash.subaccounts.fund("vendedor-42", amount=1000, idempotency_key="envio-81")`.

## Exemplos

> [tab] Pix
>
> ```python theme={null}
> cobranca = wyzecash.payments.create(
>     amount=4990,
>     description="Assinatura de novembro",
>     customerId="0d6c1a7e-...",
>     idempotency_key="assinatura-2026-11-cliente-81",
> )
>
> enviar_para_cliente(cobranca["copyPaste"])  # mostre o QR Code ou o texto para copiar
> ```
>
> O Pix vale 30 minutos (`expiresAt`).

> [tab] Cartão
>
> ```python theme={null}
> cobranca = wyzecash.payments.create(amount=15000, method="card", description="Pedido 1053", idempotency_key="pedido-1053")
>
> redirecionar(cobranca["paymentUrl"])  # página segura de pagamento
> ```
>
> O número do cartão nunca passa pelo seu servidor. A venda aprovada fica "A liberar" até `availableAt`.

> [tab] Cripto
>
> ```python theme={null}
> cobranca = wyzecash.payments.create(amount=20000, method="crypto", cryptoCurrency="sol", idempotency_key="pedido-1054")
>
> print(cobranca["crypto"])  # moeda, valor na moeda e endereço
> ```
>
> A cotação vale 10 minutos.

> [tab] Cliente paga a tarifa
>
> ```python theme={null}
> cobranca = wyzecash.payments.create(amount=10000, feePaidBy="payer", idempotency_key="pedido-1055")
>
> print(cobranca["total"])      # o que o cliente paga (valor + tarifa)
> print(cobranca["netAmount"])  # o que entra para você (o valor inteiro)
> ```

> [tab] Saque
>
> ```python theme={null}
> saque = wyzecash.withdrawals.create(
>     amount=10000,
>     destination={"keyType": "email", "key": "financeiro@exemplo.com"},
>     idempotency_key="saque-2026-11-05",
> )
>
> print(saque["status"])  # "processing": confirme pelo webhook withdrawal.completed
> ```
>
> A chave precisa da permissão `/withdrawals/create`, que pede o 2FA ligado na conta ao criar a chave.

> [tab] Estorno
>
> ```python theme={null}
> estorno = wyzecash.refunds.create(
>     transactionId="BKY-7KQ2M9XP4RZT",
>     amount=5000,
>     reason="Produto devolvido",
>     idempotency_key="estorno-pedido-1052",
> )
> ```
>
> Estorno só no Pix. Sem `amount`, devolve tudo o que ainda não foi devolvido.

## Listas

A lista traz **a primeira página**. O `for` passa por **todas**, buscando a próxima só quando precisa.

```python theme={null}
# Só a primeira página
pagina = wyzecash.payments.list(status="paid", limit=50)
print(len(pagina.items), pagina.next_cursor)

# Todas, uma de cada vez
for cobranca in wyzecash.payments.list(status="paid"):
    print(cobranca["id"], cobranca["amount"])

# Numa lista só (cuidado com contas grandes)
ultimas = wyzecash.statement.list().to_list(limit=200)
```

`pagina.next_page()` busca só a próxima, e `pagina["totals"]` lê os outros campos da resposta (como os totais do extrato).

## Idempotency-Key

Criar cobrança, saque, estorno e os envios das subcontas pedem a `Idempotency-Key`. Passe **o código do pedido no seu sistema**: repetir a mesma chave devolve o mesmo resultado, sem fazer de novo.

```python theme={null}
wyzecash.withdrawals.create(
    amount=10000,
    destination={"keyType": "cpf", "key": "12345678900"},
    idempotency_key="saque-1052",
)
```

> [!TIP]
>
> Sem a chave, o SDK cria uma por chamada: protege a nova tentativa automática, mas não protege você de chamar duas vezes. Em dinheiro saindo, passe sempre a sua.

## Erros e novas tentativas

Todo erro da API vira um `WyzeCashError`:

```python theme={null}
from wyzecash import WyzeCashConnectionError, WyzeCashError

try:
    wyzecash.payments.create(amount=15000, idempotency_key="pedido-1056")
except WyzeCashConnectionError:
    ...  # a resposta não voltou (rede ou tempo esgotado): confira antes de tentar de novo
except WyzeCashError as erro:
    print(erro.status, erro.code, erro.message, erro.request_id)
```

| Campo | O que é |
| - | - |
| `status` | A situação HTTP (`0` quando o pedido nem chegou à API) |
| `code` | Código estável para o seu sistema: `VALIDATION`, `INSUFFICIENT_FUNDS`, `RATE_LIMITED`... (veja [Erros](https://docs.wyzecash.com.br/erros.md)) |
| `message` | O motivo em português |
| `request_id` | O código do pedido: mande para o suporte quando precisar de ajuda |
| `details` | Detalhes, quando a API manda (ex.: qual campo está errado) |

O SDK tenta de novo sozinho só quando é seguro:

| Situação | Tenta de novo? |
| - | - |
| `429` (passou de 100 pedidos por minuto) | Sim, esperando o `Retry-After` |
| Erro de rede ou `5xx` numa consulta ou ao criar cobrança | Sim |
| Saque, estorno e envios das subcontas | **Nunca.** Se a rede cair, confira pela consulta ou pelo webhook antes de pedir de novo |
| `4xx` (dado errado, sem saldo, sem permissão) | Não: corrija o pedido |

## Webhooks

Cada aviso chega com o cabeçalho `X-Bukio-Signature`. Confira **com o corpo cru**, do jeito que chegou, antes de qualquer `json.loads`:

> [tab] FastAPI
>
> ```python theme={null}
> import os
> from fastapi import FastAPI, HTTPException, Request
> from wyzecash import WyzeCashWebhookError, verify_webhook
>
> app = FastAPI()
>
> @app.post("/webhooks/wyzecash")
> async def webhook(request: Request):
>     try:
>         evento = verify_webhook(await request.body(), request.headers.get("x-bukio-signature"), os.environ["WYZECASH_WEBHOOK_SECRET"])
>     except WyzeCashWebhookError as erro:
>         raise HTTPException(400, erro.message)
>     if evento["type"] == "payment.paid":
>         await liberar_pedido(evento["data"])  # use evento["id"] para não processar duas vezes
>     return {"received": True}
> ```

> [tab] Flask
>
> ```python theme={null}
> import os
> from flask import Flask, request
> from wyzecash import WyzeCashWebhookError, verify_webhook
>
> app = Flask(__name__)
>
> @app.post("/webhooks/wyzecash")
> def webhook():
>     try:
>         evento = verify_webhook(request.get_data(), request.headers.get("X-Bukio-Signature"), os.environ["WYZECASH_WEBHOOK_SECRET"])
>     except WyzeCashWebhookError:
>         return "", 400
>     if evento["type"] == "payment.paid":
>         liberar_pedido(evento["data"])
>     return "", 200
> ```

> [tab] Django
>
> ```python theme={null}
> import os
> from django.http import HttpResponse
> from django.views.decorators.csrf import csrf_exempt
> from django.views.decorators.http import require_POST
> from wyzecash import WyzeCashWebhookError, verify_webhook
>
> @csrf_exempt
> @require_POST
> def webhook(request):
>     try:
>         evento = verify_webhook(request.body, request.headers.get("X-Bukio-Signature"), os.environ["WYZECASH_WEBHOOK_SECRET"])
>     except WyzeCashWebhookError:
>         return HttpResponse(status=400)
>     if evento["type"] == "payment.paid":
>         liberar_pedido(evento["data"])
>     return HttpResponse(status=200)
> ```

| Erro (`WyzeCashWebhookError`) | Quando |
| - | - |
| `MISSING_SIGNATURE` | O aviso veio sem o cabeçalho `X-Bukio-Signature` |
| `INVALID_SIGNATURE` | Assinatura errada ou em formato errado |
| `TIMESTAMP_OUT_OF_RANGE` | Assinado há mais de 5 minutos (mude com `tolerance=segundos`) |
| `INVALID_PAYLOAD` | O corpo não é um evento válido |

Responda `2xx` em até 10 segundos e faça o trabalho pesado depois. O segredo `whsec_…` fica em **Integrações e API › Webhooks**. Veja [Validando a assinatura](https://docs.wyzecash.com.br/webhooks/assinatura.md) e a [lista de eventos](https://docs.wyzecash.com.br/webhooks/configurar.md).

## Tempo real

Sem endereço HTTPS para webhooks (bot, worker, script)? Receba os mesmos eventos pelo [WebSocket](https://docs.wyzecash.com.br/webhooks/websocket.md): a página tem um exemplo completo em Python com o pacote `websockets`.

## Onde roda

| Ambiente | Funciona? |
| - | - |
| Python 3.10, 3.11, 3.12 e 3.13 | Sim |
| Servidor, worker, função na nuvem | Sim |
| Navegador ou app | **Não use**: a chave de API ficaria exposta |

## Próximos passos

* [Sua primeira cobrança](https://docs.wyzecash.com.br/inicio-rapido.md) e [Antes de ir para produção](https://docs.wyzecash.com.br/producao.md).
* Todas as rotas, com os campos: [Referência da API](https://docs.wyzecash.com.br/referencia/visao-geral.md).
* Também há SDK oficial para [Node.js](https://docs.wyzecash.com.br/sdk/node.md) e [PHP](https://docs.wyzecash.com.br/sdk/php.md).


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.