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

@sellersmith/merchize-sdk

v0.1.0

Published

TypeScript SDK for Merchize fulfillment API

Readme

@sellersmith/merchize-sdk

TypeScript SDK for the Merchize print-on-demand fulfillment API.

[!WARNING] Monthly Token Renewal Required

Merchize access tokens expire every month. Your integration must handle token rotation or all API calls will fail with 401 errors. See Token Rotation below.


Installation

npm install @sellersmith/merchize-sdk

Quick Start

import { Merchize } from "@sellersmith/merchize-sdk";

const client = new Merchize({
  baseUrl: "https://bo-group-1-2.merchize.com/{store_id}/bo-api",
  accessToken: "your-monthly-access-token",
});

// Import an order for fulfillment
const result = await client.orders.import({
  order_id: "shopify-1001",
  shipping_info: {
    full_name: "Jane Doe",
    address_1: "123 Main St",
    city: "Austin",
    state: "TX",
    postcode: "78701",
    country: "US",
    email: "[email protected]",
    phone: "+15125550100",
  },
  items: [
    {
      name: "Classic Tee",
      product_id: 42,
      sku: "TEE-WHT-M",
      merchize_sku: "MCZ-TEE-001",
      quantity: 1,
      price: 15.99,
      currency: "USD",
      image: "https://cdn.example.com/tee.png",
      design_front: "https://cdn.example.com/design-front.png",
      attributes: [
        { name: "Color", option: "White" },
        { name: "Size", option: "M" },
      ],
    },
  ],
});

console.log(result.data.id);

All request/response fields use snake_case.

Configuration

const client = new Merchize({
  baseUrl: "https://bo-group-1-2.merchize.com/{store_id}/bo-api",
  accessToken: "your-access-token",
  timeout: 30000,
  enableLogging: false,
  onTokenExpired: () => {
    console.error("Merchize token expired — renewal required");
  },
});

| Option | Type | Required | Default | Description | |---|---|---|---|---| | baseUrl | string | Yes | — | API base URL from Integrations > API in your dashboard | | accessToken | string | Yes | — | Monthly access token from your dashboard | | timeout | number | No | 30000 | Request timeout in milliseconds | | onTokenExpired | () => void | No | — | Callback fired automatically on 401 responses | | enableLogging | boolean | No | false | Log requests and responses to console |

Token Rotation

Tokens have a monthly expiry. When expired, all calls return 401.

import { Merchize } from "@sellersmith/merchize-sdk";

const client = new Merchize({
  baseUrl: process.env.MERCHIZE_BASE_URL!,
  accessToken: await loadTokenFromDb(),
  onTokenExpired: async () => {
    // Alert your team or trigger an automated renewal flow
    console.error("Merchize token expired — renew at dashboard: Integrations > API");
    // After obtaining the new token, hot-swap without reinstantiating:
    // const newToken = await renewToken();
    // client.setAccessToken(newToken);
  },
});

// Update the token at any time without creating a new client
client.setAccessToken("new-monthly-token");

Store your token in a secrets manager (AWS Secrets Manager, Vault, etc.) and rotate it on a schedule before expiry.


API Reference

Orders

All 13 methods are on client.orders.

orders.import(order)

Import an order for fulfillment.

import(order: CreateOrderRequest): Promise<MerchizeResponse<ImportOrderResponse>>
const res = await client.orders.import({
  order_id: "my-order-123",      // your external order ID
  identifier: "shopify",         // optional — adds tag "api_shopify"
  shipping_info: {
    full_name: "John Smith",
    address_1: "456 Oak Ave",
    address_2: "Apt 2B",         // optional
    city: "New York",
    state: "NY",
    postcode: "10001",
    country: "US",               // ISO 3166-1 alpha-2
    email: "[email protected]",
    phone: "+12125551234",
  },
  items: [
    {
      name: "Custom Hoodie",
      product_id: 7,
      sku: "HOODIE-BLK-L",
      merchize_sku: "MCZ-HOOD-007",
      quantity: 2,
      price: 29.99,
      currency: "USD",
      image: "https://cdn.example.com/hoodie.png",
      design_front: "https://cdn.example.com/front.png",
      design_back: "https://cdn.example.com/back.png",  // optional
      attributes: [
        { name: "Color", option: "Black" },
        { name: "Size", option: "L" },
      ],
    },
  ],
  tags: ["vip", "rush"],         // optional
});

