JavaScript / TypeScript SDK

@pulselabs/sdk is the official typed client for the PulseLABS API. It wraps every v1 endpoint in a fluent, namespace-based interface with full TypeScript types, automatic error handling, and an AbortSignal hook on every call.

Installation

npm install @pulselabs/sdk

Requires Node.js 18+ (uses the native fetch global). No peer dependencies.

Initialization

import { PulseLabsSDK } from '@pulselabs/sdk'

const pulse = new PulseLabsSDK({
  apiKey: process.env.PULSELABS_API_KEY!,
  // baseUrl defaults to 'https://api.pulselabs.ai'
  // workspaceId is optional — omit to use your personal workspace
})
apiKeystringrequiredYour API key (sk_live_…). Get one from the dashboard.
baseUrlstringoptionalAPI base URL. Defaults to https://api.pulselabs.ai. Override for local dev.
workspaceIdstringoptionalScope all requests to a specific workspace. Omit to use your personal workspace.

Namespaces

All methods are grouped under a namespace on the SDK instance. Every method returns a typed Promise.

pulse.conversationslist, create, get, delete, sendMessage
pulse.agentslist, create, update, delete
pulse.agents.memorylist, create, delete
pulse.simulationslist, create, get, delete, advance, getReport, addScenario
pulse.aicomplete, listExtractedData, getExtractedData, deleteExtractedData
pulse.webhookslist, create, get, update, delete, deliveries, test
pulse.riskEvaluatorlistSessions, createSession, getSession, deleteSession, generateTree, applyEvent, selectBranch, exportJson
pulse.lifeCopilotgetProfile, updateProfile, listPlans, getPlan, createPlan, updatePlan, deletePlan
pulse.marketAnalysisanalyze

Conversations & debates

// Create and run a debate
const { conversation } = await pulse.conversations.create('EU expansion strategy')

const result = await pulse.conversations.sendMessage(
  conversation.id,
  'Should we expand into Germany before France?',
)

// List all conversations
const { conversations } = await pulse.conversations.list()

// Fetch one with full message history
const { conversation: full } = await pulse.conversations.get(conversation.id)

Agents

// Create an agent persona
const { agent } = await pulse.agents.create({
  name: 'Dr. Reyes',
  role: 'Risk Analyst',
  personality: 'Methodical, data-driven, conservative',
  mission: 'Identify and quantify downside risks',
  decisionStyle: 'evidence-based',
  systemPrompt: 'You are a risk analyst. For every proposal, enumerate...',
  isDevilsAdvocate: false,
})

// Add persistent memory to the agent
await pulse.agents.memory.create(agent.id, 'preferred_currency', 'EUR')

// List the agent's memories
const { memory } = await pulse.agents.memory.list(agent.id)

// Update the agent
await pulse.agents.update(agent.id, { personality: 'Methodical, data-driven, skeptical' })

Simulations

// Create a simulation
const sim = await pulse.simulations.create({
  name: 'Series A runway model',
  description: '18-month projection under three hiring scenarios',
  turnUnit: 'month',
  companyBase: {
    revenue: 85000,
    headcount: 12,
    burnRate: 42000,
    marketShare: 0.02,
  },
})

// Add a scenario
await pulse.simulations.addScenario(sim.simulation.id, {
  name: 'Aggressive hiring',
  description: 'Hire 6 engineers in Q3',
})

// Advance one turn (triggers debate between agents)
const { states } = await pulse.simulations.advance(sim.simulation.id)

// Retrieve the final report (once simulation is complete)
const { report } = await pulse.simulations.getReport(sim.simulation.id)

AI Manager (completion + extraction)

// Basic completion
const { content, usage } = await pulse.ai.complete({
  systemPrompt: 'You are a strategic business advisor.',
  messages: [
    { role: 'user', content: 'What are the top risks of a Series A raise right now?' },
  ],
  temperature: 0.4,
})

// With supplementary data + extraction
const { content: advice, extracted } = await pulse.ai.complete({
  systemPrompt: 'You are a strategic advisor.',
  messages: [
    { role: 'user', content: 'Our burn is €38k. We have 11 months left. Should we raise?' },
  ],
  supplementary: [
    {
      category: 'company-financials',
      priority: 'high',
      data: { mrr: 92000, headcount: 14, runway_months: 11 },
    },
  ],
  extraction: {
    source: 'strategy-session',
    categories: ['financials', 'intent'],
    persist: true,
  },
})

// Retrieve persisted facts
const { data: facts } = await pulse.ai.listExtractedData({
  source: 'strategy-session',
  category: 'financials',
})

Error handling

Every non-2xx response throws a PulseLabsSDKError with a status code and the raw response body. Network failures throw native TypeError (same as fetch).

401Invalid or missing API key
402Insufficient token balance or plan limit reached
403API key scope does not cover this endpoint
404Resource not found
500Server error — retry with exponential backoff
import { PulseLabsSDK, PulseLabsSDKError } from '@pulselabs/sdk'

const pulse = new PulseLabsSDK({ apiKey: process.env.PULSELABS_API_KEY! })

