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

# Custom Roles

> Define named roles that inherit a built-in role's authority, so members hold a role your product recognizes by name.

> [!NOTE]
>
> Business Accounts are in **early access**. See [Business Accounts](/docs/business-accounts/accounts.md) for the model.

A custom role is a name your account defines that inherits a built-in role's authority: `admin` or `viewer`, never `owner`. A member holding `cfo`, for example, has exactly what `admin` grants, but your product can show and reason about the distinct name instead of a generic `admin`. See [Members & roles](/docs/javascript/reference/business-accounts/members-and-roles.md) for the built-in roles a custom role builds on.

## Before you start

These calls act on behalf of the signed-in user, so create a Dynamic client with your environment ID first. See [Create a Dynamic client](/docs/javascript/reference/client/create-dynamic-client.md). The React examples also need the app wrapped in `DynamicProvider`, as in the [React quickstart](/docs/javascript/reference/react-quickstart.md).

The examples use two values from your account:

* `account` is the object that `createBusinessAccount` returns, so `account.id` is the `businessAccountId` that every call takes. [Create a business account](/docs/javascript/reference/business-accounts/create-a-business-account.md) shows how to create one and read it back.
* `userId` is the user ID of a member. `getBusinessAccount` returns the members of an account, and each member has a `userId`.

Defining, deleting and assigning roles require the owner or admin role. A holder of a custom role that inherits `admin` counts as an admin for these calls. Each call also needs step-up authentication for its scope: `business_account:role:define`, `business_account:role:delete` or `business_account:member:role:update`. See [Step-up authentication](/docs/javascript/reference/business-accounts/step-up-auth.md) for how to obtain the token.

## Define a role

`defineBusinessAccountRole` creates a custom role for the account. Owner or admin only.

**JavaScript**

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

await defineBusinessAccountRole({
  businessAccountId: account.id,
  role: 'cfo',
  inherits: 'admin',
});
```

**React**

```tsx theme={"system"}
import { useDefineBusinessAccountRole } from '@dynamic-labs-sdk/react-hooks';

const { mutate } = useDefineBusinessAccountRole();
mutate({ businessAccountId: account.id, role: 'cfo', inherits: 'admin' });
```

> [!NOTE]
>
> A role name is case-insensitive: `CFO` and `cfo` name the same role, and the account stores it lowercase. It cannot reuse a built-in name (`owner`, `admin`, `viewer`) or a name another live role in the account already uses.

On success, the call resolves to `{ version }`, the account's new version number. If the account requires approvals to define roles, the call can resolve to a pending approval instead, which means the change waits until enough approvers sign off. To tell the two outcomes apart, import `isBusinessAccountActionRequired` and check the result with it:

```javascript theme={"system"}
import {
  defineBusinessAccountRole,
  isBusinessAccountActionRequired,
} from '@dynamic-labs-sdk/client/waas';

const result = await defineBusinessAccountRole({
  businessAccountId: account.id,
  role: 'cfo',
  inherits: 'admin',
});

if (isBusinessAccountActionRequired(result)) {
  // The change is pending approval.
} else {
  // The role is defined. result.version is the account's new version.
}
```

[Governance](/docs/javascript/reference/business-accounts/governance.md) describes how approvals work. The call throws an `APIError` whose `status` is 409 for a name the account already uses, and 400 for a built-in name such as `admin`.

## List roles

`listBusinessAccountRoles` returns the account's custom roles, keyed by name.

**JavaScript**

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

const roles = await listBusinessAccountRoles({ businessAccountId: account.id });
// roles.cfo?.inherits === 'admin'
```

**React**

```tsx theme={"system"}
import { useListBusinessAccountRoles } from '@dynamic-labs-sdk/react-hooks';

const RoleList = ({ businessAccountId }: { businessAccountId: string }) => {
  const { data: roles, isLoading } = useListBusinessAccountRoles({ businessAccountId });
  if (isLoading) return null;
  return (
    <ul>
      {Object.entries(roles ?? {}).map(([name, def]) => (
        <li key={name}>{name} (inherits {def.inherits})</li>
      ))}
    </ul>
  );
};
```

An account with no custom roles returns an empty map. That is an answer, not a missing resource.

## Assign a role

A custom role's name is a valid `role` value anywhere a built-in role is: `addBusinessAccountMember` and `updateBusinessAccountMemberRole` both accept it. See [Members & roles](/docs/javascript/reference/business-accounts/members-and-roles.md) for both calls. Assigning a role your account has not defined fails, so define it first. If the account requires approvals to change member roles, the call can resolve to a pending approval instead. Check the result with `isBusinessAccountActionRequired`.

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

