Enhance Your Shopify Contact Forms with Basin for Custom Solutions

Shopify is a powerful e-commerce platform, but its default contact form helper is quite limited, allowing you only to receive an email sent to your Shopify account's email address. If you need more flexibility and advanced features, consider using Basin, a form handling service that can unlock new possibilities and achieve amazing custom results for your contact forms. In this blog post, we'll demonstrate how to replace Shopify's default contact form helper with Basin and create a custom solution using webhooks and a Cloudflare Worker.

Basin's Features

Unlike Shopify's limited contact form solution, Basin offers a wide range of features and configuration options, including:

  1. Spam protection: Basin uses built-in spam filtering to block unwanted submissions and keep your inbox clean.
  2. Email notifications: You can set up custom email notifications that are sent to you when a form is submitted, ensuring you never miss a new message.
  3. Autoresponders: Configure automatic replies to form submissions, providing an immediate response to your users.
  4. Webhooks: Send form data to other services, such as Zapier, Integromat, or custom endpoints, for further processing and automation.
  5. Export data: Export form submissions as CSV or JSON files to analyze and process the data however you need.
  6. API access: Retrieve form data programmatically using Basin's API for more advanced integrations.
  7. Integrations: Easily forward your submissions to Zapier, Make, Pabbly, and build out workflows as needed.

Use Case: Shopify Purchase Order Form to PDF emailed to the vendor

In this post, we will explore an advanced use case. We have a purchase order form for resellers of our product. A plugin within Shopify powers a vendor login experience and allows vendors to access the purchase order form.

Upon submitting the form, we want to achieve the following:

  1. Display a success message to user.
  2. Email a PDF of the purchase order to the vendor who submitted the form.
  3. CC the email to our internal team.

This sounds easier than it is. Converting a form submission to PDF is not simple. It takes some fiddling to fine-tune the HTML template. 

To accomplish this, we will follow these steps:

  1. Point your Shopify form to Basin
  2. Use Basin to process the form submission
  3. Set up a Basin webhook to trigger a Cloudflare Worker
  4. Use the Cloudflare Worker to process the form data and perform custom actions

Let's dive into each step.

Step 1: Point your Shopify form to Basin

First, create an account on Basin and set up a new form. Copy the form's endpoint URL provided by Basin. Next, update your Shopify purchase order form's action attribute to point to the Basin form URL:

Be sure to use your endpoint instead of the one in this example. Also, be sure to replace the closing liquid form tag with a closing HTML form tag.

Another tip is to make sure your form fields are named appropriately. The naming and nesting of the form fields control how they will appear in Basin and later steps.

Step 2: Use Basin to process the form submission

Once you've pointed your form to Basin, it will handle form submissions and store the data. You can also configure Basin to send email notifications, set up autoresponders, and more.

Step 3: Set up a Basin webhook to trigger a Cloudflare Worker

In Basin, go to the form settings and add a new webhook. This webhook will send a POST request to your Cloudflare Worker whenever a form is submitted. Copy your Cloudflare Worker's URL and paste it into the Basin webhook URL field.

Step 4: Use the Cloudflare Worker to process the form data and perform custom actions

Now it's time to create a Cloudflare Worker that will receive the webhook from Basin, process the form data, and perform custom actions. In our example, the Cloudflare Worker does the following:

  1. Sends the form data to a Lambda service for rendering an HTML template
  2. Converts the rendered HTML to a PDF using Api2Pdf service
  3. Emails the PDF as an attachment using SendGrid

To create the Cloudflare Worker, follow the code provided in the previous responses in this thread, and replace the necessary placeholders (e.g., API keys, email addresses) with your own values.

