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

# submitFlowTransaction

> Prepare, sign, and report a quoted Flow wallet payment in one call with the JavaScript SDK, and handle a declined or interrupted payment.

Sends a quoted wallet payment in one call. It runs these steps:

1. Calls [`prepareFlowSigning`](https://docs.dynamic.xyz/docs/javascript/reference/client/prepare-flow-signing), which checks the quote and the wallet balance and moves the flow to `signing`.
2. Switches the wallet to the network the transaction needs.
3. Asks the payer to approve a token allowance, when an ERC-20 payment needs one, and then to sign and send the payment transaction.
4. Reports the transaction hash with [`broadcastFlow`](https://docs.dynamic.xyz/docs/javascript/reference/client/broadcast-flow), which moves the flow to `broadcasted`.

## When to call

Call `submitFlowTransaction` when `executionState` is `quoted` and the source is a wallet, after the payer confirms the quote. Do not call it for deposit-address or exchange sources, which the payer pays outside your app.

## Usage

```javascript theme={"system"}
import { submitFlowTransaction } from '@dynamic-labs-sdk/client';

const flow = await submitFlowTransaction({
  flowId: 'your-flow-id',
  walletAccount,
});
```

## Parameters

| Parameter | Type | Description |
| - | - | - |
| `flowId` | `string` | The flow ID. |
| `walletAccount` | `WalletAccount` | The wallet account that was attached as the source. The wallet must be on EVM, Solana, Bitcoin, Sui, or TRON. |
| `onStepChange` | `(step: 'approval' \| 'transaction') => void` (optional) | Called when the wallet prompt changes: `approval` for a token allowance, and `transaction` for the payment. |
| `assertBalanceForGasCost` | `boolean` (optional) | Reject with `insufficient_balance` when the wallet cannot cover the gas. Default `true`. |
| `assertBalanceForTransferAmount` | `boolean` (optional) | Reject with `insufficient_balance` when the wallet cannot cover the amount. Default `true`. |
| `amount` | `string` (optional) | `deposit` mode only: the amount the payer chose. The SDK uses it when it gets a new quote after the payer declines. |
| `amountCurrency` | `FlowQuoteAmountCurrency` (optional) | The currency of `amount`: `'USD'`, the default, or `'SOURCE_TOKEN'`. |

In `deposit` mode, pass the same `amount` and `amountCurrency` you passed to `getFlowQuote`. Without them, the new quote after a decline prices the flow's current amount in USD.

## Returns

`Promise<Flow>`: usually the flow in `broadcasted` with `txHash` set.

When the wallet accepted the transaction but did not return its hash in time, `submitFlowTransaction` skips the report and returns the flow as it is, still in `signing`. The transaction may still be on its way. Treat any successful return as sent: show the payment as processing and keep reading the flow with [`getFlow`](https://docs.dynamic.xyz/docs/javascript/reference/client/get-flow).

## Examples

### Show the wallet prompt

```javascript theme={"system"}
import { submitFlowTransaction } from '@dynamic-labs-sdk/client';

const flow = await submitFlowTransaction({
  flowId: 'your-flow-id',
  walletAccount,
  onStepChange: (step) => {
    // 'approval': approve the token allowance
    // 'transaction': sign and send the payment
    showWalletPrompt(step);
  },
});
```

### Handle a declined or interrupted payment

```javascript theme={"system"}
import {
  FlowPrepareSigningError,
  getFlow,
  submitFlowTransaction,
  UserRejectedError,
} from '@dynamic-labs-sdk/client';

try {
  await submitFlowTransaction({ flowId: 'your-flow-id', walletAccount });
} catch (error) {
  if (error instanceof UserRejectedError) {
    // Nothing was sent, and the flow has a new quote. Let the payer try again.
  } else if (error instanceof FlowPrepareSigningError && error.code === 'quote_expired') {
    // Nothing was sent. Get a new quote, then let the payer confirm again.
  } else {
    // The payment may already be on its way. Read the flow instead of retrying.
    const flow = await getFlow({ flowId: 'your-flow-id' });
    showScreenFor(flow);
  }
}
```

After a decline, do not cancel the flow. The payer can submit again or choose another way to pay on the same flow.

## Errors

| Error | When | Retry |
| - | - | - |
| `UserRejectedError` | The payer declined a wallet prompt. Nothing was sent, and the SDK requested a new quote with the same token and chain before it threw. | Yes, submit again |
| `FlowPrepareSigningError` | `quote_expired`: the quote expired. `insufficient_balance`: the wallet cannot cover the amount or the gas. Nothing was sent. | After a new quote, for `quote_expired` |
| `FlowStateConflictError` | The flow no longer accepts the payment, the transaction hash is already recorded, or another request changed the flow first. | No. Read the flow |
| `FlowSessionTokenMissingError` | No session token for the flow is stored on this device. | No. Read the flow |
| `FlowSessionExpiredError` | The server no longer accepts the stored session token. | No. Read the flow |
| `InvalidParamError` | The wallet's chain cannot sign Flow payments. | No |

After any error other than `UserRejectedError` or `quote_expired`, the payment may already be on its way. Do not call `submitFlowTransaction` again. Call `getFlow` and show the screen for its state: a flow still in `quoted` was not sent, and a flow in `signing` or later is processing. [Handle Flow errors](https://docs.dynamic.xyz/docs/javascript/reference/flow-errors.md#when-to-retry) explains the retry rules.

## React

`useSubmitFlowTransaction` is a mutation hook. It takes the same parameters, and it writes the returned flow into the `useGetFlow` cache.

```tsx theme={"system"}
import { useSubmitFlowTransaction } from '@dynamic-labs-sdk/react-hooks';

function ConfirmPayment({ flowId, walletAccount }) {
  const { mutate: submitFlowTransaction, isPending, isSuccess } = useSubmitFlowTransaction();

  return (
    <button
      onClick={() => submitFlowTransaction({ flowId, walletAccount })}
      disabled={isPending || isSuccess}
    >
      {isPending ? 'Confirm in your wallet...' : 'Confirm payment'}
    </button>
  );
}
```

## Related

* [Build a custom payment UI](https://docs.dynamic.xyz/docs/javascript/reference/flow-ui-guide.md#send-the-payment): the review and confirm screens.
* [Handle Flow errors](https://docs.dynamic.xyz/docs/javascript/reference/flow-errors.md): every error and the retry rules.
* [`prepareFlowSigning`](https://docs.dynamic.xyz/docs/javascript/reference/client/prepare-flow-signing): sign and report the transaction yourself.


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