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

# Restrict a session

> Drop the intent-execution fallback so a session runs only the calls it lists.

By default, a session with permissions also carries a wildcard intent-execution fallback: a call that matches none of its permissions is not rejected, it is executed as an intent through the Orchestrator. This is what lets a scoped session drive intents instead of only the calls it lists.

Set `restrictToActions` to drop the fallback. For that permission ID, the session's permissions and actions are then the only calls it can run, and any other call reverts:

Examples on this page use `@rhinestone/sdk`, where `rhinestone` is the `RhinestoneSDK` instance from [Create a session with a custom setup](/wallets/session-keys/custom-setup/create-a-session).

```ts {17-18} theme={null}
import { erc20Abi } from "viem";
import { base } from "viem/chains";

const session = await rhinestone.createSession({
  chain: base,
  owners: {
    type: "ecdsa",
    accounts: [sessionOwnerAccount],
  },
  permissions: [
    {
      abi: erc20Abi,
      address: usdcAddress,
      functions: { transfer: {} },
    },
  ],
  restrictToActions: true,
  saltMode: "strict",
});
```

Use strict salt mode for each new restricted session. In SDK 2.16.1, the default `"none"` mode uses a zero salt, so a reused signer produces the same permission ID even when the actions change. Enabling that ID again adds policies rather than replacing previous ones. Strict mode separates different restricted definitions, but the signer still controls every earlier permission ID until you revoke it. If you are narrowing authority, revoke the previous broader session.

The tradeoffs:

* The session can no longer sign arbitrary Orchestrator intents. Only its explicit permissions and actions execute.
* It needs at least one permission or action. A restricted session with neither is rejected.
* It cannot be combined with `crossChainPermits` or `claimPolicies`, whose spending and time-frame guardrails live inside the fallback.
* Signing defaults to `disabled`, so the session key cannot sign an approval off-chain. Set [`signing`](./session-signing) explicitly if the session needs ERC-1271 signatures.

<Note>Requires `@rhinestone/sdk` 2.6.0 or later.</Note>

## Raw scoped actions

Permissions are derived from an ABI, so they can only address calls you can name as a function on a contract. Use `actions` for the rest — a call whose ABI you don't have, or one you want to scope by selector directly:

```ts {7-14} theme={null}
const session = await rhinestone.createSession({
  chain: base,
  owners: {
    type: "ecdsa",
    accounts: [sessionOwnerAccount],
  },
  actions: [
    {
      target: routerAddress,
      selector: "0x3593564c",
      policies: [{ type: "usage-limit", limit: 10n }],
    },
  ],
  restrictToActions: true,
  saltMode: "strict",
});
```

Each entry takes a `target`, a `selector`, and optional `policies`. Three rules the SDK enforces:

* An entry must be scoped: both `target` and `selector` are required. A fallback-shaped entry is rejected, because it would map to the wildcard fallback target.
* An entry may not target the fallback sentinel, which would reintroduce the wildcard the restriction drops.
* A raw action must not collide with a `permissions` entry on the same `(target, selector)` pair. The two share one on-chain action id, so merge them into a single entry instead.

A [swap scope](./swap-sessions) is sugar over exactly these two primitives: it compiles to an approve permission plus one scoped action per swap venue, and implies `restrictToActions`.