try {
  const { conversation } = await pulse.conversations.create('Test')
  await pulse.conversations.sendMessage(conversation.id, 'Hello')
} catch (err) {
  if (err instanceof PulseLabsSDKError) {
    console.error(`API error ${err.status}: ${err.message}`)
    // err.body — the raw JSON response body
    // err.status — HTTP status code (402 = out of tokens, 404 = not found, etc.)
  } else {
    throw err  // network error, re-throw
  }
}

Cancellation

Every SDK method accepts an optional { signal: AbortSignal } second argument, forwarded directly to the underlying fetch call.

const controller = new AbortController()

// Cancel after 10 seconds
setTimeout(() => controller.abort(), 10_000)

const result = await pulse.simulations.advance(simId, { signal: controller.signal })

Building and publishing

The SDK lives at sdk/ at the repository root, next to backend/ and frontend/. It is a standalone npm package — it does not depend on any other package in the monorepo.

Step 1 — Install SDK dependencies

cd sdk
npm install

Step 2 — Generate types from the live OpenAPI spec

The generate script fetches the OpenAPI 3.1 spec from a running backend, saves it to sdk/openapi.json, then runs @hey-api/openapi-ts to emit type definitions into sdk/src/generated/.

The backend must be running before you run npm run generate. Start it with cd backend && npm run dev in another terminal first.
# With default local backend (http://localhost:3001)
npm run generate

# Against a remote backend
API_URL=https://api.pulselabs.ai npm run generate

The saved openapi.json is committed to the repo so that npm run build can succeed without a running server (useful in CI).

Step 3 — Build

npm run build
# Compiles sdk/src/ → sdk/dist/
# Emits sdk/dist/index.js (CommonJS) + sdk/dist/index.d.ts (declarations)

Step 4 — Publish to npm

prepublishOnly runs generate then build automatically before every publish. You never need to run them manually before npm publish.
# First publish
npm publish --access public

# Patch release (0.1.0 → 0.1.1)
npm version patch && npm publish

# Minor release (0.1.x → 0.2.0)
npm version minor && npm publish

The package name is @pulselabs/sdk. Publishing requires an npm account in the pulselabs org, or change the name to a personal scope for testing.

Testing the SDK locally

There are two ways to test the SDK against a live backend without publishing it to npm.

Option A — npm link (recommended)

npm link creates a global symlink that other local projects can reference. Any change you make to the SDK is reflected immediately without re-publishing.

# 1. Register the SDK globally
cd sdk
npm run build
npm link

# 2. In your test project, link to it
cd /path/to/your-test-project
npm link @pulselabs/sdk

# 3. Use it like a published package
import { PulseLabsSDK } from '@pulselabs/sdk'

# 4. When done, unlink
npm unlink @pulselabs/sdk

Option B — Direct import via relative path

For a quick smoke test inside the same repository, import from the dist/ folder directly. No linking required.

# Build the SDK first
cd sdk && npm run build

# Then from a test script at the repo root:
node -e "
const { PulseLabsSDK } = require('./sdk/dist/index.js')
const pulse = new PulseLabsSDK({
  apiKey: 'sk_live_xxx',
  baseUrl: 'http://localhost:3001',
})
pulse.conversations.list().then(d => console.log(d))
"

Option C — ts-node against the source

If you are developing the SDK itself and want to test without a build step, run TypeScript directly:

cd sdk
npx ts-node -e "
import { PulseLabsSDK } from './src/index'

const pulse = new PulseLabsSDK({
  apiKey: 'sk_live_xxx',
  baseUrl: 'http://localhost:3001',
})

async function main() {
  const { conversations } = await pulse.conversations.list()
  console.log('Conversations:', conversations.length)

  const { agent } = await pulse.agents.create({
    name: 'Test Agent',
    role: 'Analyst',
    personality: 'Sharp',
    mission: 'Test',
    decisionStyle: 'data-driven',
    systemPrompt: 'You are a test agent.',
  })
  console.log('Created agent:', agent.id)
  await pulse.agents.delete(agent.id)
  console.log('Deleted agent')
}

main().catch(console.error)
"

Keeping the SDK in sync with the API

Whenever new endpoints are added to the backend, the SDK needs two updates:

1
Regenerate types
Run npm run generate with the updated backend running. This refreshes openapi.json and src/generated/.
2
Add the method
Add a typed method to the relevant namespace class in sdk/src/index.ts. Follow the existing pattern: call this.sdk.request() with the correct HTTP method and path.
3
Bump the version
Use npm version patch for bug fixes, npm version minor for new endpoints, npm version major for breaking changes.
4
Publish
Run npm publish. The prepublishOnly hook handles generate + build automatically.
The committed openapi.json is the source of truth for what types the SDK ships with. Pin your CI pipeline to regenerate it from the production API URL on every release tag to guarantee it stays current.

Quick reference

sdk/
├── package.json            # name: @pulselabs/sdk, version: 0.1.0
├── tsconfig.json           # targets CommonJS ES2020, emits to dist/
├── openapi.json            # committed snapshot of the API spec
├── scripts/
│   └── generate.ts         # fetches spec → runs @hey-api/openapi-ts
└── src/
    ├── index.ts            # PulseLabsSDK class + all namespace classes
    └── generated/          # auto-generated types (do not edit manually)

Scripts
  npm run generate          # regenerate types from a running backend
  npm run build             # tsc → dist/
  npm publish               # generate + build + publish to npm