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

# Set up

> Add an app fee to a transaction and disclose it before the user signs

An app fee is a fee your application charges on top of transaction execution costs. Set a rate, then show the quoted fee before requesting a signature. App fees remain user-paid even when you sponsor gas and bridging.

`appFees.feeBps` is an integer from `0` to `10000`, where `10000` is 100%. A rate of `25` requests a 0.25% fee. Read the quoted amount rather than calculating it from the rate: collection depends on the route.

<Tabs>
  <Tab title="Embedded wallet">
    ## Configure the fee

    Start with the embedded-wallet example in [Send a transaction](/transactions/multichain/send-a-transaction#send-the-transaction). It defines `oneAuth`, `accountAddress`, the chains, tokens, `amount`, and transfer `calls` used here.

    Add `appFees` to that request and enable the experimental [clear-signing review](/wallets/embedded-wallets/clear-signing). Keep the source constraints and other options from the transaction guide when your flow needs them:

    ```ts {6} theme={null}
    const result = await oneAuth.sendIntent({
      accountAddress,
      targetChain: arbitrum.id,
      tokenRequests: [{ token: usdcOnArbitrum, amount }],
      calls: [...calls],
      appFees: { feeBps: 25 },
      experimental_clear_signing: true,
    });
    ```

    To apply a default to every intent, add these options to your existing [client initialization](/wallets/embedded-wallets/accounts#initialize-the-client):

    ```ts {3} theme={null}
    const oneAuth = new OneAuthClient({
      ...clientConfig,
      appFees: { feeBps: 25 },
      experimental_clear_signing: true,
    });
    ```

    `clientConfig` is the provider, client ID, and sponsorship configuration from the shared setup.

    Update your existing client rather than creating a second one. Its default applies to SDK intents, batch items, the EIP-1193 provider, wagmi, and the pay button when they use that client.

    Override the default with `appFees` on an individual `sendIntent` request, `sendBatchIntent` item, or headless `prepareIntent` request. Use `{ feeBps: 0 }` to disable the fee for that intent; omitting `appFees` inherits the default. With neither a default nor an override, no fee is requested. Non-integer or out-of-range rates are rejected before signing.

    Keep [application authentication](/transactions/sponsorship/set-up#application-authentication-is-not-sponsorship) configured even for user-paid intents. The app JWT identifies your Rhinestone project, which receives the fee; the rate is not configured in the developer portal.

    ## Display the fee

    With clear signing enabled, the review dialog shows **App fee** as a separate charged line and includes it in the total. It is never labeled sponsored. Blind signing does not show this review, so do not rely on it to disclose the charge.

    For a custom signing UI, `OneAuthHeadlessClient` accepts the same client default and per-request override. After your existing headless `prepareIntent` call, read the quoted USD string:

    ```ts {1} theme={null}
    const appFeeUsd = prepared.quote?.cost.breakdown?.app;

    if (appFeeUsd !== undefined) {
      showFeeToUser({
        label: "App fee",
        amount: appFeeUsd,
        currency: "USD",
      });
    }
    ```

    `prepared` is the result of `OneAuthHeadlessClient.prepareIntent()`; `showFeeToUser` represents your UI. An absent `app` field means that route has no app fee. Refresh the displayed amount whenever you prepare a new quote, before collecting signatures.

    ## Validate the rate

    The browser chooses the rate. For sponsored intents, add an assertion to the signer from [Sponsorship setup](/transactions/sponsorship/set-up#embedded-wallets-backend):

    ```ts {5} theme={null}
    import { createSponsorshipSigner } from "@rhinestone/1auth/server";

    export const sponsorshipSigner = createSponsorshipSigner({
      shouldSponsor: {
        appFees: (fee) => fee?.feeBps === 25,
      },
    });
    ```

    Keep your other authorization checks alongside this filter. `extensionToken()` refuses to issue a grant if the requested rate differs from `25` or is missing. Use `sponsorshipMode: "required"` when sponsorship denial must stop the flow. `preferred` can obtain a new self-funded quote, and `disabled` does not request a sponsorship grant.

    This check authorizes the requested rate, not the amount ultimately collected. It does not enforce a fee on self-funded transactions or guarantee that a route can carry one. See [Policies and security](/transactions/sponsorship/policies-and-security#current-enforcement-boundary) for the authorization boundary.
  </Tab>

  <Tab title="Custom signer">
    ## Configure the fee

    Pass `appFees` when you prepare the transaction. This example requests 100 USDC on Arbitrum and adds a 25 basis point (0.25%) app fee:

    ```ts {9} theme={null}
    import { arbitrum, base } from "viem/chains";

    const usdcOnArbitrum = "0xaf88d065e77c8cC2239327C5EDb3A432268e5831";

    const prepared = await account.prepareTransaction({
      sourceChains: [base],
      targetChain: arbitrum,
      tokenRequests: [{ address: usdcOnArbitrum, amount: 100_000_000n }],
      appFees: { feeBps: 25 },
    });
    ```

    App fees are configured per transaction. Keep the account initialization from your existing wallet setup rather than creating a separate client for monetization.

    ## Display the fee

    Read the app fee from the final selected quote:

    ```ts theme={null}
    const appFeeUsd = prepared.quotes.best.cost.fees.breakdown.app.usd;

    showFeeToUser({
      label: "App fee",
      amount: appFeeUsd,
      currency: "USD",
    });
    ```

    Display the fee before the user signs. If you select or prepare a different quote, update the displayed amount because costs can change with the route.

    The full fee breakdown is available at `prepared.quotes.best.cost.fees.breakdown`. Each category includes `usd` and `sponsored`; the app fee itself is not sponsorable.
  </Tab>
</Tabs>

Next, follow [Monetize the flow](./monetize-the-flow) to understand how the fee is calculated, collected, and withdrawn.
