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

# Business Accounts API

> Manage Business Accounts, members, wallets, signers, and permissions with the REST API.

> [!NOTE]
>
> Business Accounts are in early access. Contact your Dynamic account team to
> enable them for your environment.

The Business Accounts REST API lets an authenticated end user manage
team-owned embedded wallets, members, roles, signers, and account policy rules.
You can call non-ceremony operations directly over HTTP.

## Before you start

You need:

* Your Dynamic environment ID.
* A valid user JWT issued for that environment.
* The API base URL, `https://app.dynamic.xyz/api/v0`.
* An elevated access token for sensitive operations when [step-up authentication](https://www.dynamic.xyz/docs/overview/authentication/step-up-auth.md) is enforced.

> [!WARNING]
>
> Do not use a Dashboard API token that starts with `dyn_` for these
> `/sdk/...` routes. Business Accounts endpoints act as the authenticated end
> user and require that user's JWT.

Send the JWT as a bearer token:

```bash theme={"system"}
curl "https://app.dynamic.xyz/api/v0/sdk/ENVIRONMENT_ID/businessAccounts" \
  --header "Authorization: Bearer USER_JWT"
```

**environmentId** `string` *required*

The environment that contains the user and Business Account.

**businessAccountId** `string`

The Business Account to manage. Operations that create or list accounts do
not use this parameter.

## Understand the access model

A Business Account owns one or more embedded wallets. Members administer the
account. Signers can sign with a specific wallet. Membership does not grant
signing access, and signing access does not grant administrative permissions.

Member roles are:

* `owner`: Has full administrative access and can transfer ownership. Each
  Business Account has exactly one owner.
* `admin`: Can update the account, add members, and manage wallets and signers.
* `viewer`: Has read-only access.

Signer types are:

* `endUser`: A Dynamic user who holds a client key share for the wallet.
* `server`: A server-controlled signer.

## Account operations

These operations can be called directly over HTTP:

| Operation | Method and path | Required role | Elevated scope |
| - | - | - | - |
| Create an account | `POST /sdk/{environmentId}/businessAccounts` | Authenticated user, who becomes the owner | None |
| List accounts | `GET /sdk/{environmentId}/businessAccounts` | Returns accounts where the caller is a member | None |
| Get account details | `GET /sdk/{environmentId}/businessAccounts/{businessAccountId}` | Any member | None |
| Update the account name | `PATCH /sdk/{environmentId}/businessAccounts/{businessAccountId}` | Owner or admin | None |
| Get account policy rules | `GET /sdk/{environmentId}/businessAccounts/{businessAccountId}/policy-layer` | Owner or admin | None |
| Update account policy rules | `PATCH /sdk/{environmentId}/businessAccounts/{businessAccountId}/policy-layer` | Owner or admin | None |

The account detail response includes the account's members, wallets, and
signers. A request from a user who is not a member returns `404` instead of
revealing that the Business Account exists.

See the generated Business Accounts endpoint pages in the API Reference for
request and response schemas.

## Member operations

Adding or changing a member updates the permissions represented in that
user's JWT.

| Operation | Method and path | Required role | Elevated scope |
| - | - | - | - |
| Add a member | `POST /sdk/{environmentId}/businessAccounts/{businessAccountId}/members` | Owner or admin | `business_account:member:add` |
| Change a member role | `PATCH /sdk/{environmentId}/businessAccounts/{businessAccountId}/members/{userId}` | Owner | `business_account:member:role:update` |
| Remove a member | `DELETE /sdk/{environmentId}/businessAccounts/{businessAccountId}/members/{userId}` | Owner | `business_account:member:remove` |
| Transfer ownership | `POST /sdk/{environmentId}/businessAccounts/{businessAccountId}/transferOwnership` | Owner | `business_account:transfer_ownership` |

You can add members as `admin` or `viewer`. Use the ownership transfer
operation to assign the `owner` role. The target of an ownership transfer must
already be a member.

Removing a member also removes that user's signer access across the Business
Account. The request returns `409` if the removal would leave a wallet without
an active signer.

## Wallet and signer operations

You can link an existing user wallet without starting a new MPC ceremony. The
link operation transfers the wallet to the Business Account and registers the
caller as an `endUser` signer using the wallet's existing key share.

| Operation | Method and path | Required role | Elevated scope | Direct HTTP |
| - | - | - | - | - |
| Link an existing wallet | `POST /sdk/{environmentId}/businessAccounts/linkWallet` | Owner or admin | `business_account:link_wallet` | Yes |
| Remove a wallet | `DELETE /sdk/{environmentId}/businessAccounts/{businessAccountId}/wallets/{walletId}` | Owner or admin | `business_account:wallet:remove` | Yes |
| Add a signer | `POST /sdk/{environmentId}/businessAccounts/{businessAccountId}/wallets/{walletId}/signers` | Owner or admin who is an active signer on the wallet | `business_account:signer:add` | No |
| Remove a signer | `DELETE /sdk/{environmentId}/businessAccounts/{businessAccountId}/wallets/{walletId}/signers/{signerId}` | Owner or admin | `business_account:signer:remove` | Yes |

Removing a wallet also removes its signer and key-share records. Removing a
signer does not start a reshare ceremony and does not affect the remaining
signers. A signer removal returns `409` if it would leave the wallet without an
active signer.

> [!WARNING]
>
> Use the Business Accounts helpers in the JavaScript SDK to create a new
> Business Account wallet or add a signer. These operations run client-side
> MPC ceremonies and persist client key shares. Calling the low-level endpoint
> alone does not complete the flow.

## Send elevated access tokens

When step-up authentication is enforced for your environment, the sensitive
operations in the tables above require an elevated access token with the exact
scope shown.

Obtain the token by re-verifying the user through a supported step-up method.
Then send both tokens:

```http theme={"system"}
Authorization: Bearer USER_JWT
x-dyn-elevated-access-token: ELEVATED_ACCESS_TOKEN
```

The API returns `403` if the elevated token is missing, expired, belongs to a
different user, or does not contain the required scope. The
`business_account:transfer_ownership` scope is single-use. Do not reuse that
token for another ownership transfer.

## Refresh stale permissions

Business Account permissions are included in the user JWT. A membership,
role, wallet, or signer change can make an existing JWT stale. Most Business
Accounts management routes check this freshness. A stale request returns:

```http theme={"system"}
HTTP/1.1 403 Forbidden
Content-Type: application/json
```

```json theme={"system"}
{
  "code": "rbac_stale",
  "error": "RBAC permissions are stale"
}
```

Handle this response as follows:

**1. Refresh the user JWT**

Call `POST /sdk/{environmentId}/refresh` with the current user JWT and the
registered device's `x-dynamic-device-nonce`,
`x-dynamic-device-publickey`, and `x-dynamic-device-signed-nonce` headers.
Cookie-enabled sessions do not send these headers. The response contains a
new JWT with current permissions. The generated "Refresh the JWT auth
token" endpoint page documents the response schema. If refresh fails,
authenticate the user again and do not retry the original request.

**2. Replace the bearer token**

Use the new JWT for later requests. Do not continue using the stale JWT.

**3. Reauthorize sensitive operations**

Attach a valid elevated access token again if the original request required
one. If the operation consumed a single-use elevated or MFA proof, repeat
the step-up flow instead of replaying the previous proof.

**4. Retry once**

Retry only a non-ceremony operation, and retry it at most once. Return the
error to the application if the retry also returns `rbac_stale`.

The JavaScript SDK version `1.30.3` and later detects this response, refreshes
the JWT, and retries safe requests once. If a wallet operation has already
consumed a one-time authorization, the SDK requires the user to reauthorize
before the application retries.

> [!WARNING]
>
> Ownership transfer is different. Dynamic revokes the former owner's
> session after a successful transfer. The former owner must sign in again
> instead of refreshing the previous session.

## Handle common errors

| Status | Meaning | Recovery |
| - | - | - |
| `401` | The user JWT is missing, invalid, expired, or revoked. | Authenticate the user again. |
| `403` | The caller lacks permission, step-up is required, or JWT permissions are stale. | Inspect the response body. Complete step-up or follow the stale-permissions flow above. |
| `404` | The resource is absent or not visible to the caller. | Verify the environment, Business Account, user, wallet, or signer ID. |
| `409` | The operation violates a Business Account invariant. | Preserve at least one active signer on every wallet and do not remove the owner. |


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