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

# Managing Policy Rules

> Create, list, and deactivate the org allowlist rules that drive policy decisions.

Allowlist rules are the deny-by-default constraints the policy decision point evaluates before any share is released. You manage them over the admin API at `/v1/admin/allowlist-rules`. Rules are org-bound, so a key only ever sees and changes its own organization's rules.

<Note>
  Admin API availability is per-environment. Confirm the endpoint is exposed for your environment before you wire it into an onboarding flow.
</Note>

## How rules gate signing

Creating the first active rule flips the org to deny-by-default; deactivating the last flips it back to allowlist-passes. See [Policy Gates](/core-concepts/policy-gates#how-rules-work).

Every operation is typed into one operation type, and a rule targets exactly one. See [Policy Gates → Typed Operations](/core-concepts/policy-gates#typed-operations) for the full list and selectors.

## Rule anatomy

See [Policy Gates → Rule Anatomy](/core-concepts/policy-gates#rule-anatomy) for field semantics. Admin-API specifics: `chain` synonyms are canonicalized (a rule written as `bsc` matches intents that resolve to `smartchain`); `amountCap` is a decimal string in base units; `expiresAt` is a required RFC 3339 timestamp in the future. Native-transfer rules must omit `contractAddress` and `methodSelector`; every other operation type must carry both, or the create is rejected as a structured 400.

## Create a rule

`POST /v1/admin/allowlist-rules`. The example below admits ERC-20 transfers of a specific token, to a pinned destination, capped at 1,000,000,000 base units (1000 USDC at 6 decimals), expiring at a fixed time.

<CodeGroup>
  ```json Request theme={null}
  {
    "chain": "ethereum",
    "operationType": "erc20_transfer",
    "contractAddress": "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48",
    "methodSelector": "0xa9059cbb",
    "destination": "0x1111111111111111111111111111111111111111",
    "amountCap": "1000000000",
    "expiresAt": "2027-01-01T00:00:00Z"
  }
  ```

  ```json Response theme={null}
  {
    "success": true,
    "data": {
      "id": "2f6d3a1e-94c7-4c8e-9c5a-7b0e1f2a3b4c",
      "chain": "ethereum",
      "operationType": "erc20_transfer",
      "contractAddress": "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48",
      "methodSelector": "0xa9059cbb",
      "destination": "0x1111111111111111111111111111111111111111",
      "amountCap": "1000000000",
      "expiresAt": "2027-01-01T00:00:00Z",
      "active": true,
      "createdAt": "2026-07-13T12:00:00Z",
      "createdBy": "key_..."
    }
  }
  ```
</CodeGroup>

A native-transfer rule omits the contract fields.

<CodeGroup>
  ```json Request theme={null}
  {
    "chain": "ethereum",
    "operationType": "native_transfer",
    "destination": "0x1111111111111111111111111111111111111111",
    "amountCap": "500000000000000000",
    "expiresAt": "2027-01-01T00:00:00Z"
  }
  ```

  ```json Response theme={null}
  {
    "success": true,
    "data": {
      "id": "7b0e1f2a-3b4c-4c8e-9c5a-2f6d3a1e94c7",
      "chain": "ethereum",
      "operationType": "native_transfer",
      "destination": "0x1111111111111111111111111111111111111111",
      "amountCap": "500000000000000000",
      "expiresAt": "2027-01-01T00:00:00Z",
      "active": true,
      "createdAt": "2026-07-13T12:00:00Z",
      "createdBy": "key_..."
    }
  }
  ```
</CodeGroup>

Match fields are trimmed and lowercased before storage — see [Policy Gates → Rule Anatomy](/core-concepts/policy-gates#rule-anatomy).

## List rules

`GET /v1/admin/allowlist-rules`. Returns the org's rules.

<CodeGroup>
  ```json Response theme={null}
  {
    "success": true,
    "data": [
      {
        "id": "2f6d3a1e-94c7-4c8e-9c5a-7b0e1f2a3b4c",
        "chain": "ethereum",
        "operationType": "erc20_transfer",
        "contractAddress": "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48",
        "methodSelector": "0xa9059cbb",
        "destination": "0x1111111111111111111111111111111111111111",
        "amountCap": "1000000000",
        "expiresAt": "2027-01-01T00:00:00Z",
        "active": true,
        "createdAt": "2026-07-13T12:00:00Z",
        "createdBy": "key_..."
      }
    ]
  }
  ```
</CodeGroup>

## Deactivate a rule

`DELETE /v1/admin/allowlist-rules/{id}`. Deactivation is org-bound. Another org's rule id, or an already-inactive one, uniformly reads as not found, so the endpoint never leaks whether a rule exists in another organization.

<CodeGroup>
  ```json Response theme={null}
  {
    "success": true,
    "data": null
  }
  ```
</CodeGroup>

## Every change is audited

Each create and each deactivation writes a **full snapshot** of the rule to the audit log, traceable to the key that made the change. The snapshot is written on deactivation too, so the record of what stopped being enforceable survives even after the rule is gone. Rule changes decide what the cosigner will sign, so they are recorded to be traceable and alertable. See [Policy Decisions](/core-concepts/policy-decisions) for how the decisions those rules drive are recorded.

## Related

<CardGroup cols={2}>
  <Card title="Policy Decisions" icon="clipboard-check" href="/core-concepts/policy-decisions">
    How each evaluated intent is stamped and recorded, including denials.
  </Card>

  <Card title="Policy Gates" icon="shield-check" href="/core-concepts/policy-gates">
    The deny-by-default rule model these rules feed.
  </Card>

  <Card title="Signing Intents" icon="ticket" href="/mpc/signing-intents">
    How a cleared decision unlocks one signature.
  </Card>

  <Card title="Structured Errors" icon="message-exclamation" href="/core-concepts/structured-errors">
    The error a rejected rule or a denied intent returns.
  </Card>
</CardGroup>
