Skip to main content

What is an API member?

API members are wallets that sign on behalf of automated systems. Setting one up requires registration and admin approval. Before requesting a registration payload, an admin must invite the API member through the GUI so the wallet is eligible to register.

Prerequisites

Before an API member can register, an admin must complete two steps in the Den dashboard:
  1. Create an API key - Navigate to Settings > API Keys and create a new key. This generates a key in the format ck_live_... that the API member will use to authenticate SDK requests.
  2. Invite the API member - Go to Members > New Member and enter the API member’s wallet address. Select which API key to associate with this member. The same API key can be shared across multiple API members if needed.
Once invited, the API member can proceed with the registration steps below.

1) Set up the client and signer

Create the SDK client and signer used to authorize the registration.
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) Request a registration payload

Request the payload for the invited API member wallet address.
const payload = await client.getMemberRegistrationPayload("0xabc...");
The response includes a messageToSign.

3) Sign the payload off-chain

Sign the payload to prove control of the wallet.
const signature = await signer.signMessage({
  message: payload.messageToSign,
});

4) Submit the registration

Submit the signature to register the API member wallet.
const registration = await client.registerMember({
  walletAddress: "0xabc...",
  signature,
});

5) Admins add the member

Submit and execute a member proposal to activate the API member. Registration only validates ownership of the wallet. An admin must still submit a member proposal to add the API member to the organization and execute it.
const proposal = await client.proposeMemberCreation({
  items: [{ name: "Treasury Bot", walletAddress: "0xabc..." }],
});

await client.signMemberProposal(proposal.id, {
  type: "approve",
  signature: "0x...",
});

await client.executeMemberProposal(proposal.id, { type: "approve" });

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 to learn more.
Cloud KMS
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",
  signMessage: async ({ message }) => {
    const res = await kms.send(new SignCommand({ ... }));
    return res.Signature;
  },
});
HSM
import { toAccount } from "viem/accounts";

const signer = toAccount({
  address: "0xYOUR_WALLET_ADDRESS",
  signMessage: async ({ message }) => {
    return await hsmClient.sign(message);
  },
});
Environment variable
import { privateKeyToAccount } from "viem/accounts";

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

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) Request a registration payload.
const payload = await client.getMemberRegistrationPayload("0xabc...");

// 3) Sign the payload off-chain.
const signature = await signer.signMessage({
  message: payload.messageToSign,
});

// 4) Submit the registration.
const registration = await client.registerMember({
  walletAddress: "0xabc...",
  signature,
});

// 5) Admins add the member.
const proposal = await client.proposeMemberCreation({
  items: [{ name: "Treasury Bot", walletAddress: "0xabc..." }],
});

await client.signMemberProposal(proposal.id, {
  type: "approve",
  signature: "0x...",
});

await client.executeMemberProposal(proposal.id, { type: "approve" });