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

# Subcontas

> Um saldo separado para cada cliente seu, sem você manter essa contabilidade na mão.

Se você roda uma plataforma (marketplace, SaaS que cobra pelos clientes, rede de
revendedores), uma pergunta aparece rápido: *do dinheiro que entrou, quanto é de quem?*

Subconta é a resposta da API pra isso. Você cria uma subconta por cliente, aponta as
cobranças pra ela, e o saldo de cada um passa a ser mantido pelo gateway: creditado
quando o pagamento confirma, debitado quando você transfere, consultável a qualquer
momento com extrato completo.

## O que uma subconta é (e o que não é)

> [card] É
>
> Um ledger com saldo próprio dentro da **sua** conta.
>
> Criada por API, na hora, sem documento nem aprovação.
>
> Extrato permanente de tudo que entrou e saiu.

> [card] Não é
>
> Uma conta PurinCash: não tem login, painel nem chave de API própria.
>
> Um destino de dinheiro real: o valor continua na carteira da **sua** conta.
>
> Um cadastro com KYC: a relação com o cliente final é sua.

> [!NOTE]
>
> O dinheiro real fica sempre na sua carteira, e o saldo da subconta diz que fatia dele
> pertence a cada cliente. É por isso que criar subconta é instantâneo. E é também por
> isso que um bug seu de atribuição nunca move dinheiro de verdade: a camada é contábil.

## Criando uma subconta

```bash theme={null}
curl -X POST https://api.purin.cash/v1/subaccounts \
  -H "Authorization: Bearer $PURINCASH_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Loja do João",
    "email": "joao@exemplo.com",
    "externalId": "cliente_42",
    "metadata": "{\"plano\":\"pro\"}"
  }'
```

```json Resposta 201 theme={null}
{
  "subaccountId": "sacc_a1b2c3d4e5f60718293a4b5c6d7e8f90",
  "name": "Loja do João",
  "email": "joao@exemplo.com",
  "externalId": "cliente_42",
  "active": true,
  "balanceCents": 0,
  "totalReceivedCents": 0,
  "metadata": "{\"plano\":\"pro\"}",
  "createdAt": "2026-08-04T12:00:00.000Z",
  "updatedAt": "2026-08-04T12:00:00.000Z"
}
```

**name** `string` *required*

Nome do cliente. Até 100 caracteres, contados como o JavaScript conta (unidades
UTF-16): acentos e cedilha valem 1, mas emoji e outros caracteres fora do plano
básico valem 2. Na prática só aparece com nome cheio de emoji.

**externalId** `string`

O ID do cliente **no seu sistema**. Vale usar sempre: ele é único por ambiente, e criar
de novo com o mesmo `externalId` devolve `409` com o `subaccountId` da existente. Ou
seja: a criação vira idempotente, e o retry do seu código nunca duplica cliente.

**email** `string`

Contato do cliente. Informativo, não autentica nada.

**metadata** `string`

String JSON de até 2 KB, devolvida sem alteração. Omitir é o mesmo que mandar
`"{}"`: o campo sempre volta preenchido, nunca ausente nem `null`.

Limite de 1000 subcontas por conta e por ambiente. Guarde o `sacc_...` junto do cadastro
do seu cliente: é ele que você vai usar em todas as outras chamadas.

## Cobrando para uma subconta

Adicione `subaccountId` na criação da cobrança. Funciona em `POST /v1/charges` e em
`POST /v1/payments`, com PIX:

```bash theme={null}
curl -X POST https://api.purin.cash/v1/charges \
  -H "Authorization: Bearer $PURINCASH_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "valueCents": 19900,
    "description": "Pedido #1042 da Loja do João",
    "subaccountId": "sacc_a1b2c3d4e5f60718293a4b5c6d7e8f90",
    "callbackUrl": "https://plataforma.com/webhooks/purincash"
  }'
```

