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

# Quickstart

> Make your first authenticated request and create a transaction.

## Get started by creating a transaction

Creating a transaction means assembling a transfer or contract call, submitting
it for policy checks, and collecting the required signatures. The SDK helps you propose
the transaction, tracks approvals, and executes once thresholds are met.

This quickstart walks through getting accounts, selecting a policy, and
creating a transaction proposal using the SDK.

<Warning>
  We firmly recommend using the SDK instead of calling the API directly. The SDK performs
  local policy validation, which adds an additional layer of security to every account transaction.
</Warning>

## 1) Install the SDK

Install [`@onchainden/mls-sdk-ts`](https://www.npmjs.com/package/@onchainden/mls-sdk-ts) using your package manager of choice.

<CodeGroup>
  ```bash npm theme={null}
  npm install @onchainden/mls-sdk-ts
  ```

  ```bash pnpm theme={null}
  pnpm add @onchainden/mls-sdk-ts
  ```

  ```bash yarn theme={null}
  yarn add @onchainden/mls-sdk-ts
  ```

  ```bash bun theme={null}
  bun add @onchainden/mls-sdk-ts
  ```
</CodeGroup>

## 2) Set up the client and signer

Initialize the SDK client and the signer that will submit approvals. You'll need an API key created by an admin in the Den dashboard, and your signer wallet must be added to the organization as a member to propose, sign, and execute transactions.

See [Setting up an API member](/api-reference/api-member-setup) for the full setup process.

```typescript theme={null}
import { DenClient } from "@onchainden/mls-sdk-ts";
import { privateKeyToAccount } from "viem/accounts";
import { encodeAbiParameters, type Hex } from "viem";

const signer = privateKeyToAccount("0xYOUR_PRIVATE_KEY");

const client = new DenClient({
  apiKey: process.env.DEN_API_KEY!,
  baseUrl: process.env.DEN_API_BASE_URL!,
});
```

## 3) List accounts

Fetch available accounts so you can choose a transaction source.

```typescript theme={null}
const { data: accounts } = await client.getAccounts();
const accountId = accounts[0].id;
```

## 4) List policies

Load policies and select one that matches the transaction you want to create.

```typescript theme={null}
const { data: policies } = await client.getPolicies();
const policyId = policies[0].id;
```

## 5) Create a transaction proposal

Create a proposal with the selected account, policy, and transaction details.

```typescript theme={null}
const { data: queued } = await client.createTransaction({
  accountId,
  initiatorWalletAddress: signer.address,
  networkId: 1,
  policyId,
  description: "Payment to vendor",
  to: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
  value: "0",
  data: "0xa9059cbb...",
});
```

The initial response includes a `signatureData` object with the initiator
signing payload. After the initiator signs, transaction responses use the same
`signatureData` object to expose the approve and reject payloads.

## 6) Sign as the initiator

Sign the initiator payload hash to authorize the transaction, then submit the ABI-encoded signer address and signature.

```typescript theme={null}
const initiatorSig = encodeAbiParameters(
  [
    { type: "address", name: "address" },
    { type: "bytes", name: "signature" },
  ],
  [
    signer.address,
    await signer.sign({
      hash: queued.signatureData.initiatorPayload as Hex,
    }),
  ]
);

const { data: updatedTransaction } = await client.signTransaction(queued.id, {
  type: "initiator",
  signature: initiatorSig,
});
```

For `MANUAL_APPROVAL` policies, approvers then submit their approve or reject
signatures.

## 7) Execute once threshold is met

Execute the transaction after policy approvals are satisfied.

```typescript theme={null}
if (updatedTransaction.signatureData.status === "approvalReady") {
  const { data: executed } = await client.executeTransaction(queued.id, {
    type: "approve",
  });
  console.log(executed.executionStatus);
}
```

Once the approval threshold is met, the transaction is submitted on-chain. The
returned transaction's `executionStatus` reflects its current state (for
example, `"processing"` while the on-chain submission is pending).

## Production signing setups

The `privateKeyToAccount` example in this guide is for local development. Here are some example patterns you can use in production. See [Signing keys](/api-reference/core-concepts#signing-keys) to learn more.

<Accordion title="Example patterns">
  **Cloud KMS**

  ```typescript theme={null}
  import { toAccount } from "viem/accounts";
  import { KMSClient, SignCommand } from "@aws-sdk/client-kms";

  const kms = new KMSClient({ region: "us-east-1" });

  const signer = toAccount({
    address: "0xYOUR_WALLET_ADDRESS",
    sign: async ({ hash }) => {
      const res = await kms.send(new SignCommand({ Message: hash, ... }));
      return res.Signature;
    },
  });
  ```

  **HSM**

  ```typescript theme={null}
  import { toAccount } from "viem/accounts";

  const signer = toAccount({
    address: "0xYOUR_WALLET_ADDRESS",
    sign: async ({ hash }) => {
      return await hsmClient.signDigest(hash);
    },
  });
  ```

  **Environment variable**

  ```typescript theme={null}
  import { privateKeyToAccount } from "viem/accounts";

  const signer = privateKeyToAccount(process.env.SIGNER_PRIVATE_KEY as `0x${string}`);
  ```
</Accordion>

## Full example

```typescript theme={null}
import { DenClient } from "@onchainden/mls-sdk-ts";
import { privateKeyToAccount } from "viem/accounts";
import { encodeAbiParameters, type Hex } from "viem";

// 2) Set up the client and signer.
const signer = privateKeyToAccount("0xYOUR_PRIVATE_KEY");

const client = new DenClient({
  apiKey: process.env.DEN_API_KEY!,
  baseUrl: process.env.DEN_API_BASE_URL!,
});

// 3) List accounts.
const { data: accounts } = await client.getAccounts();
const accountId = accounts[0].id;

// 4) List policies.
const { data: policies } = await client.getPolicies();
const policyId = policies[0].id;

// 5) Create a transaction proposal.
const { data: queued } = await client.createTransaction({
  accountId,
  initiatorWalletAddress: signer.address,
  networkId: 1,
  policyId,
  description: "Payment to vendor",
  to: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
  value: "0",
  data: "0xa9059cbb...",
});

// 6) Sign as the initiator.
const initiatorSig = encodeAbiParameters(
  [
    { type: "address", name: "address" },
    { type: "bytes", name: "signature" },
  ],
  [
    signer.address,
    await signer.sign({
      hash: queued.signatureData.initiatorPayload as Hex,
    }),
  ]
);

const { data: updatedTransaction } = await client.signTransaction(queued.id, {
  type: "initiator",
  signature: initiatorSig,
});

// 7) Execute once threshold is met.
if (updatedTransaction.signatureData.status === "approvalReady") {
  const { data: executed } = await client.executeTransaction(queued.id, {
    type: "approve",
  });
  console.log(executed.executionStatus);
}
```
