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

# Signer layer

> Read, add, update, remove, and replace rules on a signer's policy layer, as legacy rules or declarative rules.

> [!NOTE]
>
> Wallet and signer policy layers are in **early access**. [Talk to us](https://www.dynamic.xyz/talk-to-us) if you'd like to participate.

Calls on this page go to `/sdk/<environment_id>/...` and authenticate with the signed-in end user's JWT (`<user_jwt>`), not an environment API token. The caller must be the wallet owner, or a business-account owner or admin for a signer in their account. For which kind of rules a layer holds and how declarative rules are enabled, see [Managing layers](https://www.dynamic.xyz/docs/embedded-wallets/mpc/policies/managing-layers.md).

The signer-Layer applies to a single signer share set on a wallet. This includes the caller's own share set, delegated access share sets, and additional signers on a business-account wallet. Omit `shareSetId` to target the caller's own active share set. To find a share set id for another signer, use `getWalletAccountShareSets` from the JavaScript SDK; see the [business accounts policies guide](https://www.dynamic.xyz/docs/javascript/reference/business-accounts/policies/overview.md).

> [!NOTE]
>
> A `shareSetId` is the current identifier for a signer. It rotates when wallet shares are refreshed or reshared, so re-read it with `getWalletAccountShareSets` before each update. The policy is bound to a stable `signerId` that the enclave mints, so a rotated `shareSetId` still points to the same policy.

## Read the signer-Layer

```bash theme={"system"}
curl "https://app.dynamicauth.com/api/v0/sdk/<environment_id>/waas/<wallet_id>/signers/self/policy-layer?shareSetId=<share_set_id>" \
  -H "Authorization: Bearer <user_jwt>"
```

## Add or update a rule

For a declarative rule, the rule goes in `declarativeRule` instead of `rule`, and `ruleId` is optional: leave it out to create a rule, and the response returns the generated id. In the JavaScript SDK examples, `walletAccount` is the WaaS wallet account, for example one returned by `getWalletAccounts()`.

**Legacy rules (cURL)**

```bash theme={"system"}
curl -X PATCH \
"https://app.dynamicauth.com/api/v0/sdk/<environment_id>/waas/<wallet_id>/signers/self/policy-layer" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <user_jwt>" \
-d '{
  "op": "upsert",
  "rule": {
    "name": "Deny transfers to flagged address",
    "ruleType": "deny",
    "chain": "EVM",
    "chainIds": [1, 8453],
    "addresses": ["0x..."]
  },
  "shareSetId": "<share_set_id>"
}'
```

**Legacy rules (JavaScript SDK)**

```javascript theme={"system"}
import { createPolicy } from '@dynamic-labs-sdk/client/waas';
import { WaasChainEnum } from '@dynamic-labs/sdk-api-core';

const walletId = '<wallet_id>';
const shareSetId = '<share_set_id>';

const layer = await createPolicy({
  scope: { walletId, shareSetId },
  chain: WaasChainEnum.Evm,
  chainIds: [1, 8453],
  rules: { denyAddresses: ['0x...'] },
});
```

**Declarative rules (cURL)**

```bash theme={"system"}
curl -X PATCH \
"https://app.dynamicauth.com/api/v0/sdk/<environment_id>/waas/<wallet_id>/signers/self/policy-layer" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <user_jwt>" \
-d '{
  "op": "upsert",
  "declarativeRule": {
    "ruleId": "00000000-0000-4000-8000-000000000004",
    "name": "Blocked address",
    "phase": "transaction",
    "then": "DENY",
    "when": {
      "exists": {
        "in": "counterparties",
        "where": { "field": "address", "op": "in", "value": ["0x0000000000000000000000000000000000000bad"] }
      }
    }
  },
  "shareSetId": "<share_set_id>"
}'
```

**Declarative rules (JavaScript SDK)**

```javascript theme={"system"}
import { upsertSignerPolicyRule } from '@dynamic-labs-sdk/client/waas';
import {
  WaasPolicyAction,
  WaasPolicyDeclarativeRuleInputPhaseEnum,
} from '@dynamic-labs/sdk-api-core';

const shareSetId = '<share_set_id>';

const layer = await upsertSignerPolicyRule({
  walletAccount,
  shareSetId,
  rule: {
    ruleId: '00000000-0000-4000-8000-000000000004',
    name: 'Blocked address',
    phase: WaasPolicyDeclarativeRuleInputPhaseEnum.Transaction,
    then: WaasPolicyAction.Deny,
    when: {
      exists: {
        in: 'counterparties',
        where: { field: 'address', op: 'in', value: ['0x0000000000000000000000000000000000000bad'] },
      },
    },
  },
});
```

## Remove a rule

```bash theme={"system"}
curl -X PATCH \
"https://app.dynamicauth.com/api/v0/sdk/<environment_id>/waas/<wallet_id>/signers/self/policy-layer" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <user_jwt>" \
-d '{
  "op": "remove",
  "ruleId": "<existing_rule_id>",
  "shareSetId": "<share_set_id>"
}'
```

The same request removes a legacy rule or a declarative rule. In the JavaScript SDK, use `removeSignerPolicyRule({ walletAccount, ruleId, shareSetId })`.

## Replace the layer's rules

`op: "replace"` sets the layer's full rule list in one write, so any rule you leave out is removed. Keep the `ruleId` of every rule you are keeping. The examples use declarative rules. Only environment admins and business-account admins can replace a layer.

**cURL**

```bash theme={"system"}
curl -X PATCH \
"https://app.dynamicauth.com/api/v0/sdk/<environment_id>/waas/<wallet_id>/signers/self/policy-layer" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <user_jwt>" \
-d '{
  "op": "replace",
  "rules": [
    {
      "ruleId": "00000000-0000-4000-8000-000000000004",
      "name": "Blocked address",
      "phase": "transaction",
      "then": "DENY",
      "when": {
        "exists": {
          "in": "counterparties",
          "where": { "field": "address", "op": "in", "value": ["0x0000000000000000000000000000000000000bad"] }
        }
      }
    },
    {
      "ruleId": "00000000-0000-4000-8000-000000000010",
      "name": "No client signing",
      "phase": "operation",
      "then": "DENY",
      "when": { "field": "operation.type", "op": "eq", "value": "clientSigning" }
    }
  ],
  "shareSetId": "<share_set_id>"
}'
```

**JavaScript SDK**

```javascript theme={"system"}
import { replacePolicyLayerRules } from '@dynamic-labs-sdk/client/waas';
import {
  WaasPolicyAction,
  WaasPolicyDeclarativeRuleInputPhaseEnum,
} from '@dynamic-labs/sdk-api-core';

const walletId = '<wallet_id>';
const shareSetId = '<share_set_id>';

const layer = await replacePolicyLayerRules({
  scope: { walletId, shareSetId },
  rules: [
    {
      ruleId: '00000000-0000-4000-8000-000000000004',
      name: 'Blocked address',
      phase: WaasPolicyDeclarativeRuleInputPhaseEnum.Transaction,
      then: WaasPolicyAction.Deny,
      when: {
        exists: {
          in: 'counterparties',
          where: { field: 'address', op: 'in', value: ['0x0000000000000000000000000000000000000bad'] },
        },
      },
    },
    {
      ruleId: '00000000-0000-4000-8000-000000000010',
      name: 'No client signing',
      phase: WaasPolicyDeclarativeRuleInputPhaseEnum.Operation,
      then: WaasPolicyAction.Deny,
      when: { field: 'operation.type', op: 'eq', value: 'clientSigning' },
    },
  ],
});
```

## Next steps

> **[Account layer](https://www.dynamic.xyz/docs/embedded-wallets/mpc/policies/account-layer.md)**
>
> Rules that apply to every wallet in a business account.

> **[Wallet layer](https://www.dynamic.xyz/docs/embedded-wallets/mpc/policies/wallet-layer.md)**
>
> Rules that apply to a single wallet.

> **[Managing layers](https://www.dynamic.xyz/docs/embedded-wallets/mpc/policies/managing-layers.md)**
>
> Auth, which kind of rules a layer holds, and signer-modifiable rules.

> **[Rule examples](https://www.dynamic.xyz/docs/embedded-wallets/mpc/policies/rule-examples.md)**
>
> Ready-made declarative rules.


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