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

# Spending caps

> Cap ETH, USDC, SOL and Stellar USDC transfers from app-owned wallets per transaction or per day.

export const PermissionsCards = ({basePath, pages, cols = 2, horizontal = false}) => {
  const ImageCard = Card;
  const hrefOverrides = {
    "/v3/react/guides/permissions": {
      "conditional-approvals": "/v3/react/guides/conditional-approvals"
    },
    "/v3/rest/permissions": {
      "backend": "/v3/rest/permissions-administration"
    }
  };
  const guardrailBases = ["/v3/rest/permissions", "/v3/server/guides/permissions"];
  const ownership = guardrailBases.includes(basePath) ? 'guardrail' : 'request';
  const overviewImages = {
    "/v3/rest/permissions": {
      "imgUrl": "/images/v3/cards/rest-guardrails.png",
      "horizontalImgUrl": "/images/v3/cards/rest-guardrails-horizontal.png"
    },
    "/v3/server/guides/permissions": {
      "imgUrl": "/images/v3/cards/sdk-server-guardrail-signing.png",
      "horizontalImgUrl": "/images/v3/cards/sdk-server-guardrail-signing-horizontal.png"
    },
    "/v3/react/guides/permissions": {
      "imgUrl": "/images/v3/cards/sdk-get-policy-consent-url.png",
      "horizontalImgUrl": "/images/v3/cards/sdk-get-policy-consent-url-horizontal.png"
    },
    "/v3/react-native/guides/permissions": {
      "imgUrl": "/images/v3/cards/sdk-get-policy-consent-url.png",
      "horizontalImgUrl": "/images/v3/cards/sdk-get-policy-consent-url-horizontal.png"
    }
  };
  const registry = {
    overview: {
      title: "Permissions overview",
      description: "Return to the start of the Permissions path.",
      ...overviewImages[basePath]
    },
    "developer-portal": {
      title: "Create policies in the Developer Portal",
      description: "Build a policy with the Portal forms and activate it on a beta key.",
      imgUrl: "/images/v3/cards/developer-portal-policy-form.png",
      horizontalImgUrl: "/images/v3/cards/developer-portal-policy-form-horizontal.png"
    },
    "json": {
      title: "Write policy JSON",
      description: "Move to JSON for policies the forms cannot express.",
      imgUrl: "/images/v3/cards/developer-portal-policy-json.png",
      horizontalImgUrl: "/images/v3/cards/developer-portal-policy-json-horizontal.png"
    },
    "testing": {
      title: "Test your policies",
      description: "Exercise every rule on beta before you ship.",
      imgUrl: "/images/v3/cards/concept-policy-testing.png",
      horizontalImgUrl: "/images/v3/cards/concept-policy-testing-horizontal.png"
    },
    "production": {
      title: "Go to production",
      description: "Request a production review and publish your policy.",
      imgUrl: "/images/v3/cards/concept-policy-publication.png",
      horizontalImgUrl: "/images/v3/cards/concept-policy-publication-horizontal.png"
    },
    "conditional-approvals": {
      title: "Conditional approvals",
      description: "Require people to approve a request before it signs.",
      imgUrl: "/images/v3/cards/portal-conditional-approval-case.png",
      horizontalImgUrl: "/images/v3/cards/portal-conditional-approval-case-horizontal.png"
    },
    "approval-signatures": {
      title: "Collect approval signatures",
      description: "Collect signatures from approvers for multisig workflows.",
      imgUrl: "/images/v3/cards/sdk-get-approval-signatures.png",
      horizontalImgUrl: "/images/v3/cards/sdk-get-approval-signatures-horizontal.png"
    },
    "adjustable-parameters": {
      title: "Adjustable parameters",
      description: "Let users set their own limits and managers adjust approval settings.",
      imgUrl: "/images/v3/cards/portal-user-policy-limits.png",
      horizontalImgUrl: "/images/v3/cards/portal-user-policy-limits-horizontal.png"
    },
    "lifecycle": {
      title: "Manage the policy lifecycle",
      description: "Version, activate, replace and retire policies.",
      imgUrl: "/images/v3/cards/developer-portal-policy-versions.png",
      horizontalImgUrl: "/images/v3/cards/developer-portal-policy-versions-horizontal.png"
    },
    "backend": {
      title: "Manage permissions from your backend",
      description: "Assign approvers and read policy state with your server secret.",
      imgUrl: "/images/v3/cards/rest-permissions-administration.png",
      horizontalImgUrl: "/images/v3/cards/rest-permissions-administration-horizontal.png"
    },
    templates: ownership === "guardrail" ? {
      title: "Guardrail templates",
      description: "Start from a tested Guardrail policy.",
      imgUrl: "/images/v3/cards/concept-guardrail-templates.png",
      horizontalImgUrl: "/images/v3/cards/concept-guardrail-templates-horizontal.png"
    } : {
      title: "Request templates",
      description: "Start from a tested Request policy.",
      imgUrl: "/images/v3/cards/concept-request-templates.png",
      horizontalImgUrl: "/images/v3/cards/concept-request-templates-horizontal.png"
    },
    "guardrail:templates/spending-caps": {
      title: "Spending caps",
      description: "Cap native and token transfers per transaction or per day.",
      imgUrl: "/images/v3/cards/concept-spending-limits.png",
      horizontalImgUrl: "/images/v3/cards/concept-spending-limits-horizontal.png"
    },
    "guardrail:templates/allowlists": {
      title: "Recipient and contract allowlists",
      description: "Limit transfers and contract calls to addresses you know.",
      imgUrl: "/images/v3/cards/concept-recipient-allowlists.png",
      horizontalImgUrl: "/images/v3/cards/concept-recipient-allowlists-horizontal.png"
    },
    "guardrail:templates/approval-tiers": {
      title: "Approval thresholds and tiers",
      description: "Hold large transfers for approval, or deny the largest.",
      imgUrl: "/images/v3/cards/concept-approval-tiers.png",
      horizontalImgUrl: "/images/v3/cards/concept-approval-tiers-horizontal.png"
    },
    "guardrail:templates/treasury": {
      title: "Treasury approvals",
      description: "Deny unplanned actions and approve wallet or Safe payouts in stages.",
      imgUrl: "/images/v3/cards/concept-treasury-approvals.png",
      horizontalImgUrl: "/images/v3/cards/concept-treasury-approvals-horizontal.png"
    },
    "guardrail:templates/message-signing": {
      title: "Message and raw signing",
      description: "Block or hold raw signatures, and block permits.",
      imgUrl: "/images/v3/cards/concept-signing-actions.png",
      horizontalImgUrl: "/images/v3/cards/concept-signing-actions-horizontal.png"
    },
    "request:templates/allowances": {
      title: "Transfer allowances",
      description: "Let small transfers and a daily budget sign without a prompt.",
      imgUrl: "/images/v3/cards/concept-spending-limits.png",
      horizontalImgUrl: "/images/v3/cards/concept-spending-limits-horizontal.png"
    },
    "request:templates/recipients-and-contracts": {
      title: "Known recipients and app contracts",
      description: "Cover transfers to known addresses and your contract's calls.",
      imgUrl: "/images/v3/cards/concept-recipient-allowlists.png",
      horizontalImgUrl: "/images/v3/cards/concept-recipient-allowlists-horizontal.png"
    },
    "request:templates/cosigner-approval": {
      title: "Co-signer approval",
      description: "Send large transfers to a co-signer for approval.",
      imgUrl: "/images/v3/cards/portal-conditional-approval-case.png",
      horizontalImgUrl: "/images/v3/cards/portal-conditional-approval-case-horizontal.png"
    },
    "request:templates/adjustable-limits": {
      title: "User-adjustable limits",
      description: "Let each user choose their own spending limits.",
      imgUrl: "/images/v3/cards/portal-user-policy-limits.png",
      horizontalImgUrl: "/images/v3/cards/portal-user-policy-limits-horizontal.png"
    },
    "request:templates/message-signing": {
      title: "Message signing",
      description: "Sign messages from users' wallets without a prompt.",
      imgUrl: "/images/v3/cards/concept-signing-actions.png",
      horizontalImgUrl: "/images/v3/cards/concept-signing-actions-horizontal.png"
    }
  };
  const entryFor = slug => registry[slug] || registry[`${ownership}:${slug}`];
  const hrefFor = slug => {
    if (slug === 'overview') return basePath;
    const overrides = hrefOverrides[basePath] || ({});
    return overrides[slug] || `${basePath}/${slug}`;
  };
  return <CardGroup cols={cols}>
      {pages.map(slug => {
    const entry = entryFor(slug);
    return <ImageCard key={slug} title={entry.title} description={entry.description} href={hrefFor(slug)} imgUrl={horizontal ? entry.horizontalImgUrl : entry.imgUrl} horizontal={horizontal} />;
  })}
    </CardGroup>;
};