O resto do fluxo é o de [sempre](https://docs.purin.cash/guias/pix.md): o cliente final paga o PIX, o webhook
chega. O passo novo é o seguinte: **o líquido da venda é creditado no ledger da
subconta** no mesmo instante em que entra na sua carteira. O crédito é idempotente:
reenvio de webhook não credita duas vezes.

O webhook `charge.paid` / `payment.paid` vem com o campo `subaccountId` quando a
cobrança tem subconta, então o seu handler sabe de quem é a venda sem consultar nada:

```json theme={null}
{
  "event": "charge.paid",
  "paymentId": "psc_9f8e7d6c5b4a",
  "amountCents": 19900,
  "status": "paid",
  "subaccountId": "sacc_a1b2c3d4e5f60718293a4b5c6d7e8f90",
  "paidAt": "2026-08-04T12:05:00.000Z"
}
```

> [!NOTE]
>
> O valor creditado no ledger é o **líquido**, o mesmo que entrou na sua carteira, já
> sem a taxa do gateway. Assim a soma dos saldos das subcontas nunca promete mais
> dinheiro do que existe.

### Onde `subaccountId` não entra

| Situação | Comportamento |
| - | - |
| Cobrança com `splits` | `400`. Split divide entre contas reais; subconta atribui num ledger. Os dois juntos deixariam ambíguo quem possui o quê |
| `paymentMethod: "ltc"` em `/v1/payments` | `400`. A confirmação de LTC segue um caminho próprio que não alimenta o ledger |
| `paymentMethod` em `/v1/charges` | `400`. Cobrança é PIX; o campo nem existe nela. Pra LTC, use `/v1/payments` |
| Cartão (`/v1/card-payments`) | Campo não existe. Subconta é PIX por enquanto |
| Assinaturas (`/v1/subscriptions`) | `400` se vier preenchido (`null` e vazio são ignorados). PIX recorrente não credita o ledger; aceitar e descartar faria você perder a atribuição sem aviso |
| Saques (`/v1/payouts`) | `400` se vier preenchido (`null` e vazio são ignorados). Esse endpoint não debita ledger de subconta. Pra sacar atribuído, use [`/v1/subaccounts/{id}/payout`](#saque-direto-da-subconta) |
| Subconta desativada | `400` na criação da cobrança |

> [!WARNING]
>
> Em `/v1/payments` e `/v1/charges`, onde o campo **é** aceito, `subaccountId: ""`
> devolve `400` em vez de criar a venda sem dono. É proposital: o padrão
> `subaccountId: cliente.saccId || ""` transformaria um id faltando em dinheiro
> caindo no pool geral sem aviso, e você só descobriria fechando o mês. Pra venda
> sem atribuição, **omita** o campo.

### Reconciliando depois

Cada venda vive na listagem do endpoint que a criou, e as duas aceitam
`?subaccountId=` e devolvem o `subaccountId` de cada item (`null` quando não há
atribuição):

| Você criou com | Reconcilie com |
| - | - |
| `POST /v1/charges` (ids `psc_`) | `GET /v1/charges?subaccountId=sacc_...` |
| `POST /v1/payments` (ids `psa_`) | `GET /v1/payments?subaccountId=sacc_...` |

> [!WARNING]
>
> As duas listas são **separadas**: `/v1/payments` não devolve cobranças `psc_` e
> vice-versa. Se você usa os dois endpoints, feche o mês do cliente somando as
> duas, ou use o [extrato da subconta](#consultando-saldo-e-extrato), que junta
> tudo o que foi creditado, independente da origem.

`?subaccountId=` malformado devolve `400` e um `sacc_` que não existe na sua conta
devolve `404`, nunca uma lista vazia que pareceria "cliente sem vendas".

## Consultando saldo e extrato

```bash theme={null}
curl https://api.purin.cash/v1/subaccounts/sacc_a1b2c3d4.../transactions \
  -H "Authorization: Bearer $PURINCASH_KEY"
```

```json theme={null}
{
  "subaccountId": "sacc_a1b2c3d4e5f60718293a4b5c6d7e8f90",
  "balanceCents": 45900,
  "transactions": [
    { "type": "sale", "amountCents": 19400, "refId": "psc_9f8e7d6c5b4a", "description": "Venda confirmada", "createdAt": "2026-08-04T12:05:00.000Z" },
    { "type": "transfer_out", "amountCents": -30000, "refId": "tr_1a2b3c4d5e6f", "description": "Comissão da plataforma", "createdAt": "2026-08-03T09:00:00.000Z" }
  ],
  "total": 2,
  "limit": 50,
  "offset": 0
}
```

Os `amountCents` vêm com sinal: crédito positivo, débito negativo. Somar a coluna
reproduz o saldo. Cada lançamento tem um `id` próprio (estável, serve pra dedupe) e
o `refId` amarra à origem: `psc_`/`psa_` é uma venda, `tr_`/`idem_` é transferência,
`saq_` é saque feito pelo painel e `adj_` é correção contábil do suporte. O extrato
aceita `?from=` e `?to=` (ISO 8601) pra fechar o mês de um cliente sem baixar tudo,
e `?type=` com qualquer um dos tipos: `sale`, `transfer_in`, `transfer_out`,
`payout`, `payout_reversal`, `adjust`.

`GET /v1/subaccounts` lista todas com saldo, e aceita `?externalId=` pra achar um
cliente pelo **seu** ID sem guardar o `sacc_` do lado de lá.

O `GET` de uma subconta traz também `medBlockedCents` e `availableCents`: MED
(disputa) atribuído a uma venda daquela subconta **bloqueia** o valor nela: não
debita, espelhando a carteira mãe. O `availableCents` é o saldo menos esse bloqueio,
e é ele que limita repasse enquanto a disputa vive.

## Transferindo saldo

`POST /v1/subaccounts/{id}/transfer` move valor entre a subconta e a sua conta, nos
dois sentidos. É uma operação contábil, nenhum PIX é disparado:

```bash Cobrar sua comissão theme={null}
curl -X POST https://api.purin.cash/v1/subaccounts/sacc_a1b2c3d4.../transfer \
  -H "Authorization: Bearer $PURINCASH_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "direction": "to_master",
    "amountCents": 30000,
    "description": "Comissão da plataforma de julho"
  }'
```

```bash Dar bônus ou estorno theme={null}
curl -X POST https://api.purin.cash/v1/subaccounts/sacc_a1b2c3d4.../transfer \
  -H "Authorization: Bearer $PURINCASH_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "direction": "to_subaccount",
    "amountCents": 5000,
    "description": "Crédito de boas-vindas"
  }'
```

`to_master` exige saldo suficiente na subconta, e a checagem é atômica: duas
transferências concorrentes nunca deixam o saldo negativo. A `description` aparece no
extrato. Capriche nela: é o que o seu financeiro vai ler depois.

`to_subaccount` exige **lastro**: o valor precisa caber no que a sua carteira tem de
sacável e ainda não pertence a nenhuma subconta (o `unattributedCents` do
[`GET /v1/wallet`](https://docs.purin.cash/guias/carteira)). Aporte atribui dinheiro que existe: não cria.
Sem lastro, `400 Insufficient unattributed master balance` com o disponível no corpo.
Se duas transferências concorrentes disputarem o mesmo lastro, a que estourar o
limite volta atrás com `409`: repita a chamada.

### Direto de uma subconta para outra

Para mover saldo entre duas subcontas sem passar pela conta principal, use
`between_subaccounts` e informe o destino em `toSubaccountId`:

```bash theme={null}
curl -X POST https://api.purin.cash/v1/subaccounts/sacc_a1b2c3d4.../transfer \
  -H "Authorization: Bearer $PURINCASH_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "direction": "between_subaccounts",
    "toSubaccountId": "sacc_f0e1d2c3...",
    "amountCents": 12000,
    "description": "Repasse entre parceiros"
  }'
```

A subconta do caminho é a origem. As duas precisam ser da mesma conta, e o destino
precisa estar ativo. A resposta traz `balanceCents` (origem) e `toBalanceCents`
(destino), e o extrato ganha um lançamento em cada uma, com o mesmo `transferId`.
Como o total atribuído às subcontas não muda, essa transferência não depende de
lastro. O valor retido por disputa na origem não pode ser movido.

### Repetindo sem duplicar

Mande `idempotencyKey` **no corpo** (não é header) e o retry de rede deixa de ser um
risco: a repetição devolve `200` com a transferência original e `idempotentReplay:
true`, em vez de mover o valor de novo.

```bash theme={null}
curl -X POST https://api.purin.cash/v1/subaccounts/sacc_a1b2c3d4.../transfer \
  -H "Authorization: Bearer $PURINCASH_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "direction": "to_master",
    "amountCents": 30000,
    "idempotencyKey": "fechamento-2026-07-cliente-42"
  }'
```

A chave aceita `[A-Za-z0-9_-]`, até 64 caracteres, e é escopada à subconta. Ela vira
o `refId` do lançamento com o prefixo `idem_`, e é por isso que o extrato mostra
`idem_fechamento-2026-07-cliente-42` no lugar de um `tr_` aleatório. Sem a chave,
cada requisição é uma transferência nova.

### As regras do `amountCents`

`amountCents` é **número JSON inteiro**, de 1 a 100.000.000.000 (1 bilhão de reais).
Fora disso é `400`, sem conserto silencioso:

| Enviado | Resposta |
| - | - |
| `"1000"` (string) | `400`: número JSON, não string |
| `10.5` | `400`: inteiro, nada de arredondar por você |
| `1e21` ou acima do teto | `400`: acima do teto é dedo no teclado, não pagamento |
| `0` ou negativo | `400` |

O mesmo vale para os campos de texto: `name` acima de 100, `externalId` acima de 200,
`description` acima de 200 e `metadata` acima de 2048 caracteres são `400`: nada é
truncado em silêncio. `metadata` precisa **ser** string (serialize com
`JSON.stringify` antes); `active` precisa ser boolean de verdade (`"false"` entre
aspas é `400`). `externalId` é imutável (o `PUT` recusa) e **case-sensitive**:
`cli_1` e `CLI_1` são clientes diferentes.

## Pagando o cliente de verdade

Duas formas. A direta é um `POST` só; a de dois passos continua valendo e é a que
funciona também em sandbox.

### Saque direto da subconta

`POST /v1/subaccounts/{id}/payout` debita o ledger do cliente e manda o PIX na mesma
operação.

```json theme={null}
{
  "amountCents": 15000,
  "pixKey": "cliente@exemplo.com",
  "idempotencyKey": "repasse-2026-09-01-cli42"
}
```

| Campo | |
| - | - |
| `amountCents` | Inteiro, em centavos. `15000` é cento e cinquenta reais |
| `pixKey` | A chave PIX **do seu cliente** |
| `idempotencyKey` | Opcional, e é o que impede o retry de pagar duas vezes. Use sempre |

Se a subconta tiver [markup de saque](#markup-no-saque), o `amountCents` continua
sendo o que é debitado dela por inteiro, e o saque nasce já sem a sua parte.

> [!WARNING]
>
> **Este endpoint precisa do escopo `subcontas.sacar`, e ele nunca é concedido
> implicitamente.** Chave sem lista de escopos, que por compatibilidade pode todo o
> resto, recebe `403` aqui até alguém marcar a caixinha no painel. É proposital: ligar
> um poder novo de mandar dinheiro pra fora não pode dar esse poder, de graça, a todas
> as chaves que você já emitiu. `subcontas.gerenciar` também não abre este, porque
> aquele mexe em atribuição e este mexe em dinheiro.

Resposta `200` (ou `202`, quando o banco ainda não confirmou), no mesmo formato do saque
turbo. Chamar de novo com a mesma `idempotencyKey` devolve o saque original com
`idempotentReplay: true`, sem pagar nada. Se aquele saque tiver terminado em estorno, a
resposta é `409` com `reversed: true`: nada saiu, e a instrução é repetir com uma chave
nova, para você não marcar como pago um repasse que não aconteceu.

Live-only. Chave de sandbox recebe `400`, porque subconta de teste não tem lastro.

Recusa também deixa rastro: a tentativa fica registrada com status `falhou` e o motivo,
e aparece em [`GET /v1/payouts`](https://docs.purin.cash/guias/saques) junto com os saques normais. O dinheiro
volta pro ledger da subconta no mesmo instante, com um lançamento de estorno que cita o
motivo. Isso existe pra que um valor que sumiu e voltou no extrato sempre tenha uma
contrapartida procurável, em vez de virar um par de linhas sem explicação.

### Consultando o saque

`GET /v1/subaccounts/{id}/payouts/{payoutId}` diz como o saque terminou. O `{payoutId}`
pode ser qualquer um dos identificadores que você tem do saque, e todos devolvem a
mesma resposta:

| Você tem | De onde ele vem |
| - | - |
| `withdrawal.id` ou `code` (`TURBO-...`) | Da resposta do saque. O `code` também vem no `202` e no webhook `subaccount.payout` |
| `code` `SAQ-API-...` | Da resposta `202` de um saque que entrou na fila de conferência (`turboReason` diz por quê). O `withdrawal.id` também vem nesse `202` |
| `saq_idem_` + a sua `idempotencyKey` | De você mesmo: é o caminho de quem tomou um timeout e ficou sem resposta |
| `saq_...` | Do `refId` do lançamento `payout`, no extrato |
| `SUB-...` | Do lançamento de estorno de um saque recusado antes de chegar ao banco |

```json theme={null}
{
  "payoutId": "saq_idem_repasse-2026-09-01-cli42",
  "withdrawalId": "68c0a1b2c3d4e5f60718293a",
  "code": "TURBO-7A1F2C9D",
  "subaccountId": "sacc_3f1e9a0b2c4d6e8f0a1b3c5d7e9f1a2b",
  "status": "completed",
  "reversed": false,
  "amountCents": 15000,
  "feeCents": 0,
  "platformFeeCents": 100,
  "netAmountCents": 14900,
  "pixKey": "client***",
  "endToEndId": "E12345678202609011200abcdef01234",
  "recipientName": "Cliente Exemplo",
  "recipientBankName": "Banco Exemplo",
  "failureReason": null,
  "createdAt": "2026-09-01T12:00:00.000Z",
  "completedAt": "2026-09-01T12:00:02.000Z",
  "reversedAt": null
}
```

| `status` | O que significa | O que fazer |
| - | - | - |
| `completed` | O PIX saiu e o banco confirmou | Nada |
| `processing` | Ainda não terminou, inclusive o saque em verificação do `202` | **Não repita o saque**: o dinheiro pode ter saído. Consulte de novo mais tarde |
| `failed` | Nada foi enviado. `failureReason` diz o motivo | Com `reversed: true` o valor já voltou pra subconta e dá pra sacar de novo, com uma `idempotencyKey` nova. Com `reversed: false`, fale com o suporte |

`amountCents` é o que saiu da subconta (o mesmo valor do pedido), `feeCents` é o
[markup de saque](#markup-no-saque) dela, `platformFeeCents` é a taxa do saque e
`netAmountCents` é o que a chave PIX recebe. A chave volta mascarada.

Consultar precisa de `subcontas.gerenciar` **ou** `subcontas.sacar`: a chave que só
faz repasses consegue acompanhar os repasses que dispara.

### Ou em dois passos

**1. Traga o saldo pra conta principal**

`POST /v1/subaccounts/{id}/transfer` com `direction: "to_master"` e o valor do
repasse. Isso debita o ledger do cliente e documenta o repasse no extrato dele.

**2. Saque com a chave PIX do cliente**

[`POST /v1/payouts`](https://docs.purin.cash/guias/saques) com `method: "pix"` e o `walletAddress` sendo a
chave PIX **do cliente**. Repare que aqui o valor é `amount`, em reais, e não em
centavos.

> [!NOTE]
>
> O que os dois caminhos têm em comum, e é o que importa: o dinheiro sai pelo mesmo
> fluxo de saque de sempre (chave verificada, limites, MED e aprovação). No caminho
> direto, o débito no ledger acontece antes de qualquer dinheiro andar e é condicionado
> ao saldo da subconta menos o que estiver retido por MED dela. Se o banco negar depois,
> o valor volta pro ledger da subconta de origem, e não pro caixa geral.
>
> O mesmo saque existe no painel, em Subcontas na aba Sacar.

> [!WARNING]
>
> `POST /v1/payouts` recusa `subaccountId` com `400` de propósito. Ali não há baixa no
> ledger: o dinheiro sairia da carteira e o extrato da subconta continuaria dizendo que
> o valor está lá. Pra sacar atribuído, use o endpoint desta seção.

> [!WARNING]
>
> **O saque da conta principal não alcança o que é das subcontas.** O sacável
> (`withdrawable` do `GET /v1/wallet`) desconta a soma dos saldos de todas as
> subcontas: se o dinheiro está atribuído a um cliente, ele sai pela subconta, não
> por cima dela. Saque acima do sacável leva `400` com o motivo detalhado. Pra
> liberar um valor, traga de volta com `to_master` primeiro.

## Taxa da plataforma (markup)

Cada subconta pode ter um markup: percentual (`feePercent`, 0 a 100 com até 2 casas)
e/ou fixo em centavos (`feeFixedCents`). A cada venda confirmada, o markup sai do
líquido ANTES do crédito: a subconta recebe `liquido - markup`, e a sua fatia fica na
conta principal sem nenhum movimento extra: você não precisa mais varrer as vendas e
cobrar comissão com `to_master` uma a uma.

```bash theme={null}
curl -X POST https://api.purin.cash/v1/subaccounts \
  -H "Authorization: Bearer $PURINCASH_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Loja do João",
    "externalId": "cliente_42",
    "feePercent": 5,
    "feeFixedCents": 100
  }'
```

Com essa configuração, uma venda que rendeu R\$ 100,00 líquidos credita R\$ 94,00 na
subconta (5% + R\$ 1,00 ficam com você). O lançamento `sale` do extrato grava o
`feeCents` cobrado, então a reconciliação fecha: `liquido = amountCents + feeCents`.

Regras que valem a pena saber:

* O percentual arredonda **pra baixo** (na dúvida, o centavo é do cliente), e o
  markup nunca passa do líquido: taxa fixa maior que a venda credita zero, não gera
  dívida.
* Mudar a taxa vale só pra vendas FUTURAS: cada lançamento carrega o `feeCents` da
  época, nada é reescrito.
* Transferências não têm markup. Saques têm o seu próprio, logo abaixo.

### Markup no saque

O par acima só vale na venda. Pra cobrar também quando o cliente **saca**, existem
dois campos separados: `payoutFeePercent` (0 a 100, até 2 casas) e
`payoutFeeFixedCents`.

São campos próprios de propósito. Reaproveitar os de venda faria toda subconta que
já cobra pra receber passar a cobrar o mesmo pra sacar, sem ninguém ter pedido: quem
cobra 5% de comissão não necessariamente quer cobrar 5% de repasse. O padrão é zero
nos dois, e zero é exatamente o comportamento de quem nunca configurou nada.

```bash theme={null}
curl -X PUT https://api.purin.cash/v1/subaccounts/sacc_a1b2c3d4... \
  -H "Authorization: Bearer $PURINCASH_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "payoutFeePercent": 1,
    "payoutFeeFixedCents": 200
  }'
```

Com essa configuração, um saque de `amountCents: 15000` nessa subconta:

| | |
| - | - |
| Debitado da subconta | R\$ 150,00, o valor **cheio** que você pediu |
| Seu markup | R\$ 3,50 (1% + R\$ 2,00) |
| Vira saque de | R\$ 146,50, e é daí que sai a taxa da plataforma |

> [!NOTE]
>
> O dinheiro não se move em lugar nenhum. A subconta é debitada o cheio e o saque
> nasce pelo líquido, então o seu markup simplesmente **fica** na carteira da conta
> principal: sem crédito extra, sem lançamento paralelo, sem uma segunda escrita que
> possa falhar no meio de um saque.

Onde ler o valor cobrado: no extrato da subconta, o lançamento `payout` grava o
`feeCents` do markup ao lado do débito cheio. Vale reparar que a resposta do saque
**já vem líquida do seu markup**: o `amount` dela é o valor que virou saque
(R\$ 146,50 no exemplo) e o `fee` é a taxa da plataforma, não a sua.

* Markup que consome o valor inteiro é `400`, com a taxa citada na mensagem. Saque
  que só cobra taxa é cobrança disfarçada, não saque.
* Se o banco recusar depois, o estorno devolve à subconta o valor **cheio**, markup
  incluído: você não fica com a taxa de um saque que não aconteceu.
* Mudar a taxa vale só pra saques FUTUROS, igual à de venda.
* Vale nos dois caminhos: pelo endpoint e pelo painel, em Subcontas na aba Sacar. É
  o mesmo motor.

> [!NOTE]
>
> No painel as duas taxas ficam no mesmo lugar: ao criar a subconta, e depois em
> **Suas taxas neste cliente**, dentro do cliente. Só o dono da conta edita; membro
> de equipe vê e não mexe.

## Webhooks de movimentação

Todo movimento do ledger que não nasce de uma cobrança avisa a
[URLs da conta](https://docs.purin.cash/webhooks/visao-geral.md): `subaccount.transfer` (API **e painel**, com
`origin` dizendo quem foi), `subaccount.payout` e `subaccount.payout_reversal`.
A venda continua avisando pelo `charge.paid`/`payment.paid` com `subaccountId`.
Formato completo em [Webhooks → Subcontas](https://docs.purin.cash/webhooks/subcontas.md).

## Desativando

`PUT /v1/subaccounts/{id}` com `active: false`. A subconta some das novas cobranças e
não aceita mais entradas **novas**, mas o saldo continua consultável e o `to_master`
continua funcionando. Desativar não prende dinheiro. Excluir não existe: o extrato é
histórico financeiro, e histórico financeiro não se apaga.

> [!NOTE]
>
> Cobrança criada **antes** da desativação continua creditando a subconta quando for
> paga. A desativação fecha a porta pra cobranças novas; o que já estava emitido com o
> `subaccountId` daquele cliente pertence a ele: desviar esse crédito pro pool geral é
> que seria erro contábil.

## Em sandbox

Tudo funciona com chave `ps_test_`, com o mesmo isolamento do resto da API: subcontas
de teste são separadas das reais. O `simulate-paid` credita o ledger aplicando o
**mesmo markup** configurado na subconta (`feePercent`/`feeFixedCents`), igual à
produção: o lançamento `sale` sai com o `feeCents` cobrado, então dá pra testar o ciclo
inteiro, taxa incluída, sem mover um centavo: criar subconta, cobrar, simular
pagamento, ver o saldo e o markup, transferir.

> [card] [Referência completa](https://docs.purin.cash/api-reference/introducao.md)
>
> Os 6 endpoints de subcontas com playground, na seção Subcontas da Referência.


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