Send handwritten cards for Shopify orders

Put a pen-written card in the box's wake: when a Shopify order is created, notecardHQ writes and mails a thank-you to the shipping address.

What you need

  • A Shopify app or custom app with an orders/create webhook and its signing secret in SHOPIFY_WEBHOOK_SECRET.
  • A notecardHQ API key in NOTECARDHQ_API_KEY.

The handler

import crypto from "node:crypto";
import express from "express";

const app = express();

app.post("/webhooks/shopify/orders-create", express.raw({ type: "application/json" }), async (req, res) => {
  const digest = crypto
    .createHmac("sha256", process.env.SHOPIFY_WEBHOOK_SECRET)
    .update(req.body)
    .digest("base64");
  const given = Buffer.from(req.get("X-Shopify-Hmac-Sha256") ?? "", "base64");
  if (given.length !== 32 || !crypto.timingSafeEqual(given, Buffer.from(digest, "base64")))
    return res.status(401).end();

  const order = JSON.parse(req.body);
  const s = order.shipping_address;
  if (!s?.address1 || !s.first_name) return res.status(200).end(); // digital order, nothing to mail

  const r = await fetch("https://you.notecardhq.com/api/v1/notes", {
    method: "POST",
    headers: {
      Authorization: `Bearer ${process.env.NOTECARDHQ_API_KEY}`,
      "Content-Type": "application/json",
      // One card per customer: later orders reuse the key and send nothing new.
      "Idempotency-Key": `welcome:shopify:${order.customer?.id ?? order.id}`,
    },
    body: JSON.stringify({
      externalId: String(order.customer?.id ?? order.id),
      recipient: {
        firstName: s.first_name, lastName: s.last_name ?? "",
        businessName: s.company ?? "",
        address1: s.address1, address2: s.address2 ?? "",
        city: s.city, state: s.province_code ?? "", postalCode: s.zip ?? "",
        country: s.country_code,
      },
      content: `Hi ${s.first_name},\n\nThank you for your first order with us. We hope you love it.\n\nThe team`,
    }),
  });
  if (r.status === 429 || r.status >= 500) return res.status(503).end(); // Shopify retries
  res.status(200).end();
});

app.listen(3000);

How "one card per customer" works

The Idempotency-Key is built from the Shopify customer id. The first order creates the note. A later order with the same key either returns that note or gets a 409 if the details changed, so the customer is never sent a second welcome card. To send a card on every order instead, use order:${order.id}.

Check the address first

US addresses are verified before a note is accepted. If the shipping address cannot be delivered, notecardHQ returns 422 and nothing is created. Log it and move on rather than retrying.

VIP and win-back cards

The same handler can send a card to your best customers, and a monthly list upload covers customers who stopped ordering. See handwritten thank-you notes for Shopify stores for the triggers, the Zapier route and what to write.

Field rules, statuses and errors are in the API reference. Prefer another language? See the quickstart.

Send your first note.

Tell us what you want to send and we will set up your workspace.