orders.getDetail(params)

Get order detail by Merchize code, external number, or identifier.

getDetail(params: OrderLookupParams): Promise<MerchizeResponse<OrderDetail>>
// params: { code?, external_number?, identifier? }
const order = await client.orders.getDetail({ code: "RX-0001-ABCD" });
const order = await client.orders.getDetail({ external_number: "shopify-1001" });
const order = await client.orders.getDetail({ external_number: "1001", identifier: "shopify" });

orders.getInvoice(params)

Get invoice/cost breakdown for an order.

getInvoice(params: OrderLookupParams): Promise<MerchizeResponse<OrderInvoice>>
const invoice = await client.orders.getInvoice({ code: "RX-0001-ABCD" });
console.log(invoice.data.fulfillment_cost.total);

orders.getProgress(params)

Get fulfillment progress for an order.

getProgress(params: OrderLookupParams): Promise<MerchizeResponse<OrderDetail>>
const progress = await client.orders.getProgress({ code: "RX-0001-ABCD" });

orders.getTracking(params)

Get shipment tracking info for an order.

getTracking(params: OrderLookupParams): Promise<MerchizeResponse<OrderTracking>>
const tracking = await client.orders.getTracking({ code: "RX-0001-ABCD" });
console.log(tracking.data.status, tracking.data.service);

orders.updateStatus(request)

Pause (hold) or resume an order.

updateStatus(request: OrderLinkRequest): Promise<MerchizeResponse<boolean>>
// OrderLinkRequest: { order: { code, external_number, identifier, action: "hold" | "resume" } }
// Pause an order
await client.orders.updateStatus({
  order: { code: "RX-0001-ABCD", external_number: "shopify-1001", identifier: "shopify", action: "hold" },
});

// Resume a paused order
await client.orders.updateStatus({
  order: { code: "RX-0001-ABCD", external_number: "shopify-1001", identifier: "shopify", action: "resume" },
});

orders.push(request)

Push an order to fulfillment.

push(request: OrderLinkRequest): Promise<MerchizeResponse<boolean>>
await client.orders.push({
  order: { code: "RX-0001-ABCD", external_number: "shopify-1001", identifier: "shopify" },
});

orders.cancel(request)

Cancel an order.

cancel(request: OrderLinkRequest): Promise<MerchizeResponse<boolean>>
await client.orders.cancel({
  order: { code: "RX-0001-ABCD", external_number: "shopify-1001", identifier: "shopify" },
});

Batch methods

All batch methods accept an OrderListRequest — an object with an order array.

// Common type for all batch methods
interface OrderListRequest {
  order: Array<{ code: string; external_number: string; identifier: string }>;
}

const req = {
  order: [
    { code: "RX-0001-ABCD", external_number: "1001", identifier: "shopify" },
    { code: "RX-0002-EFGH", external_number: "1002", identifier: "shopify" },
  ],
};

| Method | Return type | Description | |---|---|---| | orders.listDetails(request) | MerchizeResponse<OrderDetail[]> | Get details for multiple orders | | orders.listInvoices(request) | MerchizeResponse<OrderInvoice[]> | Get invoices for multiple orders | | orders.listProgress(request) | MerchizeResponse<OrderProgressDetail[]> | Get progress for multiple orders | | orders.listTracking(request) | MerchizeResponse<OrderTracking[]> | Get tracking for multiple orders | | orders.listTickets(request) | MerchizeResponse<OrderTicket[]> | Get tickets for multiple orders |

