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

@openmarketsai/connect-node

v0.7.0

Published

Server SDK for OpenMarkets Connect — provision users, mint link sessions, verify webhooks, and execute on a user's behalf.

Readme

@openmarketsai/connect-node

Server SDK for OpenMarkets Connect. A thin, typed wrapper over the Flow API: provision your users, mint hosted link sessions, verify webhook deliveries, read on a user's behalf, and execute orders on their account. Pairs with @openmarketsai/connect-web (the in-app launcher).

npm install @openmarketsai/connect-node

Node 18+ (uses global fetch and node:crypto). No runtime dependencies.

Quickstart

import { createClient } from '@openmarketsai/connect-node'

const om = createClient({
  apiKey: process.env.OM_API_KEY!,          // your Connect org key
  webhookSecret: process.env.OM_WHSEC,      // optional, enables om.connect.webhooks.*
})

// 1. Provision a user (idempotent)
await om.connect.users.provision({ external_user_id: 'user-123', email: '[email protected]' })

// 2. Mint a hosted link token → hand to @openmarketsai/connect-web on the client
const { token } = await om.connect.linkSessions.create({
  external_user_id: 'user-123',
  return_url: 'https://yourapp.com/connected',
})

// 4. Read prices, 5. execute on the user's behalf
const liquidity = await om.data.contestLiquidity(contestId)
await om.orders.buy(
  // omit partner_id to route to the best venue; pass mode:'paper' for practice
  [{ position_hash, amount: 50, max_price: 0.42 }],
  { actAs: 'user-123' },
)

// balances / positions, all on the user's behalf
await om.account.balances('user-123')

Buy legs use the same field names as the liquidity/quote entries, so you can pass them through with minimal mapping.

Webhooks (3)

Verify deliveries over the raw request body (don't re-serialize):

import express from 'express'
const app = express()

app.post('/webhooks/openmarkets', express.raw({ type: 'application/json' }), (req, res) => {
  try {
    const event = om.connect.webhooks.constructEvent(
      req.body,                              // Buffer (raw)
      req.header('X-OpenMarkets-Signature'),
    )
    if (event.type === 'user.partners_connected') { /* … */ }
    res.sendStatus(200)
  } catch {
    res.sendStatus(400)                      // bad signature / stale / malformed
  }
})

Or use the standalone helpers without a client:

import { verifyWebhookSignature, constructWebhookEvent } from '@openmarketsai/connect-node'
verifyWebhookSignature(secret, rawBody, sigHeader)   // boolean

Signature scheme: header X-OpenMarkets-Signature: t=<unix_seconds>,v1=<hmac>, where the HMAC-SHA256 is over `${t}.${rawBody}` with your whsec_ secret; deliveries older than 5 minutes are rejected.

API

| Method | Endpoint | |---|---| | connect.users.provision(params) | POST /flow/v1/connect/users | | connect.users.get(id) | GET /flow/v1/connect/users/:id | | connect.users.getPolicy(id) | GET /flow/v1/connect/users/:id/policy | | connect.linkSessions.create(params) | POST /flow/v1/connect/link-sessions | | connect.linkSessions.get(id) | GET /flow/v1/connect/link-sessions/:id | | connect.webhooks.getConfig() | GET /flow/v1/connect/webhook-config | | connect.webhooks.verify / constructEvent | (local, no network) | | account.partners(id) / balances(id) | GET /flow/v1/auth/account/{partners,balances} (act-as) | | orders.list(id) | GET /flow/v1/auth/orders (act-as) | | orders.buy(orders, { actAs }) | POST /flow/v1/auth/orders/buy (act-as) | | data.leagues() / contestLiquidity(id) | GET /flow/v1/leagues, …/contests/:id/liquidity | | connect.configs.list / create / patch / setDefault / archive | named hosted flows under /flow/v1/workspaces/:id/connect-configs (player JWT, workspace admin) | | ai.invoke(params, { actAs }) | POST /flow/v1/ai/inferences + polls GET …/:id | | ai.inferences.create / get | the same, one request at a time | | ai.providers({ actAs }) | GET /flow/v1/ai/providers | | ai.models() | GET /flow/v1/ai/models — the managed-billing catalog | | request(method, path, opts) | escape hatch (opts.actAs sets the header, opts.idempotencyKey sends Idempotency-Key) | | requestPage(path, opts) / requestAll(path, opts) | list endpoints with their next_cursor — one page, or every page |

AI Connect — run models on your user's own key

Turn it on for your org with ai_providers: ['anthropic', 'openai'] in your Connect config. Your users then see an AI models section in the hosted flow, where they paste their own Anthropic or OpenAI API key. You never see the key.

const r = await om.ai.invoke<{ pick: 'home' | 'away'; confidence: number }>({
  provider: 'anthropic',
  model: 'claude-opus-5',
  system: 'You pick sides. Answer only in the schema.',
  messages: [{ role: 'user', content: JSON.stringify(snapshot) }],
  output_schema: { schema: PICK_SCHEMA },
}, { actAs: 'user-123' })

if (r.stop_reason === 'refusal') { /* not an answer */ }
r.output?.pick      // parsed, schema-shaped
r.billing           // 'byok' — calls on the user's key are NEVER charged by OpenMarkets

billing defaults to 'auto': the user's own key when they linked and shared it, otherwise OpenMarkets-hosted models charged in reasoning credits. Send billing: 'byok' to guarantee a call is never charged (it fails with 409 ai_provider_not_linked instead of falling back).

Long calls return 202 server-side; invoke polls for you and throws AiInferenceFailedError if the call fails.

Add web_search: true (up to 5 searches) or web_search: { max_uses: 3 } to let the model search the web first; the result carries web_search.requests and the sources it read. ai.models() lists what managed billing runs and whether your plan allows each model — check it before offering a model, rather than learning from a failed inference.

Errors

Non-2xx responses throw OpenMarketsError with status, code, message, and details from the Flow error envelope:

import { OpenMarketsError } from '@openmarketsai/connect-node'
try {
  await om.orders.buy(orders, { actAs: 'user-123' })
} catch (e) {
  if (e instanceof OpenMarketsError && e.code === 'execution_limit_exceeded') { /* … */ }
}

Options

| Option | Default | Notes | |---|---|---| | apiKey | — | Required. Sent as X-API-Key. | | baseUrl | https://api.openmarkets.ai | Override for staging. | | webhookSecret | — | Enables connect.webhooks.verify/constructEvent. | | fetch | global fetch | Inject for Node < 18 or testing. | | timeoutMs | 30000 | Per-request timeout. |