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

# Message and raw signing

> Block or hold raw signatures from app-owned wallets that should not produce them, and block permit and authorization signatures.

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>;
};

export const ConditionalApprovalsEnablement = () => <p>Para turns on conditional approvals per API key, so your beta and production keys are enabled separately. Until it does, validating or saving this policy fails with <code>permissions_v2_extension_unavailable</code>.</p>;

Message signing works differently from transfers. On an app-owned EVM wallet, personal messages and typed data sign unless a Guardrail blocks them, and so do raw signatures:

<Note>
  While Permissions is on, raw signatures follow the same defaults as other signing requests: an app-owned wallet signs them, and a user-owned wallet sends them to the user for review. Rules that name the `raw_signature` action, and rules with `"actions": "all"`, can change that. Raw signatures are `sign-raw` requests, messages from Solana, Stellar and Sui wallets, and REST `sign-message` requests from a Cosmos wallet that do not pass `walletType: "EVM"`. The SDKs sign messages from Cosmos wallets as EVM `sign_message` requests. Raw signatures carry no network a selector can name, so only a policy without `chainId` governs them. A rule that names `raw_signature` can read only `para.wallet.*` facts. The Developer Portal forms cannot create one, so write it in policy JSON, with **Edit JSON** in the Developer Portal or with the [CLI](/v3/cli/permissions).
</Note>

Para cannot read what a raw signature signs, so spending caps, allowlists and other rules about transfers and contract calls never apply to one. These templates block or hold raw signatures for wallets that should not produce them, and block the typed data signatures that can hand over funds. The Developer Portal forms cannot express any of them, so publish them as JSON.

## Block raw signatures

Use this when your backend never calls `sign-raw` with its EVM wallets, for example because it only sends transfers that your other Guardrails check.

```json guardrail-block-raw-signatures.json theme={null}
{
  "id": "evm-block-raw-signatures",
  "schemaVersion": "permissions.policy.v1",
  "version": "1",
  "selector": {
    "walletType": "EVM"
  },
  "rules": [
    {
      "id": "deny-raw-signatures",
      "effect": "deny",
      "actions": ["raw_signature"],
      "when": { "kind": "compare", "fact": "para.wallet.hasUserOwner", "op": "eq", "value": false }
    }
  ],
  "metadata": {
    "productMode": "guardrail",
    "description": "Block raw signatures from app-owned EVM wallets"
  }
}
```

A Guardrail applies only to app-owned wallets, where `para.wallet.hasUserOwner` is always `false`, so the rule denies every raw signature from them. Personal messages and typed data are separate actions, so this policy leaves them alone.

| Signing request | Result |
| - | - |
| REST: `sign-raw` with an app-owned EVM wallet | Denied by `deny-raw-signatures` |
| Sign a personal message or typed data | Signs, because this policy does not cover it |
| Send ETH or tokens | Not limited by this policy |

<Check>
  Test it on your Development key: call REST `sign-raw` with an app-owned EVM wallet before and after you activate the policy. The first request signs and the second is denied.
</Check>

