npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

brickken-sdk

v0.2.0

Published

TypeScript SDK for the Brickken tokenization and agentic APIs (Dapp, x402/ERC-8004, RAMS/ERC-8226)

Readme

brickken-sdk

TypeScript SDK for the Brickken platform. One typed client for the Dapp API (tokenization, STOs, security tokens), the Agentic API (x402 payments, ERC-8004 identity and reputation, agent-owned tokens), and RAMS mandates (ERC-8226).

Runs on Node 20+, in browsers, and on edge runtimes. The core has one runtime dependency.

npm install brickken-sdk

Quickstart

The Dapp API, with an API key:

import { Brickken } from 'brickken-sdk'

const bkn = new Brickken({ env: 'sandbox', apiKey: process.env.BRICKKEN_API_KEY })

const token = await bkn.tokenization.info({ tokenSymbol: 'EXMPL' })
const offerings = await bkn.sto.list({ tokenSymbol: 'EXMPL' })

The Agentic API, paying per call with x402:

import { Brickken } from 'brickken-sdk'
import { fromPrivateKey } from 'brickken-sdk/adapters/private-key'

const bkn = new Brickken({
  env: 'sandbox',
  signer: fromPrivateKey(process.env.BRICKKEN_PRIVATE_KEY!),
  payment: {
    maxAmountBaseUnits: '50000',                  // ceiling in the asset's base units
    onPaymentRequired: async quote => {
      console.log(`about to pay ${quote.displayPrice}`)
      return true
    },
  },
})

const agent = await bkn.agent.register(
  {
    chainId: '84532',
    name: 'Research Agent',
    image: 'https://example.com/agent.png',       // required, must be publicly reachable
    serviceName: 'A2A',
    serviceEndpoint: 'https://agent.example/.well-known/agent-card.json',
  },
  { execute: true },                               // prepare is free; this also pays and sends
)

console.log(agent.info?.agentUuid)                 // save it: set-uri and set-metadata need it
console.log(agent.payment?.settlement)             // what the payment settled to

KYC and agent reads

KYC link creation requires an API key. It can create an investor record and send an invitation email, so treat the returned Sumsub URL as sensitive:

const result = await bkn.kyc.createLink({
  email: '[email protected]',
  needKyc: true,
})

Agent getters accept either an API key or x402:

const agents = await bkn.agent.list({ chainId: '84532', limit: 20, offset: 0 })
const agent = await bkn.agent.get({ agentUuid: agents.agents[0]!.uuid })
const history = await bkn.agent.transactions({ agentUuid: agent.uuid, limit: 20, offset: 0 })

References can use agentUuid, or agentId together with chainId. In x402 mode, agent.list additionally requires ownerWalletAddress, and the SDK verifies locally that the payment signer owns that wallet. Pagination limits are 1-100 and offsets are non-negative.

Two credentials, and they are alternatives

Brickken accepts an API key or an x402 payment, never both on one request. When x-api-key is present the API skips the payment path entirely, which means no payment is taken and brickken-relayed execution becomes unavailable.

| | apiKey | signer, no key | | --- | --- | --- | | KYC link creation | yes | no | | Agent getters | yes | yes | | Dapp API (tokenization, STOs, reads) | yes | no | | Agentic API, client-signed | yes | yes | | Agentic API, client-broadcast | yes | yes | | Agentic API, brickken-relayed | rejected | yes | | RAMS reads and typed data | yes | yes |

The SDK derives the mode from what you pass and refuses illegal combinations locally, before any request leaves:

const bkn = new Brickken({ apiKey: 'key', signer })
await bkn.agent.register(input, { executionMode: 'brickken-relayed' })
// throws RelayedRequiresPaymentError, naming both fixes, with no network round trip

Your private key is never sent anywhere. It signs the x402 payment authorization, client-signed transactions, and RAMS typed data, all in your process.

Prepare is free; sending costs money

Every write follows prepare → sign → send. Preparing costs nothing, so the SDK defaults to prepare-only and execute: true is opt-in.

const prepared = await bkn.agentToken.create({ chainId: '84532', name: 'Agent Token', symbol: 'AGT' })
prepared.txId                  // the prepared transaction id
prepared.transactions          // unsigned transactions
prepared.x402Requirements      // what sending will cost
prepared.sent                  // undefined: nothing was sent

const done = await bkn.agentToken.create(
  { chainId: '84532', name: 'Agent Token', symbol: 'AGT' },
  { execute: true, waitForReceipt: true },        // waitForReceipt needs rpcUrl
)
done.payment?.requirement.amount
done.deployedAddress                              // recovered from the receipt

