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

@getbioflow/sdk

v0.1.1

Published

Official TypeScript SDK for the BioFlow public API (https://app.getbioflow.com/v1)

Downloads

297

Readme

@getbioflow/sdk

The official TypeScript SDK for the BioFlow public API (https://app.getbioflow.com/v1) — typed access to pages, blocks, publishing, contacts, files, analytics, usage, and outbound webhooks, plus a Standard Webhooks signature verifier.

  • Server-side only. bf_ API keys are secrets; the client refuses to construct in a browser.
  • Spec-derived types. Every request/response type is generated from the API's OpenAPI 3.1 document — CI fails if the SDK drifts from the spec.
  • Zero runtime dependencies. Node.js ≥ 18.17 (built-in fetch); Bun and nodejs_compat worker runtimes work too.

Install

npm install @getbioflow/sdk

Quickstart

Create an API key in BioFlow → Settings (Creator or Pro plan), then:

import BioFlow from "@getbioflow/sdk";

const bioflow = new BioFlow({ apiKey: process.env.BIOFLOW_API_KEY! });

// List pages — one page of results…
const pages = await bioflow.pages.list({ limit: 20 });
console.log(pages.data.map((page) => page.slug));

// …or auto-paginate the whole collection.
for await (const contact of await bioflow.contacts.list()) {
  console.log(contact);
}

// Create, edit, publish.
const page = await bioflow.pages.create({ title: "Launch page" });
await bioflow.pages.addBlock(page.id, { kind: "LINK" });
await bioflow.pages.publish(page.id);

Errors

Failures throw a typed hierarchy mirroring the API's RFC 9457 problem codes. Branch on the class or on error.code — never parse messages.

import {
  NotFoundError,
  QuotaExhaustedError,
  RateLimitError,
} from "@getbioflow/sdk";

try {
  await bioflow.pages.get("pg_missing");
} catch (error) {
  if (error instanceof NotFoundError) {
    console.log(error.code, error.requestId); // "resource_not_found", "req_…"
  }
}

| class | codes (status) | | -------------------------- | ---------------------------------------------------------------------------------------- | | BadRequestError | invalid_request (400) — error.errors holds JSON-Pointer field errors | | AuthenticationError | invalid_api_key (401) | | PermissionDeniedError | insufficient_scope, feature_not_enabled, test_key_read_only (403) | | NotFoundError | resource_not_found (404) | | ConflictError | stale_snapshot, idempotency_in_progress (409) | | UnprocessableEntityError | idempotency_key_reused, endpoint_verification_failed, endpoint_limit_reached (422) | | RateLimitError | rate_limited (429, per-key burst) | | QuotaExhaustedError | quota_exhausted (429, monthly plan quota — subclass of RateLimitError) | | InternalServerError | internal_error (5xx) |

Unknown future codes stay forward-compatible: the class follows the HTTP status family and error.code carries the new value verbatim.

Retries & idempotency

  • Every consequential POST automatically gets an Idempotency-Key (sdk_<uuid>), reused across retries — the server dedupes instead of double-executing. Pass { idempotencyKey } to control it, or autoIdempotencyKeys: false to opt out (which also disables POST retries).
  • Retried (default maxRetries: 2): 429 rate_limited for any method; network errors, timeouts, 408 and 5xx for GETs and keyed POSTs.
  • Never retried: 429 quota_exhausted (waits until your monthly reset — handle it), PATCH/DELETE on ambiguous failures, and anything after your own AbortSignal fires.
  • Retry-After is honored exactly; waits longer than maxRetryAfterMs (default 60 s) abandon the retry instead of sleeping.
await bioflow.pages.create(
  { title: "Exactly once" },
  { idempotencyKey: "order-1234", timeoutMs: 10_000 },
);

Verifying webhooks

Verify BEFORE parsing, against the raw request bytes — any re-serialize breaks the signature. Rotation overlap (two signatures) is handled.

import { verifyWebhook, WebhookVerificationError } from "@getbioflow/sdk";

app.post("/bioflow-webhooks", express.raw({ type: "*/*" }), (req, res) => {
  let event;
  try {
    event = verifyWebhook({
      payload: req.body, // Buffer — the raw bytes
      headers: req.headers,
      secret: process.env.BIOFLOW_WEBHOOK_SECRET!, // whsec_… shown at endpoint creation
    });
  } catch (error) {
    if (error instanceof WebhookVerificationError) return res.status(400).end();
    throw error;
  }
  // event.id (whmsg_…) is stable across retries — use it as your dedup key.
  switch (event.type) {
    case "contact.created":
      console.log(event.data.contact.email);
      break;
    case "page.published":
    case "sale.paid":
    case "sale.refunded":
      break;
    default: // new event types may appear — always keep a default branch
  }
  res.status(200).end();
});

Escape hatches

// Raw request through the same auth/retry/error pipeline:
const { data, response, requestId } = await bioflow.request({
  method: "GET",
  path: "/v1/usage",
});

// Page-level pagination:
const first = await bioflow.pages.list({ limit: 50 });
if (first.hasNextPage()) {
  const second = await first.nextPage();
}

Client options

new BioFlow({
  apiKey: "bf_live_…", // required
  baseUrl: "https://app.getbioflow.com",
  timeoutMs: 30_000,
  maxRetries: 2,
  authStyle: "bearer", // or "x-api-key"
  autoIdempotencyKeys: true,
  maxRetryAfterMs: 60_000,
  defaultHeaders: {},
  debug: false, // true | (line) => void — secrets are redacted
  fetch: globalThis.fetch,
  dangerouslyAllowBrowser: false,
});

Development

This package is developed in the private BioFlow monorepo and mirrored to DevinoSolutions/bioflow-sdk; issues and PRs are welcome on the mirror. Docs: getbioflow.com/developers.

MIT © Devino Solutions Inc.