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

@molecule/api-shipping-easypost

v1.0.3

Published

EasyPost shipping provider for molecule.dev.

Readme

@molecule/api-shipping-easypost

EasyPost shipping provider for molecule.dev.

Implements the @molecule/api-shipping core interface against the EasyPost REST API (https://api.easypost.com/v2). Supports rate quotes, label purchase, label void/refund, and tracker creation across the carriers EasyPost itself supports (USPS, UPS, FedEx, DHL, etc.).

Auto-generated, AI-first package reference for the molecule.dev ecosystem. It is written to be read by coding agents as much as by people, and is generated from this package's source — edit src/index.ts JSDoc, not this file.

Quick Start

import { setProvider } from '@molecule/api-shipping'
import { provider } from '@molecule/api-shipping-easypost'

setProvider(provider)

// Then anywhere in your app:
import { createShipment, createLabel, trackPackage } from '@molecule/api-shipping'
const { shipmentId, rates } = await createShipment({
  from,
  to,
  parcels: [{ length, width, height, weight }],
})
const label = await createLabel(shipmentId, rates[0])

Type

provider

Installation

npm install @molecule/api-shipping-easypost @molecule/api-bond @molecule/api-secrets @molecule/api-shipping

API

Interfaces

ShippingAddress

A postal address used as the origin or destination of a shipment.

interface ShippingAddress {
  /** Recipient or sender name. */
  name?: string
  /** Company name, if applicable. */
  company?: string
  /** Primary street address line. */
  street1: string
  /** Secondary street address line (apartment, suite, etc.). */
  street2?: string
  /** City or locality. */
  city: string
  /** State, province, or region code. */
  state?: string
  /** Postal or ZIP code. */
  postalCode: string
  /** ISO 3166-1 alpha-2 country code. */
  country: string
  /** Contact phone number in E.164 format. */
  phone?: string
  /** Contact email address. */
  email?: string
}

Parcel

Physical parcel dimensions and weight.

interface Parcel {
  /** Parcel length. */
  length: number
  /** Parcel width. */
  width: number
  /** Parcel height. */
  height: number
  /** Parcel weight. */
  weight: number
  /** Linear unit for length, width, and height. Defaults to `'in'` when omitted (all bonds agree). */
  distanceUnit?: 'in' | 'cm'
  /** Mass unit for weight. Defaults to `'lb'` when omitted (all bonds agree). */
  massUnit?: 'lb' | 'oz' | 'kg' | 'g'
}

MonetaryAmount

A monetary amount paired with its currency.

interface MonetaryAmount {
  /** Decimal amount represented as a string to avoid float precision loss. */
  amount: string
  /** ISO 4217 currency code. */
  currency: string
}

DeliveryEstimate

Estimated delivery window, if known.

interface DeliveryEstimate {
  /** Earliest expected delivery date or datetime. */
  earliest?: Date
  /** Latest expected delivery date or datetime. */
  latest?: Date
  /** Number of business days estimated for delivery. */
  businessDays?: number
}

Shipment

Description of a shipment used to request rates or create a label.

interface Shipment {
  /** Origin address. */
  from: ShippingAddress
  /** Destination address. */
  to: ShippingAddress
  /** One or more parcels included in this shipment. */
  parcels: Parcel[]
  /** Optional service-level filter (e.g., a carrier-specific service code). */
  serviceLevel?: string
  /** Optional declared value for insurance/customs. */
  declaredValue?: MonetaryAmount
}

ShippingRate

A rate quote returned by a carrier for a given shipment.

interface ShippingRate {
  /** Carrier identifier (e.g., `usps`, `ups`, `fedex`). */
  carrier: string
  /** Carrier-specific service code or name. */
  service: string
  /** Quoted price for the rate. */
  amount: MonetaryAmount
  /** Estimated delivery window for this rate, if available. */
  deliveryEstimate?: DeliveryEstimate
  /** Provider-assigned identifier used to purchase this rate. */
  rateId?: string
}

ShippingLabel

A purchased shipping label.

interface ShippingLabel {
  /** Provider-assigned label or shipment identifier. */
  id: string
  /** Carrier tracking number assigned to the shipment. */
  trackingNumber: string
  /** URL where the printable label can be downloaded. */
  labelUrl: string
  /** Carrier identifier. */
  carrier: string
  /** Carrier-specific service code or name. */
  service: string
  /** Total amount paid for the label. */
  amount?: MonetaryAmount
}

TrackingEvent

A single event in a package's tracking history.

interface TrackingEvent {
  /** When the event was recorded by the carrier. */
  timestamp: Date
  /** Normalized status at the time of the event. */
  status: TrackingStatusCode
  /** Human-readable description of the event from the carrier. */
  description: string
  /** Free-form location string from the carrier, if provided. */
  location?: string
}

TrackingStatus

Aggregated tracking status for a single tracking number.

interface TrackingStatus {
  /** Carrier identifier. */
  carrier: string
  /** Tracking number being reported on. */
  trackingNumber: string
  /** Current normalized status. */
  status: TrackingStatusCode
  /** Ordered list of tracking events from oldest to newest. */
  events: TrackingEvent[]
  /** Estimated delivery, if reported by the carrier. */
  estimatedDelivery?: DeliveryEstimate
}

ShippingProvider

Shipping provider interface.

All shipping providers must implement this interface to provide rate quoting, label purchasing, label voiding, tracking, and supported-carrier discovery capabilities.

interface ShippingProvider {
  /**
   * Lists carriers supported by this provider.
   *
   * @returns Array of carrier identifiers.
   */
  listSupportedCarriers(): Promise<string[]>
  /**
   * Creates a shipment and returns its provider-assigned id together with the
   * rate quotes for it. This is the primary quoting path: the returned
   * `shipmentId` is the handle {@link createLabel} needs to purchase a label, so
   * callers who intend to buy a label should use this (not {@link getRates}) and
   * persist the id alongside the chosen {@link ShippingRate}.
   *
   * Every provider assigns a shipment an id when it is created (EasyPost's
   * `POST /shipments`, Shippo's `POST /shipments/`), so both bonds return the id
   * and rates natively in a single round-trip — no bond-specific quote helper.
   *
   * @param shipment - The shipment to create and rate.
   * @returns The created shipment's id and its available rates.
   */
  createShipment(shipment: Shipment): Promise<ShipmentQuote>
  /**
   * Requests rate quotes for a shipment, discarding the shipment id.
   *
   * Convenience over {@link createShipment} for display-only flows that quote
   * rates without (yet) purchasing. To buy a label you also need the
   * `shipmentId` — call {@link createShipment} and keep both.
   *
   * @param shipment - The shipment to rate.
   * @returns Array of available rates.
   */
  getRates(shipment: Shipment): Promise<ShippingRate[]>
  /**
   * Purchases a shipping label for the given rate.
   *
   * @param shipmentId - Provider-assigned shipment identifier from a prior
   *   {@link createShipment} call.
   * @param rate - The rate selected for purchase (one of the
   *   {@link ShipmentQuote.rates} returned alongside `shipmentId`).
   * @returns The purchased label.
   */
  createLabel(shipmentId: string, rate: ShippingRate): Promise<ShippingLabel>
  /**
   * Voids a previously purchased label, if permitted by the carrier.
   *
   * @param labelId - Provider-assigned label identifier to void.
   */
  voidLabel(labelId: string): Promise<void>
  /**
   * Retrieves the current tracking status for a package.
   *
   * @param carrier - Carrier identifier.
   * @param trackingNumber - Carrier-assigned tracking number.
   * @returns The aggregated tracking status.
   */
  trackPackage(carrier: string, trackingNumber: string): Promise<TrackingStatus>
}

Types

TrackingStatusCode

Possible high-level tracking statuses, normalized across carriers.

type TrackingStatusCode =
  | 'pre_transit'
  | 'in_transit'
  | 'out_for_delivery'
  | 'delivered'
  | 'available_for_pickup'
  | 'return_to_sender'
  | 'failure'
  | 'unknown'

Functions

listSupportedCarriers()

Lists the carriers supported by this EasyPost bond.

function listSupportedCarriers(): Promise<string[]>

Returns: Lowercase carrier identifiers.

createShipment(shipment)

Creates an EasyPost shipment via POST /shipments and returns its shipment id together with the normalized rates. The shipmentId is the handle {@link createLabel} needs to buy a label (POST /shipments/:id/buy), so use this (not {@link getRates}) when you intend to purchase — one round-trip yields both pieces, matching the core createShipment contract that @molecule/api-shipping-shippo also satisfies natively.

function createShipment(shipment: Shipment): Promise<ShipmentQuote>
  • shipment — Normalized shipment payload.

Returns: The EasyPost shipment id and its normalized rates.

getRates(shipment)

Requests rate quotes for a shipment, discarding the EasyPost shipment id. Convenience over {@link createShipment} for display-only flows; to buy a label you also need the shipmentId, so call {@link createShipment} instead.

function getRates(shipment: Shipment): Promise<ShippingRate[]>
  • shipment — Normalized shipment payload.

Returns: Array of normalized shipping rates.

createLabel(shipmentId, rate)

Purchases a shipping label for a previously-quoted rate.

function createLabel(shipmentId: string, rate: ShippingRate): Promise<ShippingLabel>
  • shipmentId — EasyPost shipment ID returned from a prior {@link createShipment} call.
  • rate — The rate selected for purchase. Must include rateId.

Returns: The purchased label normalized to ShippingLabel.

voidLabel(labelId)

Voids (refunds) a previously purchased label. EasyPost handles void via POST /shipments/:id/refund.

function voidLabel(labelId: string): Promise<void>
  • labelId — EasyPost shipment ID associated with the label.

trackPackage(carrier, trackingNumber)

Retrieves the current tracking status for a package. Creates a tracker via POST /trackers (idempotent — EasyPost reuses an existing tracker for the same carrier + tracking code) and normalizes the response.

function trackPackage(carrier: string, trackingNumber: string): Promise<TrackingStatus>
  • carrier — Carrier identifier (e.g., usps, ups).
  • trackingNumber — Carrier-assigned tracking number.

Returns: Normalized tracking status.

Constants

provider

The EasyPost shipping provider implementing the ShippingProvider interface.

const provider: ShippingProvider

shippingEasypostSecretDefinitions

Secret definitions required by the EasyPost shipping bond.

const shippingEasypostSecretDefinitions: SecretDefinition[]

Core Interface

Implements @molecule/api-shipping interface.

Bond Wiring

Setup function to register this provider with the core interface:

import { setProvider } from '@molecule/api-shipping'
import { provider } from '@molecule/api-shipping-easypost'

export function setupShippingEasypost(): void {
  setProvider(provider)
}

Injection Notes

Requirements

Peer dependencies:

  • @molecule/api-bond ^1.0.1
  • @molecule/api-secrets ^1.0.1
  • @molecule/api-shipping ^1.0.1

Environment Variables

Runtime Dependencies

  • @molecule/api-bond

  • @molecule/api-secrets

  • @molecule/api-shipping

  • Requires EASYPOST_API_KEY in the environment (read per request — fail-fast error if unset). Optionally EASYPOST_API_URL to override the base URL (sandbox / proxy).

  • createLabel(shipmentId, rate) needs the EasyPost shipment id from the SAME quote. Use the core createShipment(shipment) → { shipmentId, rates } and persist BOTH between quote and purchase; plain getRates() discards the id.

  • One parcel per shipment. An EasyPost shipment carries exactly one parcel (its API has a single parcel field, not an array), so passing parcels.length > 1 THROWS rather than silently dropping the extras — send each parcel as its own shipment, or use -shippo for multi-piece shipments.

  • Parcel.distanceUnit/massUnit are honored. Dimensions are converted to inches and weight to ounces (EasyPost's only accepted units — its Parcel object has no unit fields) before the call: cm→in, lb/kg/g→oz, so a metric parcel is priced correctly. Units default to 'in'/'lb' when unspecified, matching the -shippo bond so a unit-less parcel is priced the same by either provider.

  • voidLabel(labelId) refunds via POST /shipments/:id/refund — pass ShippingLabel.id (the EasyPost shipment id) returned by createLabel.

E2E Tests

Integration checklist — drive the real UI (live preview, no mocks), adapt each item to this app's actual screens/flows, and check every box off one by one. A box you can't check is an integration bug to fix — not a skip. Run the whole flow against the provider's TEST mode (a test API key — e.g. EasyPost/Shippo test keys) so rates and labels are free test artifacts that carry test tracking numbers; never mock the carrier, and never flip to a production key just to "make it real" (a real label purchase costs money):

  • [ ] Requesting RATES for a real parcel (from + to address, weight, dimensions) renders MULTIPLE carrier/service options in the UI, each with a price AND an ETA — never an empty list, a spinner that never resolves, or a null/$0 amount. Remember amount.amount is a STRING (display it, don't NaN it).
  • [ ] The rates REFLECT the input: re-quote with a heavier or farther-away parcel and the prices go UP (compare decimal-safe, not as floats) — proving live carrier quotes, not a hardcoded/fixture list.
  • [ ] Buying a LABEL for a rate the user PICKED from that quote returns a real artifact (a PDF/PNG/ZPL) plus a trackingNumber, both shown in the UI. Pass back the exact ShippingRate object from getRates — its rateId is the purchase handle, so a hand-rebuilt rate is rejected.
  • [ ] The label artifact is FETCHED and stored on the app's own storage/ uploads and the UI links to that copy — NOT the raw labelUrl, which is an expiring vendor URL that 404s once it lapses.
  • [ ] TRACKING a purchased shipment (trackPackage with the label's carrier + trackingNumber) returns a real status plus an ordered event history in the UI, and re-tracking as the parcel moves advances the status (pre_transit → in_transit → delivered) instead of a frozen placeholder. If the app wires an inbound carrier tracking webhook, a delivered callback advances the STORED status AND a forged/unsigned callback is rejected.
  • [ ] A bad/undeliverable address or a provider error surfaces a graceful, readable message in the UI (not a raw stack trace, not a silent empty rate list) — user-supplied addresses/parcels are validated server-side before they reach the carrier.
  • [ ] SECURITY — the provider API key stays server-side only (this package is server-only and never ships to the client bundle), and a user can only rate, label, void, or track their OWN shipments: guessing another user's label id or tracking number must NOT return that label artifact or tracking status.