Capxul Docs
HooksSub-accounts

useCapxulTransfer

Mutation hook that moves money within one Account — between the main balance and its sub-account envelopes.

Moves money between two balances of the same Account: main balance into an envelope ("Add money"), envelope back to main ("Move money out"), or envelope to envelope. A Transfer never leaves the Account and never reaches another party — it is not a Payment, and nothing settles externally. To send money to someone else, use useCapxulPay. See Money & accounts for the model.

Import

import { useCapxulTransfer } from "@capxul/sdk-react";

Usage

"use client";

import { useCapxulAccountBalance, useCapxulTransfer } from "@capxul/sdk-react";
import type { SubAccountId } from "@capxul/sdk";

function AddMoneyButton({ subAccountId }: { subAccountId: SubAccountId }) {
  const balance = useCapxulAccountBalance();
  const transfer = useCapxulTransfer();
  const account = balance.data;

  if (account === undefined) return null;

  return (
    <button
      type="button"
      disabled={transfer.isPending}
      onClick={() =>
        transfer.mutate({
          accountId: account.id,
          from: "main",
          to: subAccountId,
          // Reuse the Account's currency and precision for the Money amount.
          amount: { ...account.available, value: "25" },
        })
      }
    >
      {transfer.isPending ? "Moving…" : "Add $25 to this envelope"}
    </button>
  );
}

Parameters

The hook itself takes no parameters. The mutation input is a TransferInput plus an accountId:

accountId

AccountId

The Account the transfer happens inside. It is carried only so the hook can invalidate the right cache keys — the backend resolves the Account from the signed-in session, not from this field.

from, to, amount

Prop

Type

from and to are each a TransferEndpoint: either the literal "main" or a SubAccountId. The main balance is the remainder left after every envelope is funded — it is not a stored row. The two endpoints must differ, and amount (a Money) must be positive.

Return type

import { type UseCapxulTransferReturn } from "@capxul/sdk-react";
// UseMutationResult<TransferResult, CapxulError, { accountId: AccountId } & TransferInput>

On success, data is a TransferResult:

Prop

Type

available is the recomputed unassigned money (balance − Σ envelope balances). The from / to slots carry the updated sub-account rows; an endpoint that is "main" has no stored row, so its slot is null.

The hook returns TanStack Query's UseMutationResult. The most-used fields:

FieldDescription
mutateFire the mutation (fire-and-forget; pair with onSuccess/onError callbacks).
mutateAsyncFire the mutation and get a Promise of the result. Rejects with a CapxulError on failure.
dataThe mutation result (typed per hook, shown above). undefined until the first success.
errorA CapxulError when the last attempt failed, otherwise null.
isPendingtrue while the mutation is in flight. Use it to disable submit buttons.
resetClear the mutation state (data, error) back to idle.

Mutations do not retry by default. The full field list is in the TanStack Query useMutation reference; see also the TanStack Query integration guide.

Cache behavior

On success this mutation invalidates the Account's sub-account list (["capxul", "subAccounts", accountId]) and the personal balance (["capxul", "accountBalance"]). Because a Transfer is not a Payment, it does not touch the payments, activity, or insights caches.

Errors

Failures surface as a CapxulError on error / thrown from mutateAsync — for example when from and to are the same endpoint, when the amount is not positive, when the source balance is insufficient (the error reports what was available versus requested), or when the mutation fires while <CapxulProvider> is still bootstrapping.

Client method

This hook wraps client.subAccounts.transfer({ from, to, amount }) on the core SDK.

On this page