const details = await client.orders.listDetails(req);
const invoices = await client.orders.listInvoices(req);
const progresses = await client.orders.listProgress(req);
const trackings = await client.orders.listTracking(req);
const tickets = await client.orders.listTickets(req);

Products

products.getCatalog(params?)

Get the product catalog. Maximum 50 products per page.

getCatalog(params?: { page?: number; limit?: number }): Promise<MerchizeResponse<ProductCatalogPage>>
// ProductCatalogPage: { products: Product[]; total: number; limit: number; page: number }
const page1 = await client.products.getCatalog({ page: 1, limit: 50 });
page1.data.products.forEach((p) => console.log(p._id, p.title));

products.searchCollections(params?)

Search product collections by name.

searchCollections(params?: { limit?: number; name?: string; page?: number }): Promise<MerchizeResponse<Paging<Collection[]>>>
const results = await client.products.searchCollections({ name: "t-shirt", page: 1, limit: 20 });
results.data.data.forEach((c) => console.log(c._id, c.name));

products.getAllVariants(productId)

Get all variants for a specific product.

getAllVariants(productId: string): Promise<MerchizeResponse<Variant[]>>
const variants = await client.products.getAllVariants("product-id-here");
variants.data.forEach((v) => {
  console.log(v.sku, v.title);
  v.options.forEach((o) => console.log(o.attribute.name, o.value));
});

Tickets

tickets.create(request)

Open a support ticket for one or more orders.

create(request: CreateTicketRequest): Promise<MerchizeResponse<Ticket>>
const ticket = await client.tickets.create({
  order_codes: ["RX-0001-ABCD"],      // Merchize order codes
  category: "Wrong item",
  subcategory: "Wrong size",
  product_type: ["T-Shirt"],          // optional
  preferred: "Reprint",               // optional preferred resolution
  description: "Customer received size M instead of L.",  // optional
  images: [                           // optional
    { original_name: "photo.jpg", image_url: "https://cdn.example.com/photo.jpg" },
  ],
});

console.log(ticket.data._id);

tickets.update(ticketId, request)

Add a comment/message to an existing ticket.

update(ticketId: number, request: { message: string; images?: TicketImage[] }): Promise<MerchizeResponse<UpdateTicket>>
await client.tickets.update(12345, {
  message: "Replacement has been dispatched.",
  images: [
    { original_name: "label.png", image_url: "https://cdn.example.com/label.png" },
  ],
});

tickets.reopen(ticketId, request)

Reopen a resolved ticket.

reopen(ticketId: number, request: { message: string; is_escalate: boolean; cf_satisfied?: string }): Promise<MerchizeResponse<UpdateTicket>>
await client.tickets.reopen(12345, {
  message: "Issue persists — replacement also arrived damaged.",
  is_escalate: true,
});

tickets.resolve(ticketId)

Mark a ticket as resolved.

resolve(ticketId: number): Promise<MerchizeResponse<UpdateTicket>>
await client.tickets.resolve(12345);

Error Handling

All failed API calls throw MerchizeError.

import { Merchize, MerchizeError } from "@sellersmith/merchize-sdk";

try {
  const order = await client.orders.getDetail({ code: "RX-NONEXISTENT" });
} catch (err) {
  if (err instanceof MerchizeError) {
    console.error(err.status);       // HTTP status code, e.g. 404
    console.error(err.message);      // human-readable message
    console.error(err.responseBody); // raw response body string
  } else {
    // Network error, timeout, etc.
    throw err;
  }
}

| Property | Type | Description | |---|---|---| | status | number | HTTP status code | | message | string | Error message | | responseBody | string | Raw response body for debugging |


Base URL

Get your baseUrl from the Merchize Dashboard > Integrations > API.

The URL follows the format:

https://bo-group-X-Y.merchize.com/{store_id}/bo-api

Tokens are store-scoped. Each store has its own base URL and access token.


Requirements

  • Node.js 18+ (uses native fetch)
  • Zero runtime dependencies

License

MIT — see LICENSE