# Transfers between accounts (/en/docs/conta-digital/internal-transfers)

<QuickLinks>
  <QuickLink href="/docs/conta-digital/endpoints/internal-transfers/post_internal_transfer" title="Transfer to another PayZu account" method="POST" path="/transactions/internal-transfer" />

  <QuickLink href="/docs/conta-digital/endpoints/internal-transfers/get_internal_transfers" title="List transfers" method="GET" path="/transactions/internal-transfer" />
</QuickLinks>

The money does not go through Pix: the destination is always another PayZu account, identified by its Pix key. For any other destination, use a [withdrawal](/docs/conta-digital/withdrawals).

<Mermaid
  chart="`
flowchart LR
  A[&#x22;Request the transfer&#x22;] --> B{&#x22;status in the response&#x22;}
  B -->|&#x22;CONFIRMED&#x22;| C[&#x22;Amount in the other account&#x22;]
  B -->|&#x22;FAILED&#x22;| D[&#x22;Nothing left the account&#x22;]

  click A &#x22;/docs/conta-digital/endpoints/internal-transfers/post_internal_transfer&#x22; &#x22;Transfer to another PayZu account&#x22;

  style A fill:#f59e0b,stroke:#d97706,color:#ffffff
  style C fill:#14ce71,stroke:#0eb464,color:#ffffff
  style D fill:#ef4444,stroke:#dc2626,color:#ffffff
`"
/>

<Steps>
  <Step>
    ### Request the transfer [#request-the-transfer]

    [`POST /transactions/internal-transfer`](/docs/conta-digital/endpoints/internal-transfers/post_internal_transfer), scope `INTERNAL_TRANSFER`. `WITHDRAW` does not give access to this route: the credential needs `INTERNAL_TRANSFER`. Required: `amount`, in cents, and `toPixKey`, the Pix key of the other PayZu account.

    Generate an `Idempotency-Key` for each transfer and, if you repeat the call, send the same one.

    <Tabs items="['curl', 'Node.js']">
      <Tab value="curl">
        ```bash
        IDEMPOTENCY_KEY=$(uuidgen)

        curl -X POST https://api.hub.payzu.com.br/api/v1/transactions/internal-transfer \
          -H "Authorization: Bearer $PAYZU_TOKEN" \
          -H "Idempotency-Key: $IDEMPOTENCY_KEY" \
          -H "Content-Type: application/json" \
          -d '{
            "amount": 10000,
            "toPixKey": "financeiro@lojaparceira.com.br",
            "comment": "Repasse do mês"
          }'
        ```
      </Tab>

      <Tab value="Node.js">
        ```ts
        import crypto from 'node:crypto';

        const idempotencyKey = crypto.randomUUID();

        const res = await fetch('https://api.hub.payzu.com.br/api/v1/transactions/internal-transfer', {
          method: 'POST',
          headers: {
            Authorization: `Bearer ${process.env.PAYZU_TOKEN}`,
            'Idempotency-Key': idempotencyKey,
            'Content-Type': 'application/json',
          },
          body: JSON.stringify({
            amount: 10000,
            toPixKey: 'financeiro@lojaparceira.com.br',
            comment: 'Repasse do mês',
          }),
        });
        const transferencia = await res.json();
        ```
      </Tab>
    </Tabs>

    `comment` appears on the receipt. `callbackUrl` receives the webhooks for your side and requires the [callback secret](/docs/conta-digital/webhooks#operation-callbackurl). All fields are in [Transfer to another PayZu account](/docs/conta-digital/endpoints/internal-transfers/post_internal_transfer).
  </Step>

  <Step>
    ### Read the result [#read-the-result]

    The response (`201`) carries the result. With `CONFIRMED`, the amount is already in the other account: you do not need to wait for the `INTERNAL_TRANSFER_SENT` webhook.

    ```json
    {
      "id": "hubp-20261005L9C3VH6KMA127431",
      "status": "CONFIRMED",
      "side": "SENT",
      "amount": 10000,
      "serviceFee": 100,
      "totalDebited": 10100,
      "counterparty": {
        "name": "Loja Parceira Ltda",
        "document": "12.345.678/0001-95",
        "pixKey": "financeiro@lojaparceira.com.br"
      },
      "comment": "Repasse do mês",
      "providerRejectedReason": null,
      "callbackUrl": null,
      "createdAt": "2026-10-05T15:02:44.010Z",
      "confirmedAt": "2026-10-05T15:02:44.418Z",
      "failedAt": null
    }
    ```

    The fee is added on top: the other account receives the full `amount`, and `totalDebited` leaves yours. With `FAILED`, nothing left the account, and `providerRejectedReason` carries a fixed message for display.
  </Step>

  <Step>
    ### Download the receipt [#download-the-receipt]

    Optional. [`GET /transactions/internal-transfer/{transferId}/receipt`](/docs/conta-digital/endpoints/internal-transfers/get_internal_transfer_receipt) returns the PDF in base64, for a `CONFIRMED` transfer. It is not a Pix receipt and has no end-to-end ID: the transfer is identified by its `id`.
  </Step>
</Steps>

## Repeated request [#repeated-request]

The `Idempotency-Key` follows the [withdrawal](/docs/conta-digital/withdrawals#repeated-request) rules, and the comparison uses amount and destination. The same key with another amount or destination is refused with `409` `TRANSFER_IDEMPOTENCY_KEY_REUSED`.

On a `502` where the bank did not respond (`PROVIDER_UNAVAILABLE`) or refused temporarily (`PROVIDER_REFUSED` with `details.status` `408` or `429`), the transfer may have gone out. It stays `REQUESTED`, with no result, and the amount stays out of the available balance:

* The API does not resolve it on its own: no webhook or lookup gives the result earlier. PayZu checks with the bank, and the transfer moves to `CONFIRMED` or `FAILED`.
* Repeating with the same `Idempotency-Key` returns the original, still `REQUESTED`, without sending another one. A new key creates another transfer.
* Until the result is checked, it counts toward that day's daily cap.

A final refusal from the bank leaves the transfer `FAILED` and returns the amount.

## Both sides [#both-sides]

The same transfer appears in both accounts, and `side` says which side:

| Field                           | `side: "SENT"`                                                    | `side: "RECEIVED"`           |
| ------------------------------- | ----------------------------------------------------------------- | ---------------------------- |
| `amount`                        | What went out to the destination                                  | What came in                 |
| `serviceFee` and `totalDebited` | Fee and total debited                                             | `0`                          |
| `counterparty`                  | Who received                                                      | Who sent                     |
| `counterparty.pixKey`           | Full in the `POST` response; masked in the lookup and in the list | Masked                       |
| `callbackUrl`                   | The URL sent                                                      | `null`                       |
| Webhook                         | `INTERNAL_TRANSFER_SENT`                                          | `INTERNAL_TRANSFER_RECEIVED` |

Only a confirmed transfer generates a webhook.

## Look up [#look-up]

* [`GET /transactions/internal-transfer/{transferId}`](/docs/conta-digital/endpoints/internal-transfers/get_internal_transfer) and [`GET /transactions/internal-transfer`](/docs/conta-digital/endpoints/internal-transfers/get_internal_transfers), scope `INTERNAL_TRANSFER_READ`. The list includes both sides; `?side=SENT` or `?side=RECEIVED` filters one. It also filters by `status`, `dateFrom` and `dateTo`.
* A transfer from another account, or one that does not exist, responds `404` `TRANSFER_NOT_FOUND`.

## Limits [#limits]

* Insufficient balance refuses the whole transfer, and the check uses `totalDebited`. `TRANSFER_INSUFFICIENT_BALANCE` carries `details.available` and `details.required`.
* The amount must be between the account's minimum and maximum transfer, in `internalTransfer` in the [limits](/docs/conta-digital/statement#limits).
* The daily cap is its own, separate from withdrawals: `dailyInternalTransfer`. `0` blocks every transfer and `limit: null` means no cap. Going over it responds `422` `TRANSFER_DAILY_LIMIT`.
* Up to 5 requests per minute per credential and 10 per account. Above that, the API responds `429` with `Retry-After`.

## Rejections [#rejections]

The ones that call for another destination or a change on the account:

| Status | `code`                              | When                                                                                                             |
| ------ | ----------------------------------- | ---------------------------------------------------------------------------------------------------------------- |
| 404    | `TRANSFER_DESTINATION`              | No PayZu account has this key active. Use a withdrawal.                                                          |
| 422    | `TRANSFER_DIFFERENT_PROVIDER`       | The destination account operates at another bank. Use a withdrawal.                                              |
| 422    | `TRANSFER_SAME_ACCOUNT`             | The key belongs to your own account.                                                                             |
| 422    | `TRANSFER_AMBIGUOUS_DESTINATION`    | The key is active in more than one account.                                                                      |
| 422    | `TRANSFER_DESTINATION_NOT_ACTIVE`   | The destination account is not active.                                                                           |
| 422    | `TRANSFER_MAIN_ACCOUNT_DESTINATION` | The key belongs to an account that does not receive transfers.                                                   |
| 422    | `TRANSFER_NO_ORIGIN_KEY`            | Your account has no active Pix key. See [Pix keys](/docs/conta-digital/pix-keys).                                |
| 422    | `ACCOUNT_BLOCKED_BY_PROVIDER`       | The bank blocked outflows on your account or inflows on the destination account. `details.operation` says which. |
| 422    | `ACCOUNT_HELD_BY_STAFF`             | Your account or the destination account is held by support.                                                      |

All rejections, with the `code`, are in [Transfer to another PayZu account](/docs/conta-digital/endpoints/internal-transfers/post_internal_transfer), and credential and scope rejections are in [Authentication](/docs/conta-digital/authentication#credential-rejections).