export const Card = ({imgUrl, compactImgUrl, title, description, href, horizontal = false, newTab = false}) => {
  const [isHovered, setIsHovered] = useState(false);
  const handleClick = e => {
    e.preventDefault();
    if (newTab) {
      window.open(href, '_blank', 'noopener,noreferrer');
    } else {
      window.location.href = href;
    }
  };
  return <div className={`not-prose relative my-2 p-[1px] rounded-xl transition-all duration-300 ${isHovered && href ? 'bg-gradient-to-r from-[#FF4E00] to-[#874AE3]' : 'bg-gray-200'}`} onMouseEnter={() => setIsHovered(true)} onMouseLeave={() => setIsHovered(false)}>
      <a href={href} onClick={href ? handleClick : undefined} className={`not-prose flex ${horizontal ? 'flex-row' : 'flex-col'} font-normal h-full bg-white overflow-hidden w-full ${href ? 'cursor-pointer' : 'cursor-default'} rounded-[11px] no-underline`}>
        {imgUrl && <div className={`relative overflow-hidden flex-shrink-0 ${horizontal ? 'w-[30%] rounded-l-[11px]' : 'w-full'}`} onClick={e => e.stopPropagation()}>
            <picture style={{
    display: 'contents'
  }}>
              {compactImgUrl && <source media="(max-width: 639px)" srcSet={compactImgUrl} />}
              <img src={imgUrl} alt={title} className="w-full h-full object-cover pointer-events-none select-none" draggable="false" />
            </picture>
            <div className="absolute inset-0 pointer-events-none" />
          </div>}
        <div className={`flex-grow px-6 py-5 ${horizontal ? 'w-[70%]' : 'w-full'} flex flex-col ${horizontal && imgUrl ? 'justify-center' : 'justify-start'}`}>
          {title && <h2 className="font-semibold text-base text-gray-800 m-0">{title}</h2>}
          {description && <div className={`font-normal text-gray-500 re leading-6 ${horizontal || !imgUrl ? 'mt-0' : 'mt-1'}`}>
              <p className="m-0 text-xs">{description}</p>
            </div>}
        </div>
      </a>
    </div>;
};

