CommerceForAI

Print-on-demand

Connect Printful, Printify or both. Each product variant is mapped to a provider. When an order is paid, it's sent to the right provider with the right artwork. When the provider ships, the order is fulfilled and the customer gets tracking.

Connect a provider

Call once per provider. Both can be connected at the same time.

await commerce.store.configureFulfillment({
  provider: "printify",
  api_key: process.env.PRINTIFY_API_KEY!,
  printify_shop_id: "12345678",
});

await commerce.store.configureFulfillment({
  provider: "printful",
  api_key: process.env.PRINTFUL_API_KEY!,
});

Orders wait for your approval in the provider's dashboard (Printful drafts, Printify “on hold”) until you pass auto_confirm: true. Start with approval on, and turn it off once you trust the flow.

Map variants

Artwork usually depends on the design, not the size, so set it once on the product.

await commerce.products.create({
  name: "Half Dome Puff Tee",
  metadata: { handle: "half-dome-puff", collection: "yosemite" },
  print_files: [{ placement: "front", url: "https://cdn.example.com/half-dome.png" }],
  variants: [
    // Printify: catalog ids → a product is created per order with your artwork
    { name: "M", sku: "HD-M", price: 4300, fulfillment: { printify: { blueprint_id: 706, print_provider_id: 99, variant_id: 73201 } } },
    { name: "L", sku: "HD-L", price: 4300, fulfillment: { printify: { blueprint_id: 706, print_provider_id: 99, variant_id: 73202 } } },
  ],
});

await commerce.products.create({
  name: "Embroidered Cap",
  print_files: [{
    placement: "embroidery_front",
    url: "https://cdn.example.com/cap.png",
    options: [{ id: "thread_colors", value: ["#FFFFFF", "#96A1A8"] }],
  }],
  variants: [
    { name: "One size", sku: "CAP", price: 3200, fulfillment: { printful_variant_id: 7854 } },
  ],
});

For a design you already built in Printify, map to it directly with { printify: { product_id, variant_id } }.

Custom products: artwork per order

Line items can carry the customer's choices (properties, shown on Stripe's payment page) and their own artwork (print_files, a public https URL that stays up until the provider downloads it). Artwork on the line wins over the variant's, which wins over the product's.

await commerce.checkouts.create({
  items: [{
    variant_id,
    quantity: 1,
    properties: { name: "Riley", parks: "Zion, Arches, Yosemite", count: "3 of 63" },
    print_files: [{ placement: "front", url: renderedPngUrl }],
  }],
  success_url,
});

What happens after payment

  • One provider order per provider. A cart with a puff-print tee and an embroidered cap becomes one Printify order and one Printful order.
  • order.fulfillment_provider_orders shows each provider order's id and status.
  • Each package that ships emits order.shipment_created.
  • The order is marked fulfilled automatically once every item is print-on-demand and every provider has shipped. The shipped email lists every package.
  • If a provider rejects an order (for example, missing artwork), you get fulfillment.failed with the reason. Fix it and call orders.submitFulfillment(order).

Provider webhooks are never trusted as-is: CommerceForAI re-fetches the order from the provider before acting on it.