await updateBusinessAccountMemberRole({
  businessAccountId: account.id,
  userId,
  role: 'cfo',
});
```

To take a custom role away from a member, assign `admin` or `viewer` in the same way. Assigning `admin` can give the member more power than the custom role had, since `admin` can add and remove members and signers and link wallets, as [Members & roles](/docs/javascript/reference/business-accounts/members-and-roles.md) describes. The member then holds that built-in role, and its `customRole` field reads `null`.

## Read a member's custom role

A member's `role` on `detail.members` is always the built-in it inherits, so a `cfo` holder still reads `admin` there. The member's `customRole` field carries the actual name and what it inherits.

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

const detail = await getBusinessAccount({ businessAccountId: account.id });
const member = detail.members.find((m) => m.userId === userId);
// member.role === 'admin'
// member.customRole === { name: 'cfo', inherits: 'admin' }
```

`customRole` is `null` for a member holding a built-in role directly.

## Delete a role

`deleteBusinessAccountRole` removes the definition. Owner or admin only. The SDK has no call that renames a role or changes what it inherits.

**JavaScript**

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

await deleteBusinessAccountRole({ businessAccountId: account.id, role: 'cfo' });
```

**React**

```tsx theme={"system"}
import { useDeleteBusinessAccountRole } from '@dynamic-labs-sdk/react-hooks';

const { mutate } = useDeleteBusinessAccountRole();
mutate({ businessAccountId: account.id, role: 'cfo' });
```

> [!WARNING]
>
> Deleting a role does not remove its holders or their authority: each one keeps reading the role's inherited base (`admin` or `viewer`), only the distinct name goes away. Deletion is refused while a governance requirement still names the role. Remove it from governance first.

A refused delete fails with a 409 `APIError`. To find the requirement that names the role, call `getBusinessAccountGovernance` and check each rule for the role name in `eligibleApproverRoles`, `eligibleInitiatorRoles` and `mandatoryApproverRoles`, and in the `role` or `newRole` field of its `when` condition. The `when` condition limits a requirement to the changes that match its fields, as [Scope a requirement with `when`](/docs/javascript/reference/business-accounts/governance#scope-a-requirement-with-when) explains.

On success, the call resolves to `{ version }`. If the account requires approvals to delete roles, the call can resolve to a pending approval instead, which `isBusinessAccountActionRequired` identifies.

## Governance

Defining, deleting and assigning a role are governed actions, under the keys `defineRole`, `deleteRole` and `updateMemberRole`. An account can attach a requirement to any of them. A requirement sets how many approvals a change needs and who may approve or propose it, and its `eligibleApproverRoles` or `eligibleInitiatorRoles` can name a custom role alongside the built-in ones. A requirement has these fields, among others:

* `id` names the requirement.
* `schemaVersion` is the requirement format version. The examples use `1`.
* `requiredApprovals` is the number of approvals the change needs.
* `eligibleApproverRoles` lists the roles that may approve the change.
* `eligibleInitiatorRoles` lists the roles that may propose it.
* `mandatoryApproverRoles` lists roles that must each approve the change.
* `when` limits the requirement to matching changes, for example by `role` or `newRole`.

A requirement on any governed action, not only the role actions, can name a custom role. This requirement asks for one approval from a `cfo` holder whenever a member is added:

```javascript theme={"system"}
import {
  getBusinessAccountGovernance,
  setBusinessAccountGovernance,
} from '@dynamic-labs-sdk/client/waas';

const current = await getBusinessAccountGovernance({
  businessAccountId: account.id,
});

await setBusinessAccountGovernance({
  businessAccountId: account.id,
  governance: {
    ...current,
    addMember: [
      {
        id: 'cfo-signoff',
        schemaVersion: 1,
        requiredApprovals: 1,
        eligibleApproverRoles: ['cfo'],
      },
    ],
  },
});
```

The custom role must already exist when the requirement is set, and a role cannot be deleted while a requirement still names it. See [Governance](/docs/javascript/reference/business-accounts/governance.md) for the full requirement fields and [Approvals](/docs/business-accounts/approvals.md) for the model and how a signed change becomes a proposal.

## Next steps

> **[Members & roles](/docs/javascript/reference/business-accounts/members-and-roles.md)**
>
> Add and remove members, and change who holds a role.

> **[Governance](/docs/javascript/reference/business-accounts/governance.md)**
>
> Require approvals before a role is defined, deleted, or assigned.


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