Payment metadata is always a sibling field. It is never mixed into the response body.

Three execution modes

executionMode decides who signs the transaction and who puts it on chain. Each write has a sensible default, so most callers never set it.

| Mode | Signs | Broadcasts | Needs | | --- | --- | --- | --- | | client-signed | you | Brickken | a signer with signTransaction | | client-broadcast | you | you, over rpcUrl | a signer with signTransaction, plus rpcUrl | | brickken-relayed | Brickken's relayer | Brickken | an x402 payment, so no apiKey |

client-signed is the default for Dapp writes: you sign locally and hand the signed transaction to Brickken to broadcast.

client-broadcast submits the signed transaction yourself through your own JSON-RPC endpoint, then confirms { txId, txHash } back to Brickken. Use it when you need to own the mempool path — your own node, a private relay, or your own gas and nonce policy.

const bkn = new Brickken({ apiKey: 'key', signer, rpcUrl: 'https://your-node.example' })

const done = await bkn.tokenization.create(input, {
  execute: true,
  executionMode: 'client-broadcast',
  signerAddress: await signer.address(),
})
done.sent?.transactionHashes[0]        // the hash your node assigned

It takes exactly one prepared transaction: the SDK rejects an empty response or batch locally rather than reporting a partial execution. If your node refuses the transaction — for example nonce too low or insufficient funds — you get an RpcRejectedError carrying the node's own code and message. An already known response is recovered automatically from the signed transaction's deterministic hash.

After broadcasting, the SDK retries the Brickken confirmation while the backend RPC catches up with transaction propagation. If confirmation still fails, BroadcastConfirmationError preserves both .txId and .txHash; resume only that step without signing or broadcasting again:

import { BroadcastConfirmationError } from 'brickken-sdk'

try {
  await bkn.tokenization.create(input, options)
} catch (error) {
  if (error instanceof BroadcastConfirmationError) {
    await bkn.tx.send({ txId: error.txId, txHash: error.txHash })
  }
}

Spending controls

x402 payments are non-refundable, and an autonomous agent with no policy is an unbounded spender. Three controls, all optional but strongly recommended:

new Brickken({
  signer,
  payment: {
    maxAmountBaseUnits: '50000',                   // hard ceiling; throws before signing
    onPaymentRequired: async quote => BigInt(quote.amountBaseUnits) <= 20_000n,
    onPayment: record => audit.log(record),        // called after settlement
  },
})

The SDK also refuses to pay the same txId twice, and every value it signs — chain, asset, transfer rail, amount, recipient, authorization window — is read from the live PAYMENT-REQUIRED header rather than hardcoded.

Signers

The SDK defines a minimal contract and ships three adapters:

import { fromPrivateKey } from 'brickken-sdk/adapters/private-key'   // raw hex key
import { fromEthers } from 'brickken-sdk/adapters/ethers'            // ethers v6
import { fromViem } from 'brickken-sdk/adapters/viem'                // viem Account or WalletClient

ethers and viem are optional peer dependencies, imported only from their own subpath. A consumer using neither installs neither.

interface Signer {
  address(): Promise<Address>
  signTypedData(data: TypedDataDefinition): Promise<Hex>
  signTransaction?(transaction: TransactionRequest): Promise<Hex>     // optional
}

signTransaction is optional by design: a KMS, MPC, or browser-wallet signer can sign typed data but not a raw transaction, and that is enough for the whole brickken-relayed path and every RAMS EIP-712 flow. Bring your own by implementing the three methods.

RAMS mandates

The four lifecycle operations — grantMandate, revokeMandate, extendMandate, setOperator — accept either direct authorization or a principal EIP-712 signature that lets any relayer send. With authorize: 'signature' the SDK fetches the typed data, signs it, and resubmits the same parameters and the same deadline, which is the constraint most easily broken by hand:

await bkn.rams.grantMandate(
  {
    agent, principal, asset, identityRef,
    validUntil: '1893456000',
    maxTransactionValue: 'max',                    // alias for 2^256-1
    maxCumulativeValue: '1000000',                 // raw base units, no decimal scaling
    actions: ['0x23b872dd'],                       // bytes32 or a bare selector
  },
  { authorize: 'signature', executionMode: 'brickken-relayed', execute: true },
)

Two asymmetries the types make visible:

  • RAMS caps are RawBaseUnits — no decimal scaling, because the backend cannot know an arbitrary asset's decimals. Agent-token amounts are HumanAmount and are scaled.
  • Only those four operations can be relayed. execute, setExecutorAction, freezeAgent, unfreezeAgent, grantPrincipal, and revokePrincipal require msg.sender to be the role holder, so the SDK rejects brickken-relayed on them locally.

RAMS runs on Ethereum Sepolia (11155111), which every RAMS call defaults to.

Errors

Branch on the class, never on the status code — the API reports the same missing-key cause as 401 on one endpoint and 400 on another.

| Class | Meaning | | --- | --- | | ValidationError | refused locally; no request was made | | AuthError | credential missing, wrong, or not accepted here | | RelayedRequiresPaymentError | relayed execution with an API key configured | | CreditsExhaustedError | plan balance for that write method is spent; carries .method | | UnauthorizedTokenSymbolError | your key did not tokenize that symbol | | PaymentRequiredError | x402 payment needed and none could be authorized | | PaymentDeclinedError | your ceiling or hook refused to pay | | RateLimitError | 429; carries .retryAfterSeconds | | ApiError | any other non-2xx; carries .status and .body | | NetworkError | transport failure or timeout | | RpcRejectedError | the configured JSON-RPC node rejected a call | | BroadcastConfirmationError | the transaction was broadcast but backend confirmation failed; carries .txId and .txHash | | TxRevertedError | the transaction was mined and reverted |

429, 5xx, and transport failures retry with jittered backoff. A send for which a payment was already authorized is never retried; if it fails, the error carries .payment so you can reconcile the charge.

Escape hatch

Any backend method is reachable the day it ships, without waiting for a typed namespace:

await bkn.tx.prepare({ method: 'someNewMethod', chainId: '8453', custom: 'value' })
await bkn.tx.sign(transactions)
await bkn.tx.send({ txId, signedTransactions })
await bkn.tx.status({ txId })
await bkn.tx.waitForReceipt({ txHash, chainId: '8453' })

Configuration

new Brickken({
  env: 'sandbox',            // 'sandbox' | 'production'
  baseUrl,                   // overrides env
  apiKey,
  signer,
  rpcUrl,                    // client-broadcast writes, receipt polling, and on-chain token metadata
  timeoutMs: 30_000,
  retry: { attempts: 3, baseDelayMs: 500, jitter: true },
  payment: { /* see above */ },
  fetch,                     // inject your own
})

Brickken.fromEnv(process.env) reads BRICKKEN_API_KEY, BRICKKEN_ENV, BRICKKEN_BASE_URL, and BRICKKEN_RPC_URL, plus the BKN_* aliases. It deliberately does not read BRICKKEN_PRIVATE_KEY: turning a key into a signer is an explicit choice, never an implicit one.

| Environment | Base URL | | --- | --- | | sandbox | https://api.sandbox.brickken.com | | production | https://api.brickken.com |

Sandbox and production are separate deployments with separate databases; a key for one is unknown to the other.

Prerequisites the API does not expose

Two things fail at request time and are invisible beforehand:

  1. The wallet you pass as signerAddress must be whitelisted by Brickken before any prepare accepts it. Request it together with your API key.
  2. An API key may only act on token symbols whose tokenizer email matches a newTokenization performed under that same key. Anything else is an UnauthorizedTokenSymbolError.

Each write method also carries its own credit balance, so one method can run out while others still work.

Preparing is free, but not unlimited: the API caps how many prepared transactions a wallet may have outstanding and answers 429 with Too many outstanding prepared transactions for this wallet. The SDK honours the Retry-After it comes with, but only for the configured number of attempts — three by default. After that it throws RateLimitError, carrying .retryAfterSeconds. A loop that prepares without sending will therefore slow down and then fail, so treat the quota as a limit to respect rather than a delay to wait out.

Development

pnpm install
pnpm test              # unit tests, no network
pnpm typecheck
pnpm build
pnpm audit:package     # pre-publish: tarball contents, secret scan, export resolution
pnpm gen:manifest      # refresh the OpenAPI snapshot the drift test compares against
pnpm test:integration  # opt-in, needs BRICKKEN_SANDBOX_API_KEY and/or BRICKKEN_SANDBOX_PRIVATE_KEY

pnpm verify runs typecheck, unit tests, build, package audit, and smoke install. Manifest generation and the live integration suite remain explicit because they access external services.

Publishing goes through the v* tag workflow in .github/workflows/publish.yml, which adds --provenance. Provenance needs CI OIDC, so it is not set in publishConfig — that would break a local npm publish.

pnpm test includes a parity suite asserting every agentic and RAMS request body is byte-identical to [email protected], whose behaviour is already validated against the live backend, and a drift test asserting the SDK reaches every operation in its generated OpenAPI manifest.

License

MIT