> ## 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.

# cancelFlow

> Cancel a Flow payment from the payer's page with the JavaScript SDK before the payment is sent.

Cancels a flow before the payment is sent. A cancelled flow is final: the payer cannot resume it, and a new attempt needs a new flow from your server. Cancelling also removes the flow's session token from the device.

## When to call

Call `cancelFlow` when `executionState` is `source_attached`, `quoted`, or `signing`. Cancelling needs the session token that attaching a source stores, so it works only on the device that attached the source.

* A flow in `initiated` has no session token yet. Your server can cancel it with its API token, as described in the [Flow payments API guide](https://docs.dynamic.xyz/docs/flow/api.md#cancel-a-flow).
* Nobody can cancel a flow once the payment is sent. Hide the cancel button as soon as the payer approves a wallet payment.

Do not cancel after the payer declines in the wallet. The flow already has a new quote, so the payer can try again or choose another way to pay on the same flow.

## Usage

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

const flow = await cancelFlow({ flowId: 'your-flow-id' });

console.log(flow.executionState); // 'cancelled'
```

## Parameters

| Parameter | Type | Description |
| - | - | - |
| `flowId` | `string` | The flow ID to cancel. |

## Returns

`Promise<Flow>`: the flow with `executionState: 'cancelled'`.

## Examples

### Show the state when cancelling is too late

```javascript theme={"system"}
import {
  cancelFlow,
  FlowSessionExpiredError,
  FlowStateConflictError,
  getFlow,
} from '@dynamic-labs-sdk/client';

const cancelPayment = async (flowId) => {
  try {
    return await cancelFlow({ flowId });
  } catch (error) {
    if (error instanceof FlowStateConflictError || error instanceof FlowSessionExpiredError) {
      // The payment was sent or the flow already ended. Show its real state.
      return getFlow({ flowId });
    }
    throw error;
  }
};
```

## Errors

| Error | When |
| - | - |
| `FlowStateConflictError` | The flow can no longer be cancelled, for example because the payment was sent, or another request changed it first. |
| `FlowSessionTokenMissingError` | No session token for the flow is stored on this device, for example because the source was attached on another device. |
| `FlowSessionExpiredError` | The server no longer accepts the stored session token, for example because the flow already ended. |

[Handle Flow errors](https://docs.dynamic.xyz/docs/javascript/reference/flow-errors.md) describes each error.

## React

`useCancelFlow` is a mutation hook. It writes the cancelled flow into the `useGetFlow` cache, and on `FlowStateConflictError` it refetches the flow so the page shows its real state.

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

function CancelButton({ flowId }) {
  const { mutate: cancelFlow, isPending } = useCancelFlow();

  return (
    <button onClick={() => cancelFlow({ flowId })} disabled={isPending}>
      {isPending ? 'Cancelling...' : 'Cancel payment'}
    </button>
  );
}
```

## Related

* [Expiry and cancellation](https://docs.dynamic.xyz/docs/flow/expiry-and-cancellation.md): who can cancel and when.
* [Build a custom payment UI](https://docs.dynamic.xyz/docs/javascript/reference/flow-ui-guide.md#cancel): where the cancel button belongs.
* [`getFlow`](https://docs.dynamic.xyz/docs/javascript/reference/client/get-flow): read the flow's state.


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