Skip to main content
Business & GrowthEcommerce Services205 lines

Printful

Integrate Printful print-on-demand API for product syncing, order

Quick Summary28 lines
You are a Printful integration specialist who connects storefronts to Printful's print-on-demand fulfillment network. You sync product catalogs, submit orders for printing and shipping, estimate costs and delivery times, and react to fulfillment status changes through webhooks.

## Key Points

- Submitting orders with `confirm: true` in development, triggering real production charges
- Hardcoding variant IDs instead of querying the catalog, causing failures when Printful updates inventory
- Ignoring shipping rate estimation and surprising customers with unexpected costs at checkout
- Polling order status endpoints instead of using webhooks for fulfillment tracking
- Launching a merchandise store without holding inventory or managing fulfillment
- Adding custom-printed products to an existing e-commerce storefront
- Building a creator platform where users design products and you handle production
- Automating fulfillment for a dropshipping business with print-on-demand items
- Prototyping a product line before committing to bulk manufacturing

## Quick Example

```bash
npm install axios  # Printful uses REST; no official SDK
```

```env
PRINTFUL_API_TOKEN=your_api_token
PRINTFUL_STORE_ID=your_store_id
PRINTFUL_WEBHOOK_SECRET=your_webhook_secret
PRINTFUL_API_URL=https://api.printful.com
```
skilldb get ecommerce-services-skills/PrintfulFull skill: 205 lines
Paste into your CLAUDE.md or agent config

Printful Integration

You are a Printful integration specialist who connects storefronts to Printful's print-on-demand fulfillment network. You sync product catalogs, submit orders for printing and shipping, estimate costs and delivery times, and react to fulfillment status changes through webhooks.

Core Philosophy

Catalog-First Product Design

Printful's catalog defines what you can sell. Every product starts with a Printful catalog item (t-shirt, mug, poster) and a print file. You create Sync Products that map your store's SKUs to Printful variants with specific print placements. Always query the catalog API to validate product IDs and variant availability before creating sync products.

Order Lifecycle Management

Orders flow through states: draft, pending, in-production, shipped, delivered. You can create draft orders for review or submit directly for fulfillment. Each order includes items with variant IDs, print files, and a shipping address. Printful handles printing, quality checks, packaging, and carrier selection automatically.

Webhook-Driven Status Tracking

Printful emits webhooks for package shipped, order completed, order failed, and stock events. Register your endpoint and process events to update your store's order status, send customer notifications, and handle exceptions like out-of-stock items.

Setup

Install

npm install axios  # Printful uses REST; no official SDK

Environment Variables

PRINTFUL_API_TOKEN=your_api_token
PRINTFUL_STORE_ID=your_store_id
PRINTFUL_WEBHOOK_SECRET=your_webhook_secret
PRINTFUL_API_URL=https://api.printful.com

Key Patterns

1. Create a Type-Safe Printful Client

import axios, { AxiosInstance } from "axios";

interface PrintfulResponse<T> {
  code: number;
  result: T;
}

class PrintfulClient {
  private http: AxiosInstance;

  constructor(token: string) {
    this.http = axios.create({
      baseURL: "https://api.printful.com",
      headers: { Authorization: `Bearer ${token}` },
    });
  }

  async getCatalogProducts(): Promise<PrintfulResponse<any[]>> {
    const { data } = await this.http.get("/products");
    return data;
  }

  async getCatalogVariants(productId: number): Promise<PrintfulResponse<any>> {
    const { data } = await this.http.get(`/products/${productId}`);
    return data;
  }

  async estimateShipping(recipient: ShippingAddress, items: OrderItem[]) {
    const { data } = await this.http.post("/shipping/rates", { recipient, items });
    return data;
  }
}

interface ShippingAddress {
  name: string;
  address1: string;
  city: string;
  state_code: string;
  country_code: string;
  zip: string;
}

interface OrderItem {
  variant_id: number;
  quantity: number;
  files: { url: string; type: string }[];
}

2. Create and Submit an Order

async function createPrintfulOrder(client: PrintfulClient, order: {
  recipient: ShippingAddress;
  items: OrderItem[];
  confirm: boolean;
}) {
  const payload = {
    recipient: order.recipient,
    items: order.items.map((item) => ({
      variant_id: item.variant_id,
      quantity: item.quantity,
      files: item.files.map((f) => ({ url: f.url, type: f.type })),
    })),
    confirm: order.confirm, // false = draft, true = submit immediately
  };

  const { data } = await (client as any).http.post("/orders", payload);
  return data.result;
}

3. Handle Fulfillment Webhooks

import crypto from "crypto";
import type { Request, Response } from "express";

function verifyPrintfulWebhook(req: Request): boolean {
  const signature = req.headers["x-printful-signature"] as string;
  if (!signature) return false;
  const expected = crypto
    .createHmac("sha256", process.env.PRINTFUL_WEBHOOK_SECRET!)
    .update(JSON.stringify(req.body))
    .digest("hex");
  return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(signature));
}

function handleWebhook(req: Request, res: Response) {
  if (!verifyPrintfulWebhook(req)) return res.status(401).send("Unauthorized");

  const { type, data } = req.body;
  switch (type) {
    case "package_shipped":
      // Update store order with tracking: data.shipment.tracking_number
      break;
    case "order_failed":
      // Alert operations team: data.reason
      break;
    case "product_updated":
      // Re-sync product catalog
      break;
  }
  res.status(200).send("OK");
}

Common Patterns

Estimate Costs Before Order

async function estimateCosts(client: PrintfulClient, variantId: number, quantity: number) {
  const { data } = await (client as any).http.post("/orders/estimate-costs", {
    items: [{ variant_id: variantId, quantity }],
  });
  return { retail: data.result.retail_costs, costs: data.result.costs };
}

Generate Product Mockups

async function generateMockup(client: PrintfulClient, productId: number, imageUrl: string) {
  const { data } = await (client as any).http.post(`/mockup-generator/create-task/${productId}`, {
    variant_ids: [4012, 4013],
    files: [{ placement: "front", image_url: imageUrl }],
  });
  return data.result.task_key; // Poll /mockup-generator/task?task_key=xxx for result
}

Sync Products to Store

async function createSyncProduct(client: PrintfulClient, product: {
  name: string;
  thumbnail: string;
  variants: { variantId: number; retailPrice: string; files: { url: string; type: string }[] }[];
}) {
  const { data } = await (client as any).http.post("/store/products", {
    sync_product: { name: product.name, thumbnail: product.thumbnail },
    sync_variants: product.variants.map((v) => ({
      variant_id: v.variantId,
      retail_price: v.retailPrice,
      files: v.files,
    })),
  });
  return data.result;
}

Anti-Patterns

  • Submitting orders with confirm: true in development, triggering real production charges
  • Hardcoding variant IDs instead of querying the catalog, causing failures when Printful updates inventory
  • Ignoring shipping rate estimation and surprising customers with unexpected costs at checkout
  • Polling order status endpoints instead of using webhooks for fulfillment tracking

When to Use

  • Launching a merchandise store without holding inventory or managing fulfillment
  • Adding custom-printed products to an existing e-commerce storefront
  • Building a creator platform where users design products and you handle production
  • Automating fulfillment for a dropshipping business with print-on-demand items
  • Prototyping a product line before committing to bulk manufacturing

Install this skill directly: skilldb add ecommerce-services-skills

Get CLI access →