addEventListener("fetch", (event) => {

const LAMBDA_URL = "https://redacted.lambda-url.us-west-2.on.aws/";
const API2PDF_URL = "https://v2.api2pdf.com/chrome/pdf/html";
const SENDGRID_API_KEY = "redacted";
const API2PDF_API_KEY = "redacted";

// https://codepen.io/anthonypenner/pen/NWONRQx
const HTML = `removed --- see codepen link above`;

async function handleRequest(request) {
  if (request.method !== "POST") {
    return new Response("Method not allowed", { status: 405 });

  const payload = await request.json();
  const email = payload.email;
  const po_number = payload.po_number;
  const data = {
    html: HTML,
    data: payload.data,


  // 1. POST to Lambda service
  const lambdaResponse = await fetch(LAMBDA_URL, {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify(data),

  if (!lambdaResponse.ok) {
    console.log("Error in Lambda service");
    console.log(await lambdaResponse.text());
    return new Response("Error in Lambda service", { status: 500 });

  const renderedTemplate = await lambdaResponse.text();

  // 2. POST to Api2Pdf
  const api2PdfResponse = await fetch(API2PDF_URL, {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      Authorization: `${API2PDF_API_KEY}`,
    body: JSON.stringify({
      html: renderedTemplate,
      options: { scale: 1 },

  if (!api2PdfResponse.ok) {
    return new Response("Error in Api2Pdf service", { status: 500 });

  const pdfResult = await api2PdfResponse.json();
  const pdfUrl = pdfResult.FileUrl;

  // 3. Email PDF using SendGrid
  const sendgridResponse = await sendEmailWithPdf(email, pdfUrl);

  if (!sendgridResponse.ok) {
    console.log("Error in SendGrid service");
    console.log(await sendgridResponse.text());
    return new Response("Error in SendGrid service", { status: 500 });

  return new Response("Email sent successfully", { status: 200 });

async function sendEmailWithPdf(email, pdfUrl) {
  const attachment = await fetch(pdfUrl);
  const attachmentBuffer = await attachment.arrayBuffer();
  const attachmentBase64 = btoa(String.fromCharCode(...new Uint8Array(attachmentBuffer)));

  const data = {
    personalizations: [
        to: [{ email: email }],
        cc: [{ email: "redacted" }],
    from: { email: "noreply@redacted" },
    subject: `PO ${po_number}: Integrity Woodcraft`,
    content: [
        type: "text/plain",
        value: `Please find the attached PDF purchase order ${po_number}.`,
    attachments: [
        content: attachmentBase64,
        filename: "integrity_woodcraft_po.pdf",
        type: "application/pdf",
        disposition: "attachment",

  return fetch("https://api.sendgrid.com/v3/mail/send", {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      Authorization: `Bearer ${SENDGRID_API_KEY}`,
    body: JSON.stringify(data),

Here is the main part of the AWS lambda code that powers our handlebars.js solution.

const AWS = require('aws-sdk');
const Handlebars = require('handlebars');

exports.handler = async (event) => {
    const requestBody = JSON.parse(event.body);
    const html = requestBody.html;
    const data = requestBody.data;
    const template = Handlebars.compile(html);
    const result = template(data);
    return {
        statusCode: 200,
        headers: { "Content-Type": "text/html" },
        body: result

And of course, the end result is a clean-looking PDF copy of the original purchase order including all the form fields filled out by our vendor on Shopify. If you inspect the HTML template on Codepen you will notice that we use handlebars to iterate over the line items to dynamically include as many rows as necessary and exclude the blank ones.

By using Basin in conjunction with Shopify, you can create custom solutions for your contact forms and overcome the limitations of Shopify's default form handling. This blog post demonstrated how to replace the default Shopify contact form helper with Basin, set up webhooks, and use a Cloudflare Worker to process form data and perform custom actions. With the advanced features and configuration options offered by Basin, you can unlock the full potential of your Shopify store and provide your customers with a more tailored experience. In this specific use case, we have shown how a Shopify purchase order form can be converted to a PDF and emailed to the vendor automatically, streamlining the process for both the vendor and your internal team.

Get Started with a Free Basin account today!

Discover the benefits of Basin and how it can help streamline your forms. Sign up now and explore our features.

Sign Up for Free