To block raw signatures for some wallets only, list their IDs in the selector as `walletId`. For Solana, Stellar or Sui wallets, copy the policy with a new `id` and change `walletType`. On those wallet types every message is a raw signature, so the copy blocks message signing as well. Cosmos wallets need more, as [Cosmos wallets](#cosmos-wallets) explains.

## Hold message signing for approval

Use this when your backend signs messages with app-owned Solana wallets only now and then, for example to prove wallet ownership to another service, and a person should confirm each one.

<Info>
  <ConditionalApprovalsEnablement />
</Info>

```json guardrail-solana-message-signing.json theme={null}
{
  "id": "solana-review-message-signing",
  "schemaVersion": "permissions.policy.v1",
  "version": "1",
  "selector": {
    "walletType": "SOLANA"
  },
  "rules": [
    {
      "id": "review-message-signing",
      "effect": "require_approval",
      "actions": ["raw_signature"],
      "when": { "kind": "compare", "fact": "para.wallet.hasUserOwner", "op": "eq", "value": false },
      "approvalRequirement": {
        "id": "message-signing-review",
        "evidence": "authenticated_approval",
        "stages": [
          { "id": "reviewer", "requiredApprovals": 1, "eligibility": [{ "kind": "role", "name": "signing-reviewer" }] }
        ]
      }
    }
  ],
  "metadata": {
    "productMode": "guardrail",
    "description": "Hold message signing from app-owned Solana wallets for approval"
  }
}
```

Each message opens an approval case for one person with the `signing-reviewer` role. Set them up as in the approval tiers templates: once the policy is active, your backend assigns the role in an [authorization scope](/v3/references/permissions/approval-requirements#eligibility) and binds the wallet to that scope.

| Signing request | Result |
| - | - |
| Sign a message with an app-owned Solana wallet | Needs approval from one `signing-reviewer` |
| Send SOL or call a program | Not limited by this policy |

The case covers that exact message. When it is `completed`, send the same request again: through REST with the same `Idempotency-Key`, or through the server SDK with the case ID as `resumeApprovalCaseId`.

<Check>
  Test it on your Development key: assign `signing-reviewer` to a test user and bind the wallet. Sign a message with the app-owned Solana wallet through REST with an `Idempotency-Key`, approve the case as that user, and send the request again with the same key to sign it.
</Check>

For Stellar or Sui wallets, copy the policy with a new `id` and change `walletType`. Each wallet type needs its own policy.

## Cosmos wallets

A Cosmos wallet can also sign as an EVM wallet, so its messages are raw signatures only on some routes. A copy of either template with `walletType` set to `COSMOS` covers those routes, and an EVM policy covers the others:

| Signing request from an app-owned Cosmos wallet | Action | Policy that covers it |
| - | - | - |
| REST `sign-raw` | `raw_signature` | The `COSMOS` copy |
| REST `sign-message` without `walletType: "EVM"` | `raw_signature` | The `COSMOS` copy |
| REST `sign-message` with `walletType: "EVM"` | `sign_message` | An EVM policy without `chainId` whose rule lists `sign_message` |
| Server SDK `signMessage` | `sign_message` | An EVM policy without `chainId` whose rule lists `sign_message` |

`evm-block-raw-signatures` does not cover the last two rows, because they are `sign_message` requests. To block or hold every message from a Cosmos wallet, add the `COSMOS` copy and an EVM policy whose rule lists `sign_message`. List the Cosmos wallet's ID as `walletId` in the EVM policy, or it also applies to messages from your EVM wallets.

## Block permit and authorization signatures

A permit is typed data that lets another address spend the wallet's tokens without a transaction from the wallet. An EIP-7702 authorization lets the account run another contract's code. Both can move funds later, outside any transfer cap. Use this template when your backend should never sign either one.

<Note>
  The Para client and server SDKs, including their viem, ethers and wagmi integrations, sign EIP-712 typed data and EIP-7702 authorizations as a message over their hash, so Para evaluates those requests as `sign_message`. The `sign_typed_data` and `sign_authorization` actions, and the facts Para reads from typed data and authorizations, such as `request.typedData.primaryType`, apply only to the REST API's `sign-typed-data` and `sign-authorization` routes, which the REST SDK calls.

  This template cannot inspect a permit or an authorization your backend signs through the server SDK.
</Note>

```json guardrail-block-permit-signatures.json theme={null}
{
  "id": "evm-block-permit-signatures",
  "schemaVersion": "permissions.policy.v1",
  "version": "1",
  "selector": {
    "walletType": "EVM"
  },
  "rules": [
    {
      "id": "block-permits-and-authorizations",
      "effect": "deny",
      "actions": ["sign_typed_data", "sign_authorization"],
      "when": { "kind": "compare", "fact": "request.approvalLike", "op": "eq", "value": true }
    }
  ],
  "metadata": {
    "productMode": "guardrail",
    "description": "Block permit and EIP-7702 authorization signatures"
  }
}
```

Para sets `request.approvalLike` to `true` for every authorization, and for typed data whose primary type contains `permit` in any letter case, such as `Permit`, `PermitSingle` or `PermitBatch`. Typed data with any other primary type is not affected.

| Signing request | Result |
| - | - |
| REST: sign typed data with primary type `Permit` or `PermitSingle` | Denied by `block-permits-and-authorizations` |
| REST: sign an EIP-7702 authorization | Denied by `block-permits-and-authorizations` |
| REST: sign typed data with primary type `Order` | Signs |
| Sign a personal message | Signs |
| Server SDK: sign a permit or an authorization | Signs, because Para sees it as a personal message |

<Check>
  Test it on your Development key: through the REST `sign-typed-data` endpoint, sign an EIP-2612 `Permit` with an app-owned EVM wallet, then sign a personal message. Only the permit is denied.
</Check>

To block all typed data and authorizations on REST, whatever their primary type, replace the condition with `{ "kind": "compare", "fact": "para.wallet.hasUserOwner", "op": "eq", "value": false }`. If your backend signs through the server SDK, also add `sign_message` to the rule's actions. That is the only way to stop permits and authorizations there, and it also blocks every personal message.

## Publish your signing rules

Wallets and roles belong to each environment. Before you request a production review, confirm that each policy names the wallet types and wallets you mean to restrict in production. After the review, assign `signing-reviewer` there and bind the production wallets. A policy with a `walletId` needs the production wallet's ID.

<CardGroup cols={1}>
  <Card title="Actions reference" description="Which signing requests count as each action, on every wallet type." href="/v3/references/permissions/policy-json#actions" imgUrl="/images/v3/cards/concept-signing-actions-horizontal.png" horizontal />
</CardGroup>

<PermissionsCards basePath="/v3/server/guides/permissions" pages={["conditional-approvals"]} cols={1} horizontal />

<PermissionsCards basePath="/v3/server/guides/permissions" pages={["templates/approval-tiers", "backend", "json", "testing"]} cols={2} />


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