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

@andeslabs/mint-sdk

v4.0.0

Published

TypeScript SDK for the Andes Mint API (https://andeslabs.io/mint/v1).

Readme

@andeslabs/mint-sdk

TypeScript SDK for the Andes Mint API. Wraps all public outbound endpoints, signs ES256 JWTs per request, unwraps response envelopes, and ships a webhook signature verifier.

  • Node 18+ (uses built-in fetch, FormData, node:crypto).
  • Zero runtime dependencies.
  • Server-side only — the SDK holds a signing key.

Install

npm install @andeslabs/mint-sdk

Authentication

Andes uses ES256 (ECDSA P-256) signed JWTs plus a project API key. Generate a key pair once and share the public key during onboarding:

openssl ecparam -genkey -name prime256v1 -noout -out ec_private.pem
openssl ec -in ec_private.pem -pubout -out ec_public.pem

Then create a client:

import { readFileSync } from "node:fs";
import { createClient } from "@andeslabs/mint-sdk";

const andes = createClient({
  apiKey: process.env.ANDES_API_KEY!,
  privateKey: readFileSync("ec_private.pem", "utf8"),
});

Every request signs a fresh 2-minute JWT. Pass issuer if you want an iss claim embedded (the backend does not verify it, so it's optional).

Using a custom signer (KMS / HSM)

Pass a signer function instead of a PEM to keep the private key outside SDK memory:

const andes = createClient({
  apiKey: process.env.ANDES_API_KEY!,
  signer: async () => kms.signAndesJwt(),
});

Usage

const { userId } = await andes.accounts.create({ name: "Juan Pérez" });
const { address } = await andes.wallets.create("base", { user_id: userId, asset: "arsa" });
const wallets = await andes.wallets.forUser(userId);
const stats = await andes.project.stats();

The response envelope ({ status, message, data }) is unwrapped — methods resolve with data directly.

Errors

Non-2xx responses throw AndesApiError:

import { AndesApiError } from "@andeslabs/mint-sdk";

try {
  await andes.wallets.create("base", { user_id, asset: "arsa" });
} catch (err) {
  if (err instanceof AndesApiError) {
    console.error(err.httpStatus, err.status, err.message, err.body);
  }
}

err.retryAfter carries the Retry-After header (seconds) when the API sends one. The first outbound transfer or fiat withdrawal from a freshly provisioned Stellar wallet returns 425 Too Early while the account is being activated — nothing is created on that response; retry after the delay and it succeeds.

Resource reference

| Namespace | Methods | |---|---| | accounts | create, list | | wallets | create(chain, body), list, forUser | | wallets.transfers | create, list, forUser, forUserSingle | | fiat | create(body, files?), createBusiness(body), updateAlias(body), list, forUser, arca, cvuLookup, withdraw, movements, movementsForUser, movementForUser | | international.accounts.fiat | list, createBob, createPen, createPyg, delete(id, { userId }) | | international.quotes | arsBob, arsPen, arsPyg | | international.offramp | createArsBob, createArsPen, createArsPyg, list | | international.banks | bob, pen, pyg | | international.cotization | () | | project | stats, statsTimeseries | | webhooks | create, list, get, update, delete, deliveries, signingKey |

Multipart: creating a fiat account

import { readFileSync } from "node:fs";

await andes.fiat.create(
  {
    user_id,
    chain: "base",
    email: "[email protected]",
    cuit: "20345678901",
    name: "Juan",
    last_name: "Pérez",
    phone: "+5491145678901",
    birthdate: "1990-05-15",
  },
  {
    face:     { buffer: readFileSync("face.jpg"),     filename: "face.jpg",     contentType: "image/jpeg" },
    id_front: { buffer: readFileSync("id-front.jpg"), filename: "id-front.jpg", contentType: "image/jpeg" },
    id_back:  { buffer: readFileSync("id-back.jpg"),  filename: "id-back.jpg",  contentType: "image/jpeg" },
  }
);

The personal fields and KYC images are required only for first-time onboarding of a CUIT. Once a person has a completed fiat account, attach a CVU on another chain with a minimal request — no personal fields, no files:

await andes.fiat.create({ user_id, chain: "stellar", cuit: "20345678901" });

Webhooks

Andes signs outbound webhooks with ECDSA-SHA256. The signed string is `${timestamp}.${rawBody}`; the signature is hex-encoded. Fetch the public key once from GET /webhooks/signing-key and cache it.

Your handler must verify against the raw request body — do not re-serialize JSON.

import { createServer } from "node:http";
import { verifyWebhookSignature, type WebhookEventPayload } from "@andeslabs/mint-sdk";

const PUBLIC_KEY = process.env.ANDES_WEBHOOK_PUBLIC_KEY!; // PEM string

createServer(async (req, res) => {
  const chunks: Buffer[] = [];
  for await (const c of req) chunks.push(c as Buffer);
  const rawBody = Buffer.concat(chunks);

  const ok = verifyWebhookSignature({
    rawBody,
    timestamp: String(req.headers["x-webhook-timestamp"] ?? ""),
    signature: String(req.headers["x-webhook-signature"] ?? ""),
    publicKey: PUBLIC_KEY,
    maxAgeMs: 5 * 60 * 1000,
  });

  if (!ok) return res.writeHead(401).end();

  const event = JSON.parse(rawBody.toString("utf8")) as WebhookEventPayload;
  // handle event …
  res.writeHead(200).end();
}).listen(3000);

Event types

fiat.account.created, fiat.deposit.success, fiat.deposit.failed, fiat.withdrawal.success, fiat.withdrawal.failed, crypto.transfer.success, crypto.transfer.failed, wallet.active, international.offramp.success, international.offramp.failed.

WebhookEventPayload is a discriminated union on event — narrow it in a switch, or use the dispatchWebhookEvent helper below.

Typed event dispatcher

Skip the switch with a typed handler map. Each callback receives only the matching variant, unknown event names fail to compile, and any events you don't list are silently ignored.

import {
  dispatchWebhookEvent,
  type WebhookEventHandlers,
  type WebhookEventPayload,
} from "@andeslabs/mint-sdk";

const handlers: WebhookEventHandlers = {
  "fiat.deposit.success":    async (e) => credit(e.user_id, e.amount, e.mint_receipt_hash),
  "fiat.withdrawal.success": async (e) => settle(e.id, e.destination_cbu),
  "crypto.transfer.failed":  async (e) => flag(e.id, e.to_address),
};

// inside your request handler, after verifying the signature:
const event = JSON.parse(rawBody.toString("utf8")) as WebhookEventPayload;
await dispatchWebhookEvent(event, handlers);

Returning a value from handlers

WebhookEventHandlers<R> is generic — every handler returns R, and dispatchWebhookEvent resolves to R | undefined (undefined when no handler matched the event). Useful when the HTTP layer needs to react to what a handler decided:

const handlers: WebhookEventHandlers<{ retry: boolean }> = {
  "fiat.deposit.success": async (e) => ({ retry: !(await credit(e.user_id, e.amount)) }),
};

const result = await dispatchWebhookEvent(event, handlers);
if (result?.retry) return res.sendStatus(503); // Andes will retry
res.sendStatus(200);

If a handler throws, return a non-2xx to trigger the Andes retry schedule. See examples/webhook-dispatcher.ts.

Headers sent with each delivery

| Header | Description | |---|---| | X-Webhook-Signature | hex ECDSA-SHA256 signature of ${timestamp}.${rawBody} | | X-Webhook-Event | Event type | | X-Webhook-Delivery-Id | UUID | | X-Webhook-Timestamp | Unix ms |

Retries: up to 5 attempts with backoff 10s → 60s → 5m → 30m → 2h.

Releasing

See RELEASE.md for the publish workflow, version bumps, and deprecation steps.

License

MIT