Mechanic
đŸ“Ŗ Shopify REST Deprecation
  • â›Šī¸Introduction
  • 🤓Hire a Mechanic developer
  • đŸ’¯status.mechanic.dev
  • đŸ“ŖShopify is deprecating the REST API
  • 🙋"I need something custom!"
  • 🧑‍đŸ’ģ"I need help with my custom task!"
  • 🤖"I need help with my AI-written task!"
  • Resources
    • âŦ‡ī¸Install Mechanic
    • 🧑‍đŸ’ģTask library
      • Contributing
      • Requesting
    • 🚀Slack community
    • 🤝Partner directory
    • 🧠Tutorials
      • Video walkthroughs
        • Auto-tag orders by originating staff member
        • Maintain a tag for orders processed today
        • Auto-tag orders with their tracking numbers
        • Sync inventory for shared SKUs
        • Auto-tag products when their SKU(s) change
        • Auto-publish new products
        • Email a report of customers who haven't ordered in X days
        • Upgrading a Mechanic task: Adding a time delay
        • Email the customer when tracking numbers are added to their order
        • Adding an optional time delay to your Mechanic task
        • Delete all orders
        • Send an email when a specific product is shipped
        • Send recurring reminders about unpaid orders
        • Send an email when a product's price goes below its cost
        • Auto-tag customers by sales channel
        • Creating products in bulk
      • Creating a Mechanic webhook
      • Practicing writing tasks
      • Triggering tasks from a contact form
      • Creating scheduled CSV feeds
      • Fetching data from a shared Google sheet
    • 🏆Converting tasks from Shopify REST to GraphQL
      • Conversion: Single resource lookups
      • Conversion: Resource loops to paginated queries
      • Conversion: Connections from a resource
      • Conversion: Metafield lookups from a resource
      • Conversion: Resource lookups in task option fields
  • Core Concepts
    • Events
      • Topics
      • Parent and child events
    • Tasks
      • Subscriptions
      • Code
        • Environment variables
        • Action objects
        • Error objects
        • Log objects
      • Options
        • Custom validation
      • Previews
        • Defining preview events
        • Stub data
      • Shopify API version
      • Advanced settings
        • Documentation
        • JavaScript
        • Perform action runs in sequence
      • Import and export
      • User Form
    • Actions
      • Cache
      • Echo
      • Email
      • Event
      • Files
      • Flow
      • FTP
      • Google Drive
      • Google Sheets
      • HTTP
      • Integrations
        • Report Toaster
      • Shopify
      • File generators
        • Base64
        • PDF
        • Plaintext
        • URL
        • ZIP
    • Runs
      • Scheduling
      • Concurrency
      • Ordering
      • Retries
    • Interacting with Shopify
      • Responding to events
        • Reconciling missing events
      • Reading data
        • âš ī¸Liquid objects
        • 🏆GraphQL in Liquid
        • Bulk operations
        • The Shopify action
      • Writing data
      • Shopify admin action links
      • API rate limit
      • API versions
  • Platform
    • 🔆Policies
      • Data
      • Plans
      • Pricing
      • Privacy
    • Cache
      • Cache endpoints
    • Email
      • Receiving email
      • Custom email addresses
      • DMARC
      • Email templates
    • Error handling
    • Events
      • Event topics
      • Event filters
    • GraphQL
      • Basics
        • Shopify Admin API GraphiQL explorer
        • Queries
        • Mutations
        • Pagination
      • Bulk operations
    • Integrations
      • Appstle Subscriptions
      • Google Drive and Google Sheets
      • Judge.me
      • Locksmith
      • Report Toaster
      • Shopify Flow
      • Run links
    • Liquid
      • Basics
        • Syntax
        • Data types
        • Variables
        • Comments
        • Filters
        • Operators
        • Control flow
          • Condition
          • Iteration
        • Whitespace
      • Liquid console
      • Mechanic filters
        • Deprecated filters
      • Mechanic keyword literals
        • array
        • hash
        • newline
      • Mechanic objects
        • Action object
        • Cache object
        • Event object
        • Options object
        • Task object
        • âš ī¸Shopify REST Admin API
          • đŸšĢArticle object
          • đŸšĢBlog object
          • đŸšĢCollection object
          • đŸšĢCustomer object
          • đŸšĢDiscount code object
          • đŸšĢDispute object
          • đŸšĢDraft order object
          • đŸšĢFulfillment object
          • đŸšĢFulfillment order object
          • đŸšĢFulfillment event object
          • đŸšĢGift card object
          • đŸšĢInventory item object
          • đŸšĢInventory level object
          • đŸšĢLine item object
          • đŸšĢLocation object
          • đŸšĢMetafields
            • Metafield object
            • Metafield representation object
            • Metafield collection object
          • đŸšĢOrder object
          • đŸšĢOrder risk object
          • đŸšĢPrice rule object
          • đŸšĢProduct object
          • đŸšĢProduct image object
          • đŸšĢRefund object
          • đŸšĢShipping zone object
          • đŸšĢShop object
          • đŸšĢTheme object
          • đŸšĢTheme asset object
          • đŸšĢTransaction object
          • đŸšĢVariant object
      • Mechanic tags
        • liquid
        • action
        • assign
        • error
        • log
      • Mechanic code snippets
    • Shopify
      • Custom authentication
      • "Read all orders"
    • Webhooks
  • Techniques
    • Preventing action loops
    • Writing a high-quality task
    • Tagging Shopify resources
    • Debouncing events
    • Responding to action results
    • Working with external APIs
      • JSON Web Signatures
      • AWS request signatures
    • Finding a resource ID
    • Migrating templates from Shopify to Mechanic
    • Securing Mechanic webhooks
    • Monitoring Mechanic
  • FAQ
    • The app isn't loading. What do I do?
    • How do I stop a large batch of runs?
    • A Shopify event is missing. Where is it?
    • Does Mechanic have an affiliate program?
    • Can non-owners install Mechanic?
    • Can I replace Shopify's order emails with Mechanic?
    • Can I manually set Shopify permissions for Mechanic?
    • Does my theme need to be updated for Mechanic?
    • Do you have a plan for development stores?
    • Why don't I see any events in my task's activity?
    • Can I read data back from my webhook submission?
    • My task added a tag, but now the tag is missing – why?
    • How do I add an unsubscribe link to my emails?
    • How do I send images with my emails?
    • Can I re-send order confirmation emails with Mechanic?
    • Why am I seeing a different price than on the app store?
    • Do you have a Partner-friendly plan?
    • Why are my tasks delayed or not running?
    • My task is failing because of a permissions problem. Why?
    • How do I preview email attachments?
    • Can I query external APIs?
    • Why can't I access the Shopify API during preview mode?
    • How do marketing preferences work with Mechanic?
    • Can I send data to Google Sheets?
    • What's possible with timeline comments?
    • I'm getting a "query param length is too long" error when using GraphQL.
    • Can my Mechanic concurrency limit be raised?
    • What IP address does Mechanic use?
    • Can Mechanic read or manage customer subscriptions?
    • Why is everything harder now?
    • Can task content be translated into multiple languages?
    • Can I add a time delay to my task?
    • Can I add another store to my existing Mechanic subscription?
    • How can I reduce memory usage of my task?
    • How do I connect PayPal to Shopify with Mechanic?
    • How do I add a Shopify access scope to my task?
    • Can I have my Mechanic data retained for more (or less) than 15 days?
Powered by GitBook

Important Updates

  • đŸ“Ŗ Shopify REST Deprecation
On this page
  • The situation
  • The plan
  • The Mechanic task
  • Step 1: Create a webhook, and connect it to a new blank task
  • Step 2: Wire up the shop frontend to send form data to our webhook
  • Step 4: Receive our form submission on the Mechanic side, convert it to a CSV, and send it as an email attachment
  • Step 5: Testing
  • The end!
  • Import the final task

Was this helpful?

Edit on GitHub
Export as PDF
  1. Resources
  2. Tutorials

Triggering tasks from a contact form

PreviousPracticing writing tasksNextCreating scheduled CSV feeds

Last updated 3 months ago

Was this helpful?

This tutorial walks you through setting up a custom task in Mechanic, which is called on Contact Form submission on your Shopify frontend, the contents of the form are passed to the task, which emails the contents in CSV format.

Before beginning this tutorial, here's what you'll need:

  • A Shopify store, which has Mechanic installed (see )

  • A basic knowledge of Liquid ()

  • A basic knowledge of JavaScript ()

The situation

We have an online store called Mario's Mushrooms, hosted on Shopify. Business is booming, and our mushrooms are being shipped all over the world. Our CEO, Mario, asks us to connect our default Shopify contact form to our legacy customer relationship management (or CRM) system. We are eager to help! While the CRM doesn't have an HTTP API, it can receive CSV imports via email, which it will then import into its database. This gives us our path forward!

The plan

We are going to make a task in this cool Shopify app called Mechanic. ;) Here's what the task will do:

  1. The task will add some JavaScript to the online Shopify store, which will capture the contents of the contact form when submitted, and then send those contents over to Mechanic via

  2. Over on the Mechanic side, the task will receive the form contents, and format them as a CSV file

  3. The task will then send an to our CRM system, containing the CSV file as an attachment

The Mechanic task

Time to build the task! Out of Mechanic's entire toolkit, here's what we'll use:

Step 1: Create a webhook, and connect it to a new blank task

Step 2: Wire up the shop frontend to send form data to our webhook

We have options here! The only hard requirement is that we use a POST request to send form data to our webhook. This can be done using pure JavaScript, or using a library like jQuery, or even by using plain HTML to set the form tag's action attribute to our webhook URL.

First things first: we're going to make sure of the element ID, for our contact form. This will be important for writing JavaScript that addresses this form. After investigating, we discover that the form ID is "ContactForm". Easy enough!

Next, we're going to write some JavaScript that listens for thesubmit event of this form – functionally, this means that we're going to wire up some code to run when the form is submitted. The goal: to jump in when the form is submitted, send the form data to our webhook (which will then trigger our Mechanic task), and then allow the form to submit as usual. This way, we add Mechanic functionality without disabling the form's existing behavior.

Let's get started on our JavaScript. In your Mechanic task editor, scroll down and find the "JavaScript for Online Storefront" area. This will add this feature to our task, and we'll be given a place to add in our JavaScript, which will be automatically loaded into our shop frontend.

Copy in the JavaScript below, reading the comments for details on what's going on. Remember the "ContactForm" ID? Here's where we get to use it!

// This code will be loaded on all pages of our store. So, we'll need
// to begin by seeing if the current page has a contact form on it,
// to make sure we're not causing errors by trying to modify a form
// that doesn't exist.

// The `contactForm` variable will either be our form (if it's present
// on this page), or will be null (if it isn't).
const contactForm = document.querySelector('#ContactForm');

// Before Mechanic delivers this JavaScript to the storefront, it first
// evaluates it for Liquid. This means that we get to use the `options`
// object. By using {{ options.mechanic_webhook_url__required }}, we can
// make the webhook URL configurable.
const mechanicWebhookUrl = {{ options.mechanic_webhook_url__required | json }};

// We only want to run all of this if there's a contact form on the page.
if (contactForm) {

  // Setting up a flag for later - keep reading!
  let submittedToMechanic = false;

  contactForm.addEventListener(
    'submit',
    (event) => {
      // We're going to prevent the form submit from doing its normal
      // normal. We'll re-submit the form in a second, after we've
      // submitted data to Mechanic.
      event.preventDefault();

      // We'll use fetch to make our POST request:
      // https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API
      fetch(
        mechanicWebhookUrl,
        {
          method: 'POST', 
          body: new FormData(contactForm),
        }
      ).then((response) => {
        console.log('Sending data to Mechanic: Success!', response);
      }).catch((error) => {
        console.error('Sending data to Mechanic: Error!', error);
      }).finally(() => {
        // Now that we're done with sending our data to Mechanic,
        // we're going to manually submit the contact form. This won't
        // trigger the "submit" event again; it'll just run the form's
        // usual submit behavior.
        contactForm.submit();
      });
    },
  );
}

When pasting in this code, a new task option will appear, allowing the user (that's us, for now) to configure the webhook URL. Here's where we use the Mechanic-generated webhook URL from earlier.

With all that in place, save the task. We're leaving the task code empty for right now, and that's okay!

Step 4: Receive our form submission on the Mechanic side, convert it to a CSV, and send it as an email attachment

To make sure what data we're working with, let's submit the contact form, and then examine the resulting event data in Mechanic. (It's okay that we hit the captcha prompt; the important part is making sure that we're sending data to Mechanic.)

Heading to the "Events" page of the Mechanic app, we can see our data coming in.

Clicking through to that new event, we can see the event data on the right, reflecting what was in the form at the time of submission. (Depending on the nature of your specific contact form HTML, you might see something slightly different.)

This is perfect! The data we are interested in is inside of an event data property called "contact". This means that, in Liquid, we can access the contact data using {{ event.data.contact }}.

In the code sample below, we reference individual input values according to the keys we see above, in the contact object. We see that the phone number is stored in the "phone" key, so we use event.data.contact.phone to reference it.

When you're assembling your version of this task, make sure to update the task code to reflect the data keys you see in the incoming event.

{% assign rows = array %}

{% assign header = array %}
{% assign header[0] = "Name" %}
{% assign header[1] = "Email" %}
{% assign header[2] = "Phone Number" %}
{% assign header[3] = "Message" %}
{% assign rows[rows.size] = header %}

{% assign row = array %}
{% assign row[0] = event.data.contact.name %}
{% assign row[1] = event.data.contact.email %}
{% assign row[2] = event.data.contact.phone %}
{% assign row[3] = event.data.contact.body %}
{% assign rows[rows.size] = row %}

{% assign csv_data = rows | csv %}

{% action "email" %}
  {
    "to": {{ options.recipient_email_address__email_required | json }},
    "subject": {{ options.email_subject__required | json }},
    "body": {{ options.email_body__required_multiline | strip | newline_to_br | json }},
    "attachments": {
       {{ options.csv_attachment_filename__required | replace: ".csv", "" | append: ".csv" | json }}: {{ rows | csv | json }}
     }
   }
{% endaction %}

Here's how we'll configure the task, using the task option fields that automatically appear based on our task code:

Step 5: Testing

With everything assembled, we head back to the contact form, and make a submission. Back in the task editor, we see a new event appear in "Recent activity", with a green checkmark indicating that the task generated and performed an action.

The end!

We did it! We augmented our existing contact form with the ability to send submission data to our new Mechanic task, which relays the data to our CRM system using a CSV email attachment. 🎉

Import the final task

If you'd like to quickly pull in all of the task code and configuration we used here, use this task export:

{"name":"Receive contact form for CRM","options":{"recipient_email_address__email_required":"crm_imports@example.com","email_subject__required":"Contact form submission for CRM: {{ \"now\" | date: \"%Y-%m-%d %H:%M\" }}","email_body__required_multiline":"Hello,\n\nPlease find the attached CSV. Thanks!\n\n-Mechanic, for {{ shop.name }}","csv_attachment_filename__required":"contact-form-for-crm-{{ \"now\" | date: \"%s\" }}","mechanic_webhook_url__required":"https://webhooks.mechanic.dev/00000000-0000-0000-0000-000000000000"},"subscriptions":["user/webhook/form"],"subscriptions_template":null,"script":"{% assign rows = array %}\n\n{% assign header = array %}\n{% assign header[0] = \"Name\" %}\n{% assign header[1] = \"Email\" %}\n{% assign header[2] = \"Phone Number\" %}\n{% assign header[3] = \"Message\" %}\n{% assign rows[rows.size] = header %}\n\n{% assign row = array %}\n{% assign row[0] = event.data.contact.name %}\n{% assign row[1] = event.data.contact.email %}\n{% assign row[2] = event.data.contact.phone %}\n{% assign row[3] = event.data.contact.body %}\n{% assign rows[rows.size] = row %}\n\n{% assign csv_data = rows | csv %}\n\n{% action \"email\" %}\n  {\n    \"to\": {{ options.recipient_email_address__email_required | json }},\n    \"subject\": {{ options.email_subject__required | json }},\n    \"body\": {{ options.email_body__required_multiline | strip | newline_to_br | json }},\n    \"attachments\": {\n       {{ options.csv_attachment_filename__required | replace: \".csv\", \"\" | append: \".csv\" | json }}: {{ rows | csv | json }}\n     }\n   }\n{% endaction %}","docs":null,"halt_action_run_sequence_on_error":false,"liquid_profiling":false,"online_store_javascript":"// This code will be loaded on all pages of our store. So, we'll need\n// to begin by seeing if the current page has a contact form on it,\n// to make sure we're not causing errors by trying to modify a form\n// that doesn't exist.\n\n// The `contactForm` variable will either be our form (if it's present\n// on this page), or will be null (if it isn't).\nconst contactForm = document.querySelector('#ContactForm');\n\n// Before Mechanic delivers this JavaScript to the storefront, it first\n// evaluates it for Liquid. This means that we get to use the `options`\n// object. By using {{ options.mechanic_webhook_url__required }}, we can\n// make the webhook URL configurable.\nconst mechanicWebhookUrl = {{ options.mechanic_webhook_url__required | json }};\n\n// We only want to run all of this if there's a contact form on the page.\nif (contactForm) {\n\n  // Setting up a flag for later - keep reading!\n  let submittedToMechanic = false;\n  \n  contactForm.addEventListener(\n    'submit',\n    (event) => {\n      // We're going to prevent the form submit from doing its normal\n      // normal. We'll re-submit the form in a second, after we've\n      // submitted data to Mechanic.\n      event.preventDefault();\n\n      // We'll use fetch to make our POST request:\n      // https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API\n      fetch(\n        mechanicWebhookUrl,\n        {\n          method: 'POST', \n          body: new FormData(contactForm),\n        }\n      ).then((response) => {\n        console.log('Sending data to Mechanic: Success!', response);\n      }).catch((error) => {\n        console.error('Sending data to Mechanic: Error!', error);\n      }).finally(() => {\n        // Now that we're done with sending our data to Mechanic,\n        // we're going to manually submit the contact form. This won't\n        // trigger the \"submit\" event again; it'll just run the form's\n        // usual submit behavior.\n        contactForm.submit();\n      });\n    },\n  );\n}","order_status_javascript":null,"perform_action_runs_in_sequence":false,"shopify_api_version":"2021-01"}

Start with the tutorial for this part. Webhooks should be configured with respect to the source that supplies them with data, so for this tutorial, use the webhook name "Contact Form" and the event topic "user/webhook/form".

For this tutorial, we'll use JavaScript. And because we're using Mechanic, we don't even have to edit the theme directly to add in our code – instead, we can use the task editor's feature to have our code automatically loaded into the online storefront. (Under the hood, Mechanic leverages Shopify's API.)

For this tutorial, I created a development store and installed the . I use the contact form that comes with the theme as the form that submits to our webook. You can use any contact form on any theme, or create a form specifically for the purpose of submitting to our webhook.

Moving back to the task editor, the first step is to extract this data, and assemble it into something we can format using the filter. Because that filter is made to handle tables of data, this means that we'll create an array of "rows", and fill it with arrays of "columns", and then pass the result into the csv filter.

After that, we'll add an action, configuring it with our CSV data as an attachment. We'll also add a few more task options that will make it easy to reconfigure this task in the future, without having to touch the task code.

When writing a task, it's important to think about , and how they appear to the user (and to Mechanic itself). This task always sends a simple email for every event it receives, and doesn't require any special permissions, so we don't need to do any preview work here. If the task only sent an email under limited conditions, or if it needed to access the Shopify API, we'd need to do more work to make sure the task generates an intentional preview.

To learn more about this, see .

Thanks for reading! If you've got questions or suggestions, join the . :)

🧠
Mechanic's app store page
need a refresher?
need a refresher?
webhook
email
Online storefront JavaScript
Mechanic webhooks
The Email action
Creating a Mechanic webhook
JavaScript
ScriptTag
Debut theme
Email
previews
Previews
Mechanic Slack workspace
The csv Liquid filter
csv
Here, we use Chrome's developer tools to verify the form's ID attribute.
We've got the Chrome developer tools open so we can see our console.log messages.