These templates cap how much an app-owned wallet can send, per transaction or per day. Each one covers a single asset on a single network, so pick the closest match and change the amounts.

Daily caps use a fixed window that resets at 00:00 UTC. A transfer counts toward the day's total once Para signs it, whether or not you broadcast it, and a denied transfer does not count.

<Note>
  Native limits count positive native value: on EVM from any transaction, contract calls included, and on Solana, Cosmos, Stellar and Sui from native transfers. Token limits count ERC-20 `transfer` calls on EVM, including ERC-20 transfers sent through Para gas sponsorship, and token transfers on Solana, Cosmos and Stellar. Sui has no token limits. Gas and fees never count. Apart from those sponsored transfers, smart account user operations do not count toward EVM limits. A token limit that names an asset counts only that asset. Each network, asset and window has its own budget, so a limit of 1 ETH per day on Ethereum and Base allows 1 ETH per day on each network. Payments a Safe makes count only toward the Safe limits, `safe.spend.native` and `safe.spend.erc20`, which keep one budget per Safe. See [Metered facts](/v3/references/permissions/facts#metered-facts) for the full rules.
</Note>

## Cap ETH per transaction and per day

Use this to limit what a compromised or misbehaving backend can move. The policy denies an ETH transfer on Base above 0.1 ETH, and any ETH transfer that would take the day's total above 1 ETH.

<Tabs>
  <Tab title="Developer Portal">
    1. On your **Development** key, open **Permissions**, select the **Guardrails** tab, and select **Add** or **New Guardrail**.
    2. Enter a **Guardrail Policy Name**. In **Networks**, clear **Ethereum (1)**, select **Base (8453)**, and select **Continue**.
    3. Select **Transfer ETH**. Select **Add Condition**, change the condition to **Transaction amount**, keep **is at most** and **per transaction**, and enter `0.1` with the unit set to **ETH**.
    4. Select **Add Action** and choose **Transfer ETH** again. Add a **Transaction amount** condition: **is at most**, `1` **ETH**, **per time window**, `24` **hours**.
    5. Select **Review**, then **Save Draft**, then **Activate** on the draft.
  </Tab>

  <Tab title="JSON">
    ```json guardrail-native-spending-caps.json theme={null}
    {
      "id": "base-native-spending-caps",
      "schemaVersion": "permissions.policy.v1",
      "version": "1",
      "selector": {
        "walletType": "EVM",
        "chainId": "8453"
      },
      "rules": [
        {
          "id": "per-transaction-cap",
          "effect": "deny",
          "actions": ["transfer_native"],
          "when": {
            "kind": "all",
            "of": [
              { "kind": "compare", "fact": "request.classification", "op": "eq", "value": "native_transfer" },
              { "kind": "compare", "fact": "request.value.baseUnits", "op": "gt", "value": "100000000000000000" }
            ]
          }
        },
        {
          "id": "daily-cap",
          "effect": "deny",
          "actions": ["transfer_native"],
          "when": {
            "kind": "all",
            "of": [
              { "kind": "compare", "fact": "request.classification", "op": "eq", "value": "native_transfer" },
              {
                "kind": "aggregate",
                "fact": "wallet.spend.native",
                "op": "gt",
                "value": "1000000000000000000",
                "window": "24h"
              }
            ]
          }
        }
      ],
      "metadata": {
        "productMode": "guardrail",
        "description": "Cap ETH on Base at 0.1 per transaction and 1 per day"
      }
    }
    ```
  </Tab>
</Tabs>

In the form you enter the amount the wallet may send, and the saved rule denies anything above it. That is why each condition in the JSON uses `gt`.

| Signing request | Result |
| - | - |
| Send 0.05 ETH with 0.3 ETH sent today | Signs |
| Send 0.2 ETH | Denied by `per-transaction-cap` |
| Send 0.08 ETH with 0.95 ETH sent today | Denied by `daily-cap` |

<Check>
  Test it on your Development key: sign a 0.05 ETH transfer, then a 0.2 ETH transfer. The first signs and the second is denied.
</Check>

<Info>
  The cap counts ETH transfers only. ETH sent along with a contract call is a `call_contract` request, which this policy does not cover.
</Info>

## Cap USDC per day

Use this when the wallet pays out USDC and other tokens should stay unrestricted. The policy denies a USDC transfer on Base once the day's total would pass 1,000 USDC. USDC has 6 decimals, so the cap is `1000000000`.

<Tabs>
  <Tab title="Developer Portal">
    <Warning>
      The form builds a stricter version of this template. It pins the token contract, so the saved Guardrail also denies transfers of every other ERC-20 token on Base, and its JSON leaves out `asset`.
    </Warning>

    1. On your **Development** key, open **Permissions**, select the **Guardrails** tab, and select **Add** or **New Guardrail**.
    2. Enter a **Guardrail Policy Name**. In **Networks**, clear **Ethereum (1)**, select **Base (8453)**, and select **Continue**.
    3. Select **Transfer tokens** and enter `0x833589fcd6edb6e08f4c7c32d4f71b54bda02913` in **Contract Address**. **Function Name** is fixed to `transfer`.
    4. Select **Add Condition**, change the condition to **Transaction amount**, choose **per time window** with `24` **hours**, keep **is at most**, and enter `1000000000` token base units.
    5. Select **Review**, then **Save Draft**, then **Activate** on the draft.
  </Tab>

  <Tab title="JSON">
    ```json guardrail-usdc-daily-cap.json theme={null}
    {
      "id": "base-usdc-daily-cap",
      "schemaVersion": "permissions.policy.v1",
      "version": "1",
      "selector": {
        "walletType": "EVM",
        "chainId": "8453"
      },
      "rules": [
        {
          "id": "usdc-daily-cap",
          "effect": "deny",
          "actions": ["transfer_tokens"],
          "when": {
            "kind": "all",
            "of": [
              { "kind": "compare", "fact": "request.classification", "op": "eq", "value": "erc20_transfer" },
              {
                "kind": "aggregate",
                "fact": "wallet.spend.erc20",
                "op": "gt",
                "value": "1000000000",
                "window": "24h",
                "asset": "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913"
              }
            ]
          }
        }
      ],
      "metadata": {
        "productMode": "guardrail",
        "description": "Cap USDC transfers on Base at 1,000 per day"
      }
    }
    ```
  </Tab>
</Tabs>

| Signing request | Result |
| - | - |
| Transfer 400 USDC with 500 USDC sent today | Signs |
| Transfer 200 USDC with 900 USDC sent today | Denied by `usdc-daily-cap` |
| Transfer another ERC-20 token | Not limited by this policy |

<Warning>
  Keep `asset` when you edit this template. Without it, the rule caps every ERC-20 token at `1000000000` base units a day, each token with its own budget. That is 1,000 USDC, but only a billionth of a token that uses 18 decimals.
</Warning>

<Check>
  Test it on your Development key with a lower cap, such as `1000000` (1 USDC): sign two 0.6 USDC transfers, and the second is denied. Restore the cap before you request a production review.
</Check>

## Cap SOL per day

The same daily cap works on other wallet types. This policy denies a SOL transfer on Solana mainnet once the day's total would pass 10 SOL, which is `10000000000` lamports.

<Tabs>
  <Tab title="Developer Portal">
    1. On your **Development** key, open **Permissions**, select the **Guardrails** tab, and select **Add** or **New Guardrail**.
    2. Enter a **Guardrail Policy Name**. In **Networks**, expand **Solana** and select **Mainnet**. Choosing a Solana network clears the EVM selection. Select **Continue**.
    3. Select **Native transfer**. Select **Add Condition**, change the condition to **Transaction amount**, choose **per time window** with `24` **hours**, keep **is at most**, and enter `10000000000` lamports.
    4. Select **Review**, then **Save Draft**, then **Activate** on the draft.
  </Tab>

  <Tab title="JSON">
    ```json guardrail-solana-daily-cap.json theme={null}
    {
      "id": "solana-sol-daily-cap",
      "schemaVersion": "permissions.policy.v1",
      "version": "1",
      "selector": {
        "walletType": "SOLANA",
        "chainId": "solana:mainnet"
      },
      "rules": [
        {
          "id": "sol-daily-cap",
          "effect": "deny",
          "actions": ["transfer_native"],
          "when": {
            "kind": "all",
            "of": [
              { "kind": "compare", "fact": "request.actionFamilies", "op": "in", "value": "native_transfer" },
              {
                "kind": "aggregate",
                "fact": "wallet.spend.native",
                "op": "gt",
                "value": "10000000000",
                "window": "24h"
              }
            ]
          }
        }
      ],
      "metadata": {
        "productMode": "guardrail",
        "description": "Cap SOL transfers on Solana at 10 per day"
      }
    }
    ```
  </Tab>
</Tabs>

The rule checks `request.actionFamilies`, which lists every kind of action in the transaction, so the cap also counts a SOL transfer bundled with other instructions.

| Signing request | Result |
| - | - |
| Send 4 SOL with 5 SOL sent today | Signs |
| Send 2 SOL with 9 SOL sent today | Denied by `sol-daily-cap` |

<Check>
  Test it on your Development key with `chainId` set to `solana:devnet` and a 1 SOL cap (`1000000000`): sign two 0.6 SOL transfers, and the second is denied. Restore the mainnet values before you request a production review.
</Check>

## Cap Stellar USDC per day

On Stellar, a token cap names the asset as `stellar:<network>:<code>:<issuer>`. This policy denies a USDC payment on the Stellar public network once the day's total would pass 1,000 USDC. Stellar amounts use 7 decimals, so the cap is `10000000000`.

<Tabs>
  <Tab title="Developer Portal">
    <Warning>
      The form builds a stricter version of this template. It pins the asset in **Token Asset**, so the saved Guardrail also denies payments of every other Stellar token. It also writes the network as its passphrase instead of `stellar:mainnet`.
    </Warning>

    1. On your **Development** key, open **Permissions**, select the **Guardrails** tab, and select **Add** or **New Guardrail**.
    2. Enter a **Guardrail Policy Name**. In **Networks**, expand **Stellar**, select **Public**, and select **Continue**.
    3. Select **Token transfer** and enter `stellar:mainnet:USDC:GA5ZSEJYB37JRC5AVCIA5MOP4RHTM335X2KGX3IHOJAPP5RE34K4KZVN` in **Token Asset**.
    4. Select **Add Condition**, change the condition to **Transaction amount**, choose **per time window** with `24` **hours**, keep **is at most**, and enter `10000000000` token base units.
    5. Select **Review**, then **Save Draft**, then **Activate** on the draft.
  </Tab>

  <Tab title="JSON">
    ```json guardrail-stellar-usdc-daily-cap.json theme={null}
    {
      "id": "stellar-usdc-daily-cap",
      "schemaVersion": "permissions.policy.v1",
      "version": "1",
      "selector": {
        "walletType": "STELLAR",
        "chainId": "stellar:mainnet"
      },
      "rules": [
        {
          "id": "stellar-usdc-daily-cap",
          "effect": "deny",
          "actions": ["transfer_tokens"],
          "when": {
            "kind": "all",
            "of": [
              { "kind": "compare", "fact": "request.actionFamilies", "op": "in", "value": "token_transfer" },
              {
                "kind": "aggregate",
                "fact": "wallet.spend.token",
                "op": "gt",
                "value": "10000000000",
                "window": "24h",
                "asset": "stellar:mainnet:USDC:GA5ZSEJYB37JRC5AVCIA5MOP4RHTM335X2KGX3IHOJAPP5RE34K4KZVN"
              }
            ]
          }
        }
      ],
      "metadata": {
        "productMode": "guardrail",
        "description": "Cap USDC transfers on Stellar at 1,000 per day"
      }
    }
    ```
  </Tab>
</Tabs>

| Signing request | Result |
| - | - |
| Pay 400 USDC with 500 USDC paid today | Signs |
| Pay 200 USDC with 900 USDC paid today | Denied by `stellar-usdc-daily-cap` |
| Pay XLM or another Stellar asset | Not limited by this policy |

<Check>
  Test it on your Development key on `stellar:testnet`: change `chainId`, and change `asset` to `stellar:testnet:USDC:` followed by the testnet issuer. Lower the cap, then sign payments that pass it.
</Check>

## Publish your caps

Once each cap holds on beta, request a production review from your Production key. Para applies the same caps to production, so set the amounts you want in production before you ask.

<CardGroup cols={1}>
  <Card title="Spend facts and windows" description="See which transfers each spend fact counts and how budgets are shared." href="/v3/references/permissions/facts" imgUrl="/images/v3/cards/concept-policy-facts-horizontal.png" horizontal />
</CardGroup>

<PermissionsCards basePath="/v3/server/guides/permissions" pages={["testing", "templates/allowlists", "production"]} cols={3} />


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