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

# User-adjustable limits

> Let each user choose their own ETH or USDC spending limits, starting from your defaults.

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 let each user choose their own spending limits. Your policy sets the defaults, and every user can change them for their own wallet without a new policy version.

<Info>
  Para turns on personal limits per API key, so your beta and production keys are enabled separately. Until it does, validating or saving a policy with a `parameter` fails with `permissions_v2_extension_unavailable`.
</Info>

<Warning>
  Para does not bound the values users save. A user can set any amount, including one far above your default. Use these templates only where any limit the user picks is acceptable to you.
</Warning>

## Let users set a daily ETH limit

Use this when users should decide how much ETH your app may send for them each day. The default is 0.05 ETH (`50000000000000000` wei) per day on Base. The Developer Portal forms cannot express a `parameter`, so publish the template as JSON.

```json request-adjustable-daily-limit.json theme={null}
{
  "id": "base-adjustable-daily-limit",
  "schemaVersion": "permissions.policy.v1",
  "version": "1",
  "selector": {
    "walletType": "EVM",
    "chainId": "8453"
  },
  "rules": [
    {
      "id": "allow-daily-native-transfers",
      "effect": "allow",
      "actions": ["transfer_native"],
      "when": {
        "kind": "all",
        "of": [
          { "kind": "compare", "fact": "request.classification", "op": "eq", "value": "native_transfer" },
          {
            "kind": "aggregate",
            "fact": "wallet.spend.native",
            "op": "lte",
            "value": "50000000000000000",
            "window": "24h",
            "parameter": { "id": "daily-eth-limit", "label": "Daily ETH limit" }
          }
        ]
      }
    }
  ],
  "metadata": {
    "productMode": "delegation",
    "description": "Send ETH on Base up to a daily limit you choose"
  }
}
```

The `parameter` turns the condition's `value` into a default the user can change. Its `label` is the name users see next to the input.

| Signing request | Result |
| - | - |
| Send ETH within the user's daily limit | Signs without a prompt |
| Send ETH that would take the day's total past the user's limit | Goes to the user for review |

<Check>
  Test it on your Development key: open the URL that `getPolicyConsentUrl` returns for a test user, set **Daily ETH limit** to `20000000000000000` (0.02 ETH), and approve. A 0.015 ETH transfer then signs without a prompt, and a second one opens a review.
</Check>

## Let users set USDC limits per transfer and per day

Use this when users should cap both single payments and the daily total. The defaults are 25 USDC per transfer (`25000000`) and 100 USDC per day (`100000000`) on Base.

```json request-adjustable-usdc-limits.json theme={null}
{
  "id": "base-adjustable-usdc-limits",
  "schemaVersion": "permissions.policy.v1",
  "version": "1",
  "selector": {
    "walletType": "EVM",
    "chainId": "8453"
  },
  "rules": [
    {
      "id": "allow-usdc-within-limits",
      "effect": "allow",
      "actions": ["transfer_tokens"],
      "when": {
        "kind": "all",
        "of": [
          { "kind": "compare", "fact": "request.classification", "op": "eq", "value": "erc20_transfer" },
          {
            "kind": "compare",
            "fact": "request.contract.address",
            "op": "eq",
            "value": "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913"
          },
          {
            "kind": "compare",
            "fact": "request.token.value.baseUnits",
            "op": "lte",
            "value": "25000000",
            "parameter": { "id": "usdc-per-transfer", "label": "USDC per transfer" }
          },
          {
            "kind": "aggregate",
            "fact": "wallet.spend.erc20",
            "op": "lte",
            "value": "100000000",
            "window": "24h",
            "asset": "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913",
            "parameter": { "id": "usdc-per-day", "label": "USDC per day" }
          }
        ]
      }
    }
  ],
  "metadata": {
    "productMode": "delegation",
    "description": "Send USDC on Base within limits you choose"
  }
}
```

The rule allows a USDC transfer only when it fits both limits. Each `parameter` has its own `id`, and users set each one separately.

| Signing request | Result |
| - | - |
| Transfer 20 USDC with 50 USDC sent today, with the defaults | Signs without a prompt |
| Transfer 30 USDC, with the defaults | Goes to the user for review |
| Transfer 20 USDC with 90 USDC sent today, with the defaults | Goes to the user for review |

<Check>
  Test it on Base Sepolia: set `chainId` to `84532` and change both USDC addresses to `0x036cbd53842c5426634e7929541ec2318f3dcf7e`. On the consent screen, lower **USDC per transfer** to `1000000` (1 USDC), then check that a 2 USDC transfer opens a review.
</Check>

## What users see

The consent screen shows an input for each parameter, labelled with its `label`, with your default as the placeholder. Values the user enters there are saved with their consent.

The inputs take base units, so 0.02 ETH is `20000000000000000`. For amounts in ETH or USDC, build a settings screen in your app that converts the value and saves it with the SDK, as the adjustable parameters guide for your platform shows. After a user saves new values, they approve the policy again before it applies with those values.

## Change a policy with personal limits

Users' saved limits are tied to the policy version they saved them for. Plan changes with that in mind:

* The Developer Portal forms cannot show parameters. When you select the policy's networks to edit it, the Developer Portal opens it in the JSON editor only.
* Your backend can read users' limits but cannot change them.

<Warning>
  A user who has saved limits must confirm them again after you publish a new version. Until they do, Para denies every signing request from their wallet on the policy's networks, not only the requests its rules cover.
</Warning>

## Publish your limits

Values users save belong to the environment where they save them. After Para applies the policy to production through a production review, production users start from your defaults.

<CardGroup cols={1}>
  <Card title="User-adjustable parameters" description="Which facts and operators can take a parameter, and the validation codes." href="/v3/references/permissions/facts" imgUrl="/images/v3/cards/concept-policy-facts-horizontal.png" horizontal />
</CardGroup>

<PermissionsCards basePath="/v3/react-native/guides/permissions" pages={["adjustable-parameters"]} cols={1} horizontal />

<PermissionsCards basePath="/v3/react-native/guides/permissions" pages={["testing", "production"]} cols={2} />


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