How to Email an Order Confirmation or Invoice PDF to Customers

Generate a branded PDF order confirmation or tax invoice for every new Shopify order and email it to the customer automatically, without a PDF invoice app.

What This Integration Does

Many customers, especially businesses, need a PDF for their records: a tax invoice for their accounts team, or an order confirmation they can forward. Shopify's built-in notifications are emails, not documents, so stores usually install a PDF invoice app. This Spojit workflow does the same job. When an order is created, Shopify calls a Spojit webhook; the workflow lays the order out as a PDF with your business details, the customer's addresses, the line items in a table and the totals, using the PDF Tools create-pdf tool, then emails it to the customer as an attachment with Resend, and keeps a copy in your File Store.

Each order produces one run and one PDF. Spojit deduplicates Shopify's webhook retries on the X-Shopify-Webhook-Id header, so a retried delivery does not email the customer twice.

Prerequisites

  • A Shopify webhook signing connection and an Order creation webhook pointed at the workflow (Settings -> Notifications -> Webhooks in Shopify).
  • A Resend connection with a verified sending domain (or an SMTP Email connection).
  • Your business details for the document: legal name, address and tax number (for example your ABN or VAT number).

Step 1: Webhook Trigger

Add a Trigger node with type Webhook and your Shopify signing connection, response mode Async. The order arrives as {{ input }}, with name, created_at, email, billing_address, shipping_address, line_items, subtotal_price, total_tax, total_price, currency and shipping_lines.

Step 2: Skip Orders That Should Not Get a PDF

Add a Condition that continues only when {{ input.email }} is not empty and, if you only want PDFs for paid orders, {{ input.financial_status }} equals paid. Point-of-sale orders without an email address are skipped.

Step 3: Shape the Line Items

Add a Transform with Iterate over set to {{ input.line_items }}:

{
  "item": "{{ item.title }} {{ item.variant_title }}",
  "sku": "{{ item.sku }}",
  "qty": {{ item.quantity }},
  "unit_price": "{{ item.price }}",
  "line_total": "(item.price multiplied by item.quantity, two decimals)"
}

Use Math Tools for the multiplication, or a short Code Runner step if you also want to format currency. Name the output lines.

Step 4: Build the PDF

Add a Date & Time format step named order_date that formats {{ input.created_at }} as a readable date (for example D MMMM YYYY), then a PDF Tools step with create-pdf:

  • title: Tax invoice {{ input.name }}
  • content:
    # Tax invoice
    
    **Invoice number:** {{ input.name }}
    **Date:** {{ order_date.result }}
    **From:** Acme Pty Ltd, 1 Example Street, Sydney NSW 2000
    **ABN:** 12 345 678 901
    
    ## Bill to
    
    **Name:** {{ input.billing_address.name }}
    **Company:** {{ input.billing_address.company }}
    **Address:** {{ input.billing_address.address1 }}, {{ input.billing_address.city }} {{ input.billing_address.province_code }} {{ input.billing_address.zip }}
    
    ## Totals
    
    **Subtotal:** {{ input.subtotal_price }} {{ input.currency }}
    **Shipping:** {{ input.shipping_lines.0.price }} {{ input.currency }}
    **Tax:** {{ input.total_tax }} {{ input.currency }}
    **Total:** {{ input.total_price }} {{ input.currency }}
    
    ## Items
  • rows: {{ lines }}
  • columns: ["item", "sku", "qty", "unit_price", "line_total"]
  • footer: Thank you for your order. Questions? support@example.com

Each **Label:** value line becomes a row in a label and value list; plain lines next to each other would join into one paragraph, which is why the address uses labels. The items table prints after the content, so the totals appear above it; if you prefer totals at the bottom, add them as a keyValue block in blocks, which prints after the table. Name the output invoice.

Step 5: Email the Customer

Add a Resend step with send-email:

  • from: Acme <orders@yourdomain.com>
  • to: {{ input.email }}
  • subject: Your invoice for order {{ input.name }}
  • html: a short message, for example "Thanks for your order. Your invoice is attached."
  • attachments: [ { "filename": "invoice-{{ input.order_number }}.pdf", "content": "{{ invoice.pdf }}" } ]

Step 6: Keep a Copy

Add a File Store node with Save a file, name invoice-{{ input.order_number }}.pdf, content {{ invoice.pdf }}, content type application/pdf, and If the name is taken set to Overwrite. You can resend any invoice from there.

Tips

  • Logo: add your logo as an image block in blocks (Base64 PNG or JPEG). It prints after the content and table, or use a small wordmark in the title instead.
  • Tax wording: in Australia a document is a tax invoice when it shows your ABN, the words "tax invoice", the date, the items and the GST. Check the rules for where you sell.
  • B2B only: add a Condition on {{ input.billing_address.company }} to send PDFs only to business customers who need them.
  • Other platforms: the same steps work from a WooCommerce or BigCommerce order webhook; only the field names in the templates change.

Common Pitfalls

  • Prices as text: Shopify sends prices as strings such as "89.00". They print fine; convert them only where you calculate.
  • Discounts and refunds: a confirmation at creation shows the original order. If you issue refunds, send a credit note from a separate workflow on Shopify's refund webhook.
  • Unverified sending domain: Resend rejects mail from a domain that is not verified. Verify it before going live, or mail lands in spam or fails.
  • Non-Latin names: names in Chinese, Japanese or Korean script print as ? with the embedded font. Check unsupportedCharacters in the output if you sell in those markets.

Testing

Send yourself a test: create a test order in a development store with your own email address, then open the email and the PDF. Check the business details, the tax line and the totals against the order in Shopify, then enable the webhook on your live store.

Learn More

Ready to build this?

Start on the free plan: 500 AI credits a month, no card needed.

Start free

Did this answer your question? Thanks for the feedback There was a problem submitting your feedback. Please try again later.