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

@spindlecommerce/plugin-klaviyo

v1.0.0

Published

Klaviyo for SpindleCommerce: customer profiles, list subscriptions for customers who accept marketing, and order events (Placed Order, Ordered Product, Fulfilled, Cancelled and Refunded Order).

Readme

Klaviyo, a SpindleCommerce plugin

Customer profiles, list subscriptions and order events in Klaviyo, for its flows, segments and reports:

  • Profiles. Customers' names, phone numbers and locales, kept up to date as they register and change their details.
  • Subscriptions. Customers who accept marketing join a list of your choice, subscribed to email marketing with their consent.
  • Orders. Placed Order once an order's payment goes through, Ordered Product for each line, and Fulfilled, Canceled and Refunded Order as orders ship, are canceled and are refunded. These are Klaviyo's standard ecommerce events, which its order flows and revenue reports use.

The plugin calls Klaviyo's REST API, revision 2026-10-15, as the platform delivers its events. When Klaviyo is unreachable or answers with an error, the platform retries the delivery.

Setup

  1. Add the plugin to your SpindleCommerce installation and deploy:

    pnpm add @spindlecommerce/plugin-klaviyo
    // ecom.config.ts
    import { klaviyo } from "@spindlecommerce/plugin-klaviyo";
    
    export default defineConfig({ plugins: [email(), stripe(), klaviyo()] });

    It needs plugin SDK 1.1, for customers' marketing consent and orders' coupon codes.

  2. Create a private API key in Klaviyo, under Settings › API keys › Create Private API Key. Make it a custom key with:

    • read access to Accounts, for Test connection;
    • full access to Events, Lists, Profiles and Subscriptions.
  3. Settings › Plugins › Klaviyo. Enter the key. On a server, the KLAVIYO_PRIVATE_API_KEY environment variable can supply it instead. Test connection names the Klaviyo account the key belongs to.

  4. Choose a list for customers who accept marketing: copy its ID from the list's settings in Klaviyo into List ID. Without one, nobody is subscribed.

  5. Map the metrics in Klaviyo. Under Analytics › Metrics › Edit mapped metrics, map:

    • Placed Order as revenue;
    • Ordered Product as ordered product;
    • Canceled Order as cancelled sales;
    • Refunded Order as refunded sales.

    Klaviyo's predictive analytics, CLV and benchmark reports read these mappings.

Settings

  • Private API key (secret, KLAVIYO_PRIVATE_API_KEY).
  • List ID: customers who accept marketing join this list. Empty subscribes no one.
  • Send Ordered Product events (on by default): one per order line, besides Placed Order.

Events sent

| Klaviyo event | Sent on | unique_id | value | Properties besides the order's | |---|---|---|---|---| | Placed Order | The first of order.payment_authorized and order.payment_captured, once per order | The order's ID | The order's total | — | | Ordered Product | The same, one per line, when turned on | <order ID>:<line ID> | The line's total | Its own: OrderId, ProductID, SKU, ProductName, Quantity, ItemPrice, RowTotal | | Fulfilled Order | order.fulfilled, one per shipment | <order ID>:<fulfillment number> | The order's total | FulfillmentNumber, Tracking (Carrier, Number, URL), FulfilledItems | | Canceled Order | order.canceled, if Placed Order was sent | The order's ID | The order's total | Reason | | Refunded Order | order.refunded, one per refund | The order's ID for the refund that completes it; <order ID>:<refund number> before that | The amount refunded | RefundNumber |

  • The order's properties:
    • OrderId: the order number, such as US-000042.
    • Items: ProductID, SKU, ProductName, Quantity, ItemPrice and RowTotal for each line.
    • ItemNames, and DiscountCodes (the coupon codes entered).
    • Subtotal, Discount, Shipping and Tax.
  • Amounts are in the order's currency (value_currency), in major units: 19.99, not 1999.
  • The profile of each order event is the order's email. Its name comes from the billing address (the buyer's), else the shipping address.
  • Times. Placed Order and Ordered Product carry the time the order was created. The others carry the time of the shipment, cancellation or refund.
  • Once each. Klaviyo records one event per profile, metric and unique_id, so a delivery the platform runs twice is not counted twice.
  • Why not order.placed? It fires before payment, and an order whose payment is declined is canceled at once. So Klaviyo hears only of orders that were paid for, and Canceled Order follows only a Placed Order.

Customers (customer.created, customer.updated) are upserted through profile import, by email: first and last name, phone number and locale (the one they last shopped in). Fields the store has no value for are left as they are in Klaviyo.

Consent

  • Only with consent. A customer is subscribed only when a list is set and they accept marketing (acceptsMarketing, given when registering or in their account). Everyone else gets a profile and their order events, and is never subscribed.
  • How. Klaviyo's bulk subscribe job records their consent to email marketing, given now, with SpindleCommerce as its source. The list's opt-in setting applies: with double opt-in, Klaviyo asks them to confirm by email first.
  • Once per opt-in. Klaviyo's subscribe lifts unsubscribes, so customers are not subscribed again each time their account changes: someone who unsubscribed through a Klaviyo email stays unsubscribed. They are subscribed again if they withdraw consent in the store and give it again, or change their email address.
  • Withdrawing consent isn't passed on. A customer who stops accepting marketing in the store stays subscribed in Klaviyo until they unsubscribe through its emails, or staff unsubscribe them there.
  • Email only. Phone numbers go on profiles, but nobody is subscribed to SMS.

Limitations

  • No backfill. Customers and orders from before the plugin was set up aren't sent. A customer is synced at their next change. Import history with Klaviyo's CSV import.
  • Phone numbers are sent only in international format (+44 20 7946 0000). The profile syncs without them when they are in another format, or Klaviyo refuses them (another profile has the number, say).
  • Profiles are matched by email. A customer who changes their email address gets a new profile. The old one keeps its history and subscription.
  • Items carry no product URLs, images, categories or brands.
  • Refunds and CLV. Klaviyo ties Refunded Order and Canceled Order to a Placed Order by unique_id in CLV.
    • The refund that leaves an order fully refunded uses the order's ID, so the order leaves the customer's lifetime value.
    • Partial refunds before it get their own IDs: they are recorded, but don't lower CLV.
    • Canceled Order uses the order's ID, as Placed Order does.
  • Event names. These follow Klaviyo's guide for platforms without a pre-built integration. It spells Canceled Order with one "l"; Klaviyo's Shopify integration sends "Cancelled Order". Map whichever you use as cancelled sales.
  • API revision. Klaviyo deprecates a revision a year after its release and retires it a year later, then answers as the next one. 2026-10-15 is stable until October 2027 and retired in October 2028. Raise REVISION in src/klaviyo.ts before then, after checking Klaviyo's changelog.

Develop

pnpm install
pnpm test
pnpm build

The tests run the plugin against a fake Klaviyo API. The plugin API it builds on is @spindlecommerce/plugin-sdk.

Release

Each version is staged on npm and goes live when a maintainer approves it.

  1. Raise version in package.json and in src/index.ts. A test checks they match.
  2. Commit, then tag and push: for example git tag v1.0.1 && git push origin main v1.0.1.
  3. The Release workflow tests, builds and stages the version, and creates the GitHub release.
  4. Approve it on npmjs.com, under Staged Packages, with 2FA.

The workflow publishes with npm's trusted publishing, so it needs no token. Before its first release, a maintainer links the package to the workflow:

npm trust github @spindlecommerce/plugin-klaviyo --repo spindlecommerce/plugin-klaviyo --file release.yml --allow-stage-publish

Run it just before tagging: npm drops a link that isn't used within 48 hours.

License

MIT