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

@pulgueta/bold

v1.0.1

Published

Node.js Bold SDK

Readme

Bold Node.js SDK

Implement Bold in your Node.js applications.

Installation

bun add @pulgueta/bold zod

Quick Start

import { Bold } from "@pulgueta/bold";

export const bold = new Bold({
  identityKey: process.env.BOLD_IDENTITY_KEY,
  secretKey: process.env.BOLD_SECRET_KEY,
  environment: "sandbox", // or "production"
});

// Create a payment
const [error, payment] = await bold.payments.create({
  amount: {
    currency: "COP",
    total_amount: 50000,
    taxes: [],
    tip_amount: 0,
  },
  payment_method: "POS",
  terminal_model: "P400",
  terminal_serial: "12345678",
  reference: "ORDER-123",
  user_email: "[email protected]",
});

if (error) {
  console.error("Payment failed:", error);
} else {
  console.log("Payment created:", payment.payload.integration_id);
}

Configuration

SDK Options

interface BoldOptions {
  // Required: Your Bold API identity key
  identityKey: string;

  // Optional: Secret key for webhook verification
  secretKey?: string;

  // Optional: OAuth client credentials
  clientId?: string;
  clientSecret?: string;

  // Optional: Environment (default: "sandbox")
  environment?: "sandbox" | "production";

  // Optional: Request timeout in milliseconds (default: 30000)
  timeoutMs?: number;

  // Optional: Number of retry attempts (default: 0)
  retries?: number;

  // Optional: Delay between retries in milliseconds (default: 1000)
  retryDelayMs?: number;
}

Example Configuration

const bold = new Bold({
  identityKey: process.env.BOLD_IDENTITY_KEY!,
  secretKey: process.env.BOLD_SECRET_KEY,
  clientId: process.env.BOLD_CLIENT_ID,
  clientSecret: process.env.BOLD_CLIENT_SECRET,
  environment: "production",
  timeoutMs: 15000,
  retries: 3,
  retryDelayMs: 2000,
});

API Reference

OAuth

Get an OAuth access token using client credentials.

const [error, token] = await bold.oauth.getToken();

if (error) {
  console.error("Failed to get token:", error);
} else {
  console.log("Access token:", token.access_token);
  console.log("Expires in:", token.expires_in);
}

Payments

Create Payment

Create a payment through the API Integrations (App Checkout).

import type { AppCheckoutRequest } from "@pulgueta/bold";

const paymentRequest: AppCheckoutRequest = {
  amount: {
    currency: "COP",
    total_amount: 100000,
    taxes: [
      {
        type: "VAT",
        base: 84034,
        value: 15966,
      },
    ],
    tip_amount: 5000,
  },
  payment_method: "NEQUI",
  terminal_model: "SMARTPHONE",
  terminal_serial: "123456789",
  reference: "ORDER-001",
  user_email: "[email protected]",
  description: "Product purchase",
  payer: {
    email: "[email protected]",
    phone_number: "+573001234567",
    document: {
      document_type: "CEDULA",
      document_number: "1234567890",
    },
  },
};

const [error, payment] = await bold.payments.create(paymentRequest, {
  // Optional request config
  idempotencyKey: "unique-request-id",
  timeoutMs: 15000,
});

Get Payment Methods

Get available payment methods for the merchant.

const [error, methods] = await bold.payments.getMethods();

if (error) {
  console.error("Failed to get payment methods:", error);
} else {
  console.log("Available methods:", methods.payload.payment_methods);
}

Get Payment Status

Get the payment voucher / transaction status for a sale.

const [error, status] = await bold.payments.getStatus("sale-id-123");

if (error) {
  console.error("Failed to get payment status:", error);
} else {
  console.log("Payment status:", status.payment_status);
  console.log("Transaction ID:", status.transaction_id);
}

Terminals

List Terminals

Get available payment terminals (dataphones) for the merchant.

const [error, terminals] = await bold.terminals.list();

if (error) {
  console.error("Failed to get terminals:", error);
} else {
  for (const terminal of terminals.payload.available_terminals) {
    console.log(`${terminal.name}: ${terminal.terminal_serial}`);
  }
}

Webhooks

Verify Webhook Signature

Verify the authenticity of a webhook payload using HMAC-SHA256.

import express from "express";

const app = express();

app.post("/webhook", express.raw({ type: "application/json" }), (req, res) => {
  const signature = req.headers["x-bold-signature"] as string;

  // Verify the webhook signature
  const result = bold.webhooks.verify(req.body, signature);

  if (!result.valid) {
    console.error("Invalid webhook signature:", result.error);
    return res.status(400).json({ error: result.error });
  }

  // Parse the webhook payload
  const notification = bold.webhooks.parse<WebhookNotification>(
    req.body.toString()
  );

  if (!notification) {
    return res.status(400).json({ error: "Invalid payload" });
  }

  // Process the webhook
  console.log("Webhook received:", notification.type);
  console.log("Payment ID:", notification.data.payment_id);

  res.status(200).json({ received: true });
});

Generate Webhook Signature (for testing)

Generate a webhook signature for testing purposes.

const payload = JSON.stringify({
  id: "evt_123",
  type: "SALE_APPROVED",
  data: {
    payment_id: "pay_123",
    merchant_id: "merchant_123",
    amount: { total: 50000, currency: "COP" },
  },
});

