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.
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.
1) Set up the client and signer
Initialize the SDK client and the signer that will submit approvals.
import { DenClient } from "@den/sdk";
import { privateKeyToAccount } from "viem/accounts";
const signer = privateKeyToAccount("0xYOUR_PRIVATE_KEY");
const client = new DenClient({
apiKey: "ck_live_...",
rpcProviders: {
1: "https://eth-mainnet.g.alchemy.com/v2/YOUR_KEY",
},
});
2) List accounts
Fetch available accounts so you can choose a transaction source.
const accounts = await client.getAccounts({ limit: 20 });
const accountId = accounts.data[0].id;
3) List policies
Load policies and select one that matches the transaction you want to create.
const policies = await client.getPolicies();
const policyId = policies[0].id;
4) Create a transaction proposal
Create a proposal with the selected account, policy, and transaction details.
const queued = await client.createTransaction(accountId, {
networkId: 1,
policyId,
description: "Payment to vendor",
to: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
value: "0",
data: "0xa9059cbb...",
});
The response includes signingPayloads for the initiator and approvers.
5) Sign as the initiator
Sign the initiator payload to register your approval.
const initiatorSig = await signer.signMessage({
message: queued.signingPayloads.initiatorPayload,
});
const afterInitiator = await client.signTransaction(accountId, queued.id, {
type: "initiator",
signature: initiatorSig,
});
For MANUAL_APPROVAL policies, approvers then submit their approve or reject
signatures.
6) Execute once threshold is met
Execute the transaction after policy approvals are satisfied.
if (afterInitiator.signatureStatus === "approvalReady") {
const executed = await client.executeTransaction(accountId, queued.id, {
type: "approve",
});
console.log(executed.result?.transactionHash);
}
If the approval threshold is met, the transaction is submitted on-chain and the
response includes a transactionHash.
Full example
import { DenClient } from "@den/sdk";
import { privateKeyToAccount } from "viem/accounts";
// 1) Set up the client and signer.
const signer = privateKeyToAccount("0xYOUR_PRIVATE_KEY");
const client = new DenClient({
apiKey: "ck_live_...",
rpcProviders: {
1: "https://eth-mainnet.g.alchemy.com/v2/YOUR_KEY",
},
});
// 2) List accounts.
const accounts = await client.getAccounts({ limit: 20 });
const accountId = accounts.data[0].id;
// 3) List policies.
const policies = await client.getPolicies();
const policyId = policies[0].id;
// 4) Create a transaction proposal.
const queued = await client.createTransaction(accountId, {
networkId: 1,
policyId,
description: "Payment to vendor",
to: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
value: "0",
data: "0xa9059cbb...",
});
// 5) Sign as the initiator.
const initiatorSig = await signer.signMessage({
message: queued.signingPayloads.initiatorPayload,
});
const afterInitiator = await client.signTransaction(accountId, queued.id, {
type: "initiator",
signature: initiatorSig,
});
// 6) Execute once threshold is met.
if (afterInitiator.signatureStatus === "approvalReady") {
const executed = await client.executeTransaction(accountId, queued.id, {
type: "approve",
});
console.log(executed.result?.transactionHash);
}