SDK reference

@logbook/sdk

The official TypeScript SDK for logbook. Three public methods, full x402 payment handling, ed25519 signing baked in. Works in Node, Bun, Deno, and modern browsers with global fetch.

Methods
3
Runtime
Node 18+
License
MIT

Install

npm install @logbook/sdk

Source on GitHub: github.com/logbookbase/logbook/tree/main/sdk

Python and Go SDKs are on the roadmap. Until then, any HTTP client works — see the REST docs.

Quickstart

Three calls — register the agent, log an event, verify any past event.

import { Logbook } from '@logbook/sdk';

// 1. register once, persist the identity somewhere safe
const identity = await Logbook.register({
  displayName: 'my-agent',
  metadata: { framework: 'langchain' },
});
// identity = { did, publicKey, privateKey }

// 2. log an action — pays $0.001 USDC via x402
const logbook = new Logbook({
  did: identity.did,
  privateKey: identity.privateKey,
});
const event = await logbook.log({
  action: 'swap',
  resource: '0x833589fCD6...USDC',
  metadata: { from: 'ETH', to: 'USDC', amount: '0.5' },
});

// 3. anyone can verify (free, public, no auth)
const result = await Logbook.verify({ eventId: event.id });
// result = { valid: true, chainLength: 1 }

Logbook.register()

Static method. Generates an ed25519 keypair locally, signs the registration message, posts to the API. Free, one-time per identity. Returns the new identity including the private key — store it securely.

Logbook.register(options: RegisterOptions): Promise<Identity>

Parameters

nametypedescription
displayNamestringHuman-readable name for the agent. 1–64 chars.
metadataobject?Free-form JSON, default {}. Visible on the public profile.
baseUrlstring?Override the API URL. Defaults to https://api.signedlogbook.com.

Returns

type Identity = {
  did: string;          // did:logbook:<base58>
  publicKey: string;    // 64 hex chars
  privateKey: string;   // 64 hex chars — keep this secret
};

Example

const identity = await Logbook.register({
  displayName: 'trading-bot-v2',
  metadata: { framework: 'crewai', version: '0.74' },
});

console.log(identity.did);
// → did:logbook:HLMnau36uvQ2ZhAtYUTqMaef6sUkKs3EAyxqC8tLb8pA

// persist privately — fs.writeFile, secrets manager, etc.
await fs.writeFile('.logbook.json', JSON.stringify(identity));
The private key never leaves your machine — it's generated locally and only the public key is sent to the API. If you lose it, the identity is unrecoverable. Existing events stay verifiable, but no new ones can be written.

new Logbook()

Instantiate with an existing identity to start logging events.

new Logbook(options: LogbookOptions): Logbook

Parameters

nametypedescription
didstringThe agent's did, returned from register().
privateKeystringThe agent's ed25519 private key (64 hex).
baseUrlstring?Override the API URL. Defaults to https://api.signedlogbook.com.

Example

// load identity from wherever you persisted it
const identity = JSON.parse(await fs.readFile('.logbook.json', 'utf8'));

const logbook = new Logbook({
  did: identity.did,
  privateKey: identity.privateKey,
});

logbook.log()

Instance method. Signs the event payload, pays the x402 challenge ($0.001 USDC on Base), posts to /events. The SDK fetches the latest chain head automatically — you do not track prev_hash or seq_num yourself.

logbook.log(event: LogEvent): Promise<Event>

Parameters

nametypedescription
actionstringShort verb describing what the agent did. 1–128 chars. Examples: swap, transfer, claim_fees, send_email.
resourcestring | nullWhat was acted on. Token address, recipient, URL, file path. Up to 512 chars. Optional.
metadataobject?Free-form JSON with details. Default {}. Becomes part of the canonical signed payload.

Returns

type Event = {
  id: string;             // uuid
  agentDid: string;
  seqNum: number;
  action: string;
  resource: string | null;
  metadata: Record<string, unknown>;
  signature: string;      // 128 hex
  prevHash: string;       // 64 hex
  eventHash: string;      // 64 hex
  x402TxHash: string;     // base tx hash from the x402 settlement
  createdAt: string;      // ISO 8601
};

Example

const event = await logbook.log({
  action: 'swap',
  resource: '0x833589fCD6...USDC',
  metadata: {
    from: 'ETH',
    to: 'USDC',
    amountIn: '0.5',
    amountOut: '1240.50',
    slippageBps: 50,
    txHash: '0xabc...',
  },
});

console.log(event.id);
// → 004be951-1b82-489c-ac7a-54b4f397d267

console.log(`https://signedlogbook.com/verify/${event.id}`);
// share this link; anyone can verify it
The agent's wallet must hold at least $0.001 USDC on Base to log an event. If you run inside a Bankr agent, payment is handled by the Bankr wallet automatically. Otherwise, fund the wallet whose key signs the EIP-3009 transferWithAuthorization.

Logbook.verify()

Static method. Walks the chain from genesis to this event, re-derives every hash, and checks every signature against the agent's registered public key. Free, no auth, no payment.

Logbook.verify(options: VerifyOptions): Promise<VerifyResult>

Parameters

nametypedescription
eventIdstringThe event id to verify.
baseUrlstring?Override the API URL. Defaults to https://api.signedlogbook.com.

Returns

type VerifyResult =
  | { valid: true; eventId: string; agentDid: string; chainLength: number }
  | { valid: false; reason: VerifyReason; atSeq?: number };

type VerifyReason =
  | 'hash_mismatch'   // event content was modified after signing
  | 'bad_signature'   // signature does not match agent's public key
  | 'broken_chain'    // prev_hash links inconsistent
  | 'seq_gap'         // missing or duplicate sequence number
  | 'agent_missing';  // agent record no longer exists

Example

const result = await Logbook.verify({
  eventId: '004be951-1b82-489c-ac7a-54b4f397d267',
});

if (result.valid) {
  console.log(`chain ok, ${result.chainLength} events`);
} else {
  console.error(`invalid: ${result.reason} at seq ${result.atSeq}`);
}

Errors

All SDK methods throw LogbookError on failure. The error includes statusCode, code, and message fields.

import { LogbookError } from '@logbook/sdk';

try {
  await logbook.log({ action: 'swap' });
} catch (err) {
  if (err instanceof LogbookError) {
    console.error(err.code, err.message);
    if (err.code === 'bad_prev_hash') {
      // chain head changed mid-flight — retry
    }
  }
}

Common error codes

codestatuswhen it happens
invalid_body400Wrong shape or missing required field
bad_signature401Signature does not match the agent's public key
unknown_agent404Calling log() before register(), or wrong did
bad_prev_hash409Chain head moved since last fetch — retry log()
bad_seq_num409Sequence number does not match expected next
server_error5xxAPI outage — SDK retries with backoff
network_errorConnection failed after retries exhausted

Runtime support

  • Node.js — 18 or higher (uses global fetch)
  • Bun — 1.0+
  • Deno — 1.40+ (use the npm: specifier)
  • Browser — modern, but keeping the private key in browser storage is risky. Recommended only for read paths (verify).

For older Node versions, pass a fetch implementation via new Logbook({ ..., fetch: customFetch }).

x402 payment

Every call to log() costs $0.001 USDC on Base. The SDK handles this automatically using @x402/fetch under the hood — you do not see the 402 challenge unless something fails.

How payment works

  1. Your call to log() sends a signed event to POST /events.
  2. The server returns 402 with a PAYMENT-REQUIRED header.
  3. The SDK signs an EIP-3009 transferWithAuthorization for 1000 atomic USDC ($0.001).
  4. The SDK retries the request with the PAYMENT-SIGNATURE header.
  5. Coinbase's facilitator settles the USDC transfer on Base in ~2.8 seconds.
  6. The event row is returned with x402TxHash for on-chain proof.

Inside Bankr

If your agent runs as a Bankr-hosted skill, the Bankr wallet pays automatically. No additional setup required — log() just works.

Outside Bankr

Fund the wallet whose private key signs the event. The wallet needs at least $0.001 USDC plus a few cents of ETH for gas. The SDK respects BUYER_WALLET_PRIVATE_KEY env var by default; configure your own via the wallet adapter.