const signature = bold.webhooks.generateSignature(payload);

// Use in tests
const response = await fetch("http://localhost:3000/webhook", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    "x-bold-signature": signature,
  },
  body: payload,
});

Get Webhook Notifications

Get webhook notifications for a payment (fallback service).

// By payment ID
const [error, notifications] = await bold.webhooks.getNotifications("pay_123");

// By external reference
const [error2, notifications2] = await bold.webhooks.getNotifications(
  "ORDER-123",
  { isExternalReference: true }
);

if (error) {
  console.error("Failed to get notifications:", error);
} else {
  for (const notification of notifications.notifications) {
    console.log(`${notification.type} at ${notification.time}`);
  }
}

Error Handling

The SDK uses a Result Tuple pattern for explicit error handling. All async methods return [error, null] on failure or [null, result] on success.

Result Tuple Pattern

import type { BoldError } from "@pulgueta/bold";

const [error, payment] = await bold.payments.create(paymentRequest);

if (error) {
  // Handle error based on type
  switch (error.kind) {
    case "network":
      console.error("Network error:", error.message);
      break;
    case "http":
      console.error(`HTTP ${error.status}: ${error.statusText}`);
      break;
    case "invalid_response":
      console.error("Invalid response:", error.issues);
      break;
    case "api_error":
      console.error("API error:", error.errors);
      break;
    case "config":
      console.error("Configuration error:", error.message);
      break;
    case "timeout":
      console.error(`Request timeout after ${error.timeoutMs}ms`);
      break;
    case "aborted":
      console.error("Request aborted:", error.message);
      break;
  }
  return;
}

// Success - payment is guaranteed to be non-null
console.log("Payment ID:", payment.payload.integration_id);

Error Types

type BoldError = { kind: Kind } & (
  | NetworkError // Fetch failed (network issues, timeout, abort)
  | HttpError // Non-2xx HTTP status
  | InvalidResponseError // Response failed Zod validation
  | ApiError // Bold API returned errors
  | ConfigError // SDK misconfiguration
  | TimeoutError // Request timeout
  | AbortedError
); // Request aborted

TypeScript Support

The SDK is fully typed with TypeScript and exports all schemas and types.

Using Types

import type {
  AppCheckoutRequest,
  AppCheckoutResponse,
  PaymentStatus,
  WebhookNotification,
  WebhookEventType,
  BoldError,
} from "@pulgueta/bold";

const payment: AppCheckoutRequest = {
  amount: {
    currency: "COP",
    total_amount: 50000,
    taxes: [],
    tip_amount: 0,
  },
  payment_method: "POS",
  terminal_model: "P400",
  terminal_serial: "12345678",
  reference: "ORDER-123",
  user_email: "[email protected]",
};

Using Schemas (Runtime Validation)

import { AppCheckoutRequestSchema } from "@pulgueta/bold";

const result = AppCheckoutRequestSchema.safeParse(unknownData);

if (result.success) {
  const payment = result.data;
  // payment is now typed as AppCheckoutRequest
} else {
  console.error("Validation errors:", result.error.issues);
}

Advanced Features

Request Configuration

All methods accept optional request configuration:

interface RequestConfig {
  // Custom timeout for this request
  timeoutMs?: number;

  // AbortSignal for request cancellation
  signal?: AbortSignal;

  // Number of retry attempts
  retries?: number;

  // Delay between retries in milliseconds
  retryDelayMs?: number;

  // Idempotency key for safe retries (payments only)
  idempotencyKey?: string;
}

Request Cancellation

const controller = new AbortController();

// Cancel request after 5 seconds
setTimeout(() => controller.abort(), 5000);

const [error, payment] = await bold.payments.create(paymentRequest, {
  signal: controller.signal,
});

if (error?.kind === "aborted") {
  console.log("Request was cancelled");
}

Idempotent Requests

import { randomUUID } from "crypto";

const idempotencyKey = randomUUID();

// First attempt
const [error1, payment1] = await bold.payments.create(paymentRequest, {
  idempotencyKey,
});

// Retry with same key - will not create duplicate payment
const [error2, payment2] = await bold.payments.create(paymentRequest, {
  idempotencyKey,
});

Webhook Event Types

type WebhookEventType =
  | "SALE_APPROVED"
  | "SALE_REJECTED"
  | "VOID_APPROVED"
  | "VOID_REJECTED";

Payment Methods

type PaymentMethod = "DAVIPLATA" | "NEQUI" | "PAY_BY_LINK" | "POS";

Payment Status

type PaymentStatus =
  | "NO_TRANSACTION_FOUND"
  | "PROCESSING"
  | "PENDING"
  | "APPROVED"
  | "REJECTED"
  | "FAILED"
  | "VOIDED";

Document Types

type DocumentType =
  | "CEDULA"
  | "NIT"
  | "CEDULA_EXTRANJERIA"
  | "PEP"
  | "PASAPORTE"
  | "NUIP"
  | "REGISTRO_CIVIL"
  | "DOCUMENTO_EXTRANJERIA"
  | "TARJETA_IDENTIDAD"
  | "PPT";

Development

Building

pnpm install
pnpm build

Linting and Formatting

pnpm lint
pnpm format

Contributing

Contributions are welcome! Please open an issue or submit a pull request.

License

MIT © Andrés Rodríguez

Links