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

# Migrate to SDK V2

> Update vault, contact, and transfer calls for @primevault/js-api-sdk V2. V2 adds groups and makes approval an explicit SDK call.

SDK V2 changes how you create vaults, contacts, and transfers. Each create or update call opens a change request. Your API user must approve the change request before the change takes effect. This page lists the changes and shows the new call sequence.

## Install V2

> [!WARNING]
>
> The `latest` npm tag points to a 1.x release. A plain `npm install @primevault/js-api-sdk` installs V1. Pin the major version.

```bash theme={null}
npm install @primevault/js-api-sdk@^2
```

V2 requires Node.js 20 or later. Client setup does not change. See [Authentication](https://docs.primevault.com/api-basics/authentication.md).

## What changed

| Area | Change in V2 | What to do |
| :- | :- | :- |
| Vaults | `templateId` is not required. | Remove `templateId` from the `createVault` payload. |
| Vaults | A vault can join vault groups. | Pass `vaultGroupIds` in `createVault`. |
| Vaults | Vault creation needs approval. | Call `createVaultApproval` after `createVault`. |
| Contacts | A contact can join contact groups. | Pass `contactGroupIds` in `createContact` or `updateContact`. |
| Contacts | Contact create and update need approval. | Call `createContactApproval` after `createContact` or `updateContact`. |
| Transfers | A new transfer stays `PENDING` until approved. | Call `approveChangeRequest` with the transaction `id`. |

## How approval works

Each approval call gets the approval message for the entity. The SDK signs the message with the key in your `APIClient`. The SDK then submits the signed approval. See [Approvals](https://docs.primevault.com/approvals/overview.md) for the REST flow.

Use two separate calls: create, then approve. Store the entity `id` after the create call. If the approval call fails, retry the approval only. Do not repeat the create call, or you create a duplicate.

## Vaults

**1. Create the vault**

Pass `vaultName`, `chains`, and `vaultGroupIds`. Do not pass `templateId`.

```typescript theme={null}
const vault = await apiClient.createVault({
  vaultName: "Treasury Vault",
  chains: ["ETHEREUM", "POLYGON"],
  vaultGroupIds: ["vault_group_123"],
});
```

Copy the vault group IDs from the PrimeVault web app. The SDK has no method to list vault groups.

**2. Approve the vault**

```typescript theme={null}
await apiClient.createVaultApproval(vault);
```

`createVaultApproval` approves the change request and returns the vault.

**3. Wait for wallet addresses**

Wallet generation is asynchronous. Retrieve the vault until `walletsGenerated` is `true`. Then read the addresses from `wallets`.

```typescript theme={null}
async function waitForWallets(vaultId: string, attempts = 30, delayMs = 2000) {
  for (let i = 0; i < attempts; i++) {
    const current = await apiClient.getVaultById(vaultId);
    if (current.walletsGenerated) return current;
    await new Promise((resolve) => setTimeout(resolve, delayMs));
  }
  throw new Error(`Wallets not generated for vault ${vaultId}`);
}

const ready = await waitForWallets(vault.id);
ready.wallets.forEach((w) => console.log(w.blockchain, w.address));
```

The attempt count and delay are example values. Tune them for your workload.

## Contacts

### Create a contact

```typescript theme={null}
const contact = await apiClient.createContact({
  name: "Acme Payouts",
  address: "0x1111111111111111111111111111111111111111",
  chain: "ETHEREUM",
  assetList: ["USDC"],
  contactGroupIds: ["contact_group_123"],
});

const approved = await apiClient.createContactApproval(contact);
```

A new contact has `status` set to `PENDING`. Use the contact in a transfer only after `status` is `APPROVED`.

### Update contact groups

`updateContact` changes `assetList` and `contactGroupIds` only. It cannot change `name` or `address`.

```typescript theme={null}
const current = await apiClient.getContactById("contact_123");

const updated = await apiClient.updateContact({
  id: current.id,
  assetList: current.assetList ?? [],
  contactGroupIds: ["contact_group_123", "contact_group_456"],
});

await apiClient.createContactApproval(updated);
```

> [!WARNING]
>
> `updateContact` sends `assetList: []` when you omit `assetList`. Always pass the current `assetList` of the contact to keep it.

Pass the full list of group IDs that the contact must belong to.

## Transfers

**1. Create the transfer**

`source` and `destination` are party objects with a `type` and an `id`. `amount` is a decimal string.

```typescript theme={null}
import { ApprovalAction, TransactionStatus } from "@primevault/js-api-sdk";

const txn = await apiClient.createTransferTransaction({
  source: { type: "VAULT", id: "vault_123" },
  destination: { type: "CONTACT", id: "contact_123" },
  amount: "12.50",
  asset: "USDC",
  chain: "ETHEREUM",
  externalId: "payout_789",
});
```

**2. Approve the transfer**

```typescript theme={null}
if (txn.status === TransactionStatus.PENDING) {
  await apiClient.approveChangeRequest({
    entityId: txn.id,
    action: ApprovalAction.APPROVE,
  });
}
```

Your policy can approve a transfer on create. In that case the status is not `PENDING`. Skip the approval call.

**3. Track the transfer**

`approveChangeRequest` returns the approval result, not the transaction. Call `getTransactionById(txn.id)` or listen for [webhooks](https://docs.primevault.com/api-basics/webhooks.md). Stop when `status` is `COMPLETED`, `FAILED`, or `DECLINED`.

## One-call helpers

V2 also has helpers that create and approve in one call. Use them in scripts and tests. In production, prefer two calls so a retry does not create a duplicate.

| Two calls | One call |
| :- | :- |
| `createVault` + `createVaultApproval` | `createVaultWithApproval` |
| `createContact` + `createContactApproval` | `createContactWithApproval` |
| `updateContact` + `createContactApproval` | `updateContactWithApproval` |
| `createTransferTransaction` + `approveChangeRequest` | `createTransactionWithApproval` |

## Other V1 methods that changed

| V1 | V2 |
| :- | :- |
| `submitContactApprovalAction(entityId)` | `createContactApproval(contact)` |
| `submitBankAccountApprovalAction(entityId)` | `createBankAccountApproval(bankAccount)` |
| `ApprovalAction.DECLINE` sends `"decline"` | `ApprovalAction.REJECT` sends `"reject"`. `DECLINE` is an alias for `"reject"`. |
| `getTradeQuote`, `getRampQuote` | `getQuote` |
| `createTradeTransaction`, `createOnRampTransaction`, `createOffRampTransaction` | `createTransactionFromIntent` |
| `updateBalances` | Removed. |
| `isAutomation`, `executeAt` on transfer requests | Removed. |

## Next steps

> [card] [Approvals](https://docs.primevault.com/approvals/overview.md)
>
> Approval messages, signing, and the REST approval flow.

> [card] [Create a Transfer](https://docs.primevault.com/transactions/on-chain-operations/create-transfer.md)
>
> Transfer fields, fee payer, and lifecycle.


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