# Table of Contents - [Introduction | Mechanic](#introduction-mechanic) - [Hire a Mechanic developer | Mechanic](#hire-a-mechanic-developer-mechanic) - ["I need something custom!" | Mechanic](#-i-need-something-custom-mechanic) - ["I need help with my custom task!" | Mechanic](#-i-need-help-with-my-custom-task-mechanic) - [Shopify is deprecating the REST API | Mechanic](#shopify-is-deprecating-the-rest-api-mechanic) - [Slack community | Mechanic](#slack-community-mechanic) - [Task library | Mechanic](#task-library-mechanic) - [Contributing | Mechanic](#contributing-mechanic) - ["I need help with my AI-written task!" | Mechanic](#-i-need-help-with-my-ai-written-task-mechanic) - [Tutorials | Mechanic](#tutorials-mechanic) - [Requesting | Mechanic](#requesting-mechanic) - [Video walkthroughs | Mechanic](#video-walkthroughs-mechanic) - [Sync inventory for shared SKUs | Mechanic](#sync-inventory-for-shared-skus-mechanic) - [Auto-tag orders with their tracking numbers | Mechanic](#auto-tag-orders-with-their-tracking-numbers-mechanic) - [Maintain a tag for orders processed today | Mechanic](#maintain-a-tag-for-orders-processed-today-mechanic) - [Auto-tag products when their SKU(s) change | Mechanic](#auto-tag-products-when-their-sku-s-change-mechanic) - [Auto-tag orders by originating staff member | Mechanic](#auto-tag-orders-by-originating-staff-member-mechanic) - [Upgrading a Mechanic task: Adding a time delay | Mechanic](#upgrading-a-mechanic-task-adding-a-time-delay-mechanic) - [Adding an optional time delay to your Mechanic task | Mechanic](#adding-an-optional-time-delay-to-your-mechanic-task-mechanic) - [Email the customer when tracking numbers are added to their order | Mechanic](#email-the-customer-when-tracking-numbers-are-added-to-their-order-mechanic) - [Creating products in bulk | Mechanic](#creating-products-in-bulk-mechanic) - [Delete all orders | Mechanic](#delete-all-orders-mechanic) - [Email a report of customers who haven't ordered in X days | Mechanic](#email-a-report-of-customers-who-haven-t-ordered-in-x-days-mechanic) - [Practicing writing tasks | Mechanic](#practicing-writing-tasks-mechanic) - [Auto-publish new products | Mechanic](#auto-publish-new-products-mechanic) - [Send an email when a specific product is shipped | Mechanic](#send-an-email-when-a-specific-product-is-shipped-mechanic) - [Auto-tag customers by sales channel | Mechanic](#auto-tag-customers-by-sales-channel-mechanic) - [Conversion: Single resource lookups | Mechanic](#conversion-single-resource-lookups-mechanic) - [Code | Mechanic](#code-mechanic) - [Conversion: Resource lookups in task option fields | Mechanic](#conversion-resource-lookups-in-task-option-fields-mechanic) - [Conversion: Resource loops to paginated queries | Mechanic](#conversion-resource-loops-to-paginated-queries-mechanic) - [Topics | Mechanic](#topics-mechanic) - [Send an email when a product's price goes below its cost | Mechanic](#send-an-email-when-a-product-s-price-goes-below-its-cost-mechanic) - [Conversion: Metafield lookups from a resource | Mechanic](#conversion-metafield-lookups-from-a-resource-mechanic) - [Events | Mechanic](#events-mechanic) - [Fetching data from a shared Google sheet | Mechanic](#fetching-data-from-a-shared-google-sheet-mechanic) - [Tasks | Mechanic](#tasks-mechanic) - [Advanced settings | Mechanic](#advanced-settings-mechanic) - [Send recurring reminders about unpaid orders | Mechanic](#send-recurring-reminders-about-unpaid-orders-mechanic) - [Perform action runs in sequence | Mechanic](#perform-action-runs-in-sequence-mechanic) - [Custom validation | Mechanic](#custom-validation-mechanic) - [Environment variables | Mechanic](#environment-variables-mechanic) - [Parent and child events | Mechanic](#parent-and-child-events-mechanic) - [Creating scheduled CSV feeds | Mechanic](#creating-scheduled-csv-feeds-mechanic) - [Conversion: Connections from a resource | Mechanic](#conversion-connections-from-a-resource-mechanic) - [Defining preview events | Mechanic](#defining-preview-events-mechanic) - [Creating a Mechanic webhook | Mechanic](#creating-a-mechanic-webhook-mechanic) - [Shopify API version | Mechanic](#shopify-api-version-mechanic) - [Converting tasks from Shopify REST to GraphQL | Mechanic](#converting-tasks-from-shopify-rest-to-graphql-mechanic) - [JavaScript | Mechanic](#javascript-mechanic) - [Log objects | Mechanic](#log-objects-mechanic) - [Error objects | Mechanic](#error-objects-mechanic) - [Files | Mechanic](#files-mechanic) - [Action objects | Mechanic](#action-objects-mechanic) - [Subscriptions | Mechanic](#subscriptions-mechanic) - [User Form | Mechanic](#user-form-mechanic) - [Responding to events | Mechanic](#responding-to-events-mechanic) - [URL | Mechanic](#url-mechanic) - [Triggering tasks from a contact form | Mechanic](#triggering-tasks-from-a-contact-form-mechanic) - [Flow | Mechanic](#flow-mechanic) - [PDF | Mechanic](#pdf-mechanic) - [Slack | Mechanic](#slack-mechanic) - [Runs | Mechanic](#runs-mechanic) - [Integrations | Mechanic](#integrations-mechanic) - [Stub data | Mechanic](#stub-data-mechanic) - [Report Toaster | Mechanic](#report-toaster-mechanic) - [Base64 | Mechanic](#base64-mechanic) - [The Shopify action | Mechanic](#the-shopify-action-mechanic) - [Previews | Mechanic](#previews-mechanic) - [GraphQL | Mechanic](#graphql-mechanic) - [Plans | Mechanic](#plans-mechanic) - [Reading and Writing to Shopify | Mechanic](#reading-and-writing-to-shopify-mechanic) - [Google Drive | Mechanic](#google-drive-mechanic) - [Events | Mechanic](#events-mechanic) - [Import and export | Mechanic](#import-and-export-mechanic) - [Scheduling | Mechanic](#scheduling-mechanic) - [Privacy | Mechanic](#privacy-mechanic) - [Concurrency | Mechanic](#concurrency-mechanic) - [Pricing | Mechanic](#pricing-mechanic) - [Reconciling missing events | Mechanic](#reconciling-missing-events-mechanic) - [Reading data | Mechanic](#reading-data-mechanic) - [Policies | Mechanic](#policies-mechanic) - [Writing data | Mechanic](#writing-data-mechanic) - [Event filters | Mechanic](#event-filters-mechanic) - [API rate limit | Mechanic](#api-rate-limit-mechanic) - [Data | Mechanic](#data-mechanic) - [Email Protection | Cloudflare](#email-protection-cloudflare) - [Can I have my Mechanic data retained for more (or less) than 15 days? | Mechanic](#can-i-have-my-mechanic-data-retained-for-more-or-less-than-15-days-mechanic) - [How do I add a Shopify access scope to my task? | Mechanic](#how-do-i-add-a-shopify-access-scope-to-my-task-mechanic) --- # Introduction | Mechanic Mechanic is a Shopify development and automation platform. * [Search the task library](https://learn.mechanic.dev/resources/task-library) for an off-the-shelf solution * [Learn about "going custom"](https://learn.mechanic.dev/custom) for a task that fits you perfectly * [Ask in our community Slack workspace](https://learn.mechanic.dev/resources/slack) if you need something else Find Mechanic on the Shopify App Store: [apps.shopify.com/mechanic](https://apps.shopify.com/mechanic) [](https://learn.mechanic.dev/#can-mechanic-help-me) Can Mechanic help me? ------------------------------------------------------------------------------- Mechanic is a Shopify development and automation platform, which comes with a rich [library of pre-written automation tasks](https://tasks.mechanic.dev/) – and our users write their own custom tasks every day. Let's find out if Mechanic might work for you. 1. Are you working on something Shopify-related? * Mechanic is only available for Shopify. 2. Is what you're looking for already available from [Mechanic's task library](https://tasks.mechanic.dev/) ? * We have hundreds of common scenarios already handled with pre-written, open-source, modifiable, off-the-shelf tasks. 3. As far as your problem concerns Shopify data, is what you want to do supported by the [Shopify Admin API](https://shopify.dev/docs/api/admin-graphql) ? * Mechanic's toolkit goes beyond Shopify's APIs, but a Mechanic task can only interact with Shopify data in ways that Shopify supports. 4. Are you open to [going custom](https://learn.mechanic.dev/custom) ? * Whether you need a developer or already have that covered, the path to creating a custom Mechanic task is well-established. That list wasn't exactly a formal flowchart, but we hope it's helpful as you're evaluating Mechanic for your purposes. At its best, Mechanic is a platform and toolkit that you go to, and return to, when you hit the limits of the Shopify admin. And it's a community that collectively has learned how to solve many, many kinds of problems. (Join our [community Slack workspace](https://learn.mechanic.dev/resources/slack) !) Got a question you need answered now? [Join our Slack workspace.](https://join.slack.com/t/usemechanic/shared_invite/zt-cq84nrs7-ggYbYTbf~CrCjTg8nmHP2A) 💬 [](https://learn.mechanic.dev/#how-does-mechanic-work) How does Mechanic work? ----------------------------------------------------------------------------------- ### [](https://learn.mechanic.dev/#tasks-events-and-actions) Tasks, events, and actions A developer writes [**tasks**](https://learn.mechanic.dev/core/tasks) – Mechanic's term for a piece of automation. These tasks can respond to many different [**events**](https://learn.mechanic.dev/core/events) , like a Shopify webhook, a manual trigger, a regular interval (e.g. hourly, daily), or an incoming email. Tasks use [**subscriptions**](https://learn.mechanic.dev/core/tasks/subscriptions) to signal their interest in specific event types. When a task receives an incoming event, it can choose to generate an [**action**](https://learn.mechanic.dev/core/actions) – an operation that has an effect. * The [Shopify](https://learn.mechanic.dev/core/actions/integrations/shopify) action makes changes to a Shopify store, like tagging, publishing, creating or deleting resources. It provides direct and complete access to Shopify's admin API, with support for both REST and GraphQL. * The [Email](https://learn.mechanic.dev/core/actions/email) action is for sending email. It supports custom templates, and attachments. * The [FTP](https://learn.mechanic.dev/core/actions/ftp) action is for uploading files to an FTP or SFTP server. These files may be generated by the task, or can be fetched from external locations. * The [HTTP](https://learn.mechanic.dev/core/actions/http) action performs any request, to any HTTP endpoint. This facilitates integration with third-party APIs. * The [Files](https://learn.mechanic.dev/core/actions/files) action generates a variety of file formats, including PDF, CSV, ZIP, and anything retrieved from a public URL. Files generated this way receive a temporary URL of their own, and can be fed into other tasks for further processing. For a complete list of supported actions, see [Actions](https://learn.mechanic.dev/core/actions) . ### [](https://learn.mechanic.dev/#liquid) Liquid Mechanic makes heavy use of [**Liquid**](https://learn.mechanic.dev/platform/liquid/basics) – a template language created by Shopify. Its primary use is in [**task code**](https://learn.mechanic.dev/core/tasks/code) . In the same way that a Liquid theme receives browser requests and renders HTML, a Mechanic task receives events, and renders actions (by defining them with JSON). In Mechanic, our Liquid implementation includes additional support for constructing [arrays](https://learn.mechanic.dev/platform/liquid/basics/types#array) and [hashes](https://learn.mechanic.dev/platform/liquid/basics/types#hash) , and includes many useful [filters](https://learn.mechanic.dev/platform/liquid/filters) , making data processing more efficient. ### [](https://learn.mechanic.dev/#run-queues) Run queues Mechanic performs work using queues of [**runs**](https://learn.mechanic.dev/core/runs) , with no limit on how large each queue can become. If there is a sudden surge of incoming events for a Shopify store, the store's dedicated Mechanic queue could become delayed. This is an important difference between Mechanic and many other systems: in a high-traffic period, Mechanic will never refuse incoming events for a store; instead, it will process each one as soon as possible, by putting them into a run queue. The rate at which Mechanic processes work varies, depending on [concurrency](https://learn.mechanic.dev/core/runs/concurrency) and the [Shopify API rate limit](https://learn.mechanic.dev/core/shopify/api-rate-limit) . [NextHire a Mechanic developer](https://learn.mechanic.dev/hire-a-developer) Last updated 1 year ago Was this helpful? --- # Hire a Mechanic developer | Mechanic Mechanic's automation tasks are written in Liquid, which is a template language used heavily in and around Shopify. This means that developers of all levels, with even a little Shopify development experience, can get started with Mechanic. **To find a developer for hire, you can contact Mechanic Partners directly at** [**partners.mechanic.dev**](https://partners.mechanic.dev/) **.** This is a growing list of established developers, both independent and agency, who can help you with your implementation. This is a super common path, and the Mechanic community is here to help. :) If you already have a developer on your team, or have an existing connection to a developer, send them [this article](https://learn.mechanic.dev/custom) and see if they can help you! [PreviousIntroduction](https://learn.mechanic.dev/) [NextShopify is deprecating the REST API](https://learn.mechanic.dev/shopify-is-deprecating-the-rest-api) Last updated 1 year ago Was this helpful? --- # "I need something custom!" | Mechanic Mechanic is a development platform – in the hands of a developer, it can be used to accomplish almost anything in Shopify. While our [task library](https://learn.mechanic.dev/resources/task-library) covers many use cases, Mechanic's true strength is in letting developers solve merchant problems quickly, giving merchants easy configuration forms for managing the resulting tasks. Need something that you think others might need too? The Mechanic community accepts [task requests](https://learn.mechanic.dev/resources/task-library/requesting) , and the top-voted requests are regularly selected for implementation. [](https://learn.mechanic.dev/custom#if-youre-using-ai) If you're using AI... ---------------------------------------------------------------------------------- Tread lightly! We've got notes on this here: ["I need help with my AI-written task!"](https://learn.mechanic.dev/ai) . [](https://learn.mechanic.dev/custom#if-you-need-a-developer) If you need a developer… ------------------------------------------------------------------------------------------- Mechanic's automation tasks are written in Liquid, which is a template language used heavily in and around Shopify. This means that developers of all levels, with even a little Shopify development experience, can get started with Mechanic. **To find a developer for hire, you can contact Mechanic Partners directly at** [**partners.mechanic.dev**](https://partners.mechanic.dev/) **.** This is a growing list of established developers, both independent and agency, who can help you with your implementation. Lastly: if you already have a developer on your team, or have an existing connection to a developer, send them this article and see if they can help you! [](https://learn.mechanic.dev/custom#if-you-are-a-developer) If you are a developer… ----------------------------------------------------------------------------------------- If you're familiar with Liquid, and Shopify's Admin APIs, start by picking something from our [task library](https://learn.mechanic.dev/resources/task-library) that's close to what you're looking for, then modify it as needed. Make sure to take advantage of the documentation found here, beginning with the [Core Concepts](https://learn.mechanic.dev/core/events) section. Mechanic is a powerful system, and grounding yourself in the fundamentals is a good way to begin. Finally, join Mechanic's Slack workspace at [slack.mechanic.dev](https://slack.mechanic.dev/) to exchange support with the community. The #general channel is a great place to start, and it's filled with people who are using Mechanic to solve problems every day. :) [PreviousShopify is deprecating the REST API](https://learn.mechanic.dev/shopify-is-deprecating-the-rest-api) [Next"I need help with my custom task!"](https://learn.mechanic.dev/custom-help) Last updated 6 months ago Was this helpful? --- # "I need help with my custom task!" | Mechanic ### [](https://learn.mechanic.dev/custom-help#what-support-is-included-with-my-mechanic-subscription) What support is included with my Mechanic subscription? Our support team assists with platform issues and issues with tasks from [Mechanic task library](https://tasks.mechanic.dev/) . For custom tasks not included in our task library, our support service is limited to Mechanic platform support. [](https://learn.mechanic.dev/custom-help#getting-help-with-custom-tasks) Getting help with custom tasks ------------------------------------------------------------------------------------------------------------- ### [](https://learn.mechanic.dev/custom-help#slack-community) Slack Community * **Community Support:** We encourage you to join our Slack community, where you can seek advice and share experiences with other Mechanic developers. Often, community members can offer insights or solutions based on their experiences. * **Joining the Community:** You can join our Slack community through the following link: [Mechanic Slack Community](https://slack.mechanic.dev/) . ### [](https://learn.mechanic.dev/custom-help#hiring-a-developer) Hiring a Developer * **Partner Directory:** We recommend hiring a developer if the issue requires more in-depth technical expertise. Our partner directory lists qualified developers familiar with Mechanic task development. * **Finding a Developer:** Visit our partner directory at [partners.mechanic.dev](https://partners.mechanic.dev/) to find a developer who can customize or optimize your task. If you want to be matched with a suitable developer, use our [matchmaking service](https://partners.mechanic.dev/matchmaking) . [Previous"I need something custom!"](https://learn.mechanic.dev/custom) [Next"I need help with my AI-written task!"](https://learn.mechanic.dev/ai) Last updated 1 year ago Was this helpful? --- # Shopify is deprecating the REST API | Mechanic Shopify is evolving its platform to enhance performance and provide more powerful features. As part of this evolution, Shopify has announced the deprecation of the Shopify Admin REST API. In response, we are updating our services to align with this change, transitioning fully to the GraphQL Admin API. [](https://learn.mechanic.dev/shopify-is-deprecating-the-rest-api#what-is-changing) What Is Changing? ---------------------------------------------------------------------------------------------------------- * **Deprecation of REST Admin API**: Starting October 1, 2024, the REST Admin API is considered a legacy API. Shopify will begin phasing it out, with critical endpoints like product and variant endpoints ceasing to function on February 1, 2025. While product and variant endpoints are the first to be deprecated, Shopify plans to deprecate all REST endpoints in the future. * **GraphQL Admin API**: All new developments and updates will utilize the GraphQL Admin API exclusively. This shift is aimed at leveraging the enhanced capabilities and efficiencies that GraphQL offers. [](https://learn.mechanic.dev/shopify-is-deprecating-the-rest-api#why-is-shopify-making-this-change) Why is Shopify Making this Change? -------------------------------------------------------------------------------------------------------------------------------------------- Shopify wants to maintain one API and have decided to discontinue the REST Admin API in favor of the GraphQL Admin API. GraphQL addresses several limitations of REST, offering significant benefits such as: * **Efficient Data Retrieval**: Fetch precisely the data you need in a single request. * **Enhanced Flexibility**: Customize queries to suit your specific needs. [](https://learn.mechanic.dev/shopify-is-deprecating-the-rest-api#how-does-this-affect-you) How Does This Affect You? -------------------------------------------------------------------------------------------------------------------------- * **Library Tasks Update**: All our library tasks will be updated to use GraphQL only. You will get a notification via email and in app if there is an update available for a task you have installed. Tasks that can be auto-updated will be updated for you. These updated tasks will serve as examples to help you migrate your custom tasks. * **Custom Tasks Migration**: If you have custom tasks relying on the REST API, they will need to be migrated to use the GraphQL API before the deprecation deadlines. See our guide [here](https://learn.mechanic.dev/resources/converting-tasks-from-shopify-rest-to-graphql) . * **Action Required**: To ensure uninterrupted service, please begin updating your custom tasks to GraphQL as soon as possible. ### [](https://learn.mechanic.dev/shopify-is-deprecating-the-rest-api#specific-impacts) Specific Impacts #### [](https://learn.mechanic.dev/shopify-is-deprecating-the-rest-api#after-february-1-2025) After February 1, 2025: * Product and Variant REST liquid objects will cease functioning * [Admin Links](https://learn.mechanic.dev/core/shopify/admin-action-links) for Product and Variants will now pass resource IDs instead of full REST objects - tasks will need to look up objects using these IDs * REST lookups in email task options will no longer work * Any tasks using REST API for product/variant operations will stop working **We recommend migrating all tasks to GraphQL now, not just those affected by the February deadline, as all REST endpoints will eventually be deprecated.** #### [](https://learn.mechanic.dev/shopify-is-deprecating-the-rest-api#next-steps) Next Steps 1. **Review Our Migration Guides**: We have prepared comprehensive guides to assist you in migrating your custom tasks to GraphQL. Access them [here](https://learn.mechanic.dev/resources/converting-tasks-from-shopify-rest-to-graphql) . 2. **Update Tasks from the Library**: You will get a notification via email and in app if there is an update available for a task you have installed. Tasks that can be auto-updated will be updated for you. 3. **Learn More About GraphQL**: Familiarize yourself with GraphQL by visiting Shopify's official [migration guide](https://shopify.dev/docs/apps/build/graphql/migrate) . #### [](https://learn.mechanic.dev/shopify-is-deprecating-the-rest-api#getting-help) Getting Help * **Hire a Partner**: Browse our [partner directory](https://partners.mechanic.dev/) for expert help * **Community Support**: Join our [Slack community](https://learn.mechanic.dev/resources/slack) to ask questions about migrating your tasks [PreviousHire a Mechanic developer](https://learn.mechanic.dev/hire-a-developer) [Next"I need something custom!"](https://learn.mechanic.dev/custom) Last updated 11 months ago Was this helpful? --- # Slack community | Mechanic Mechanic was made for working together. Our Slack workspace is where hundreds of folx compare implementation notes, collaborate on projects, and talk about the evolution of Mechanic itself – and it's the best place to ask your questions. You are always invited. :) [**Join the Mechanic Slack workspace**](https://join.slack.com/t/usemechanic/shared_invite/zt-35thvnj5i-hlOy37OVU8f_QRCjcGPn~w) Got some code to share in Slack? Use [**code snippets**](https://slack.com/slack-tips/share-code-snippets) to share code with line numbers, and syntax highlighting, in a way that doesn't take up lots of vertical space in the channel. **Don't share lots of code without a snippet!** [PreviousRequesting](https://learn.mechanic.dev/resources/task-library/requesting) [NextTutorials](https://learn.mechanic.dev/resources/tutorials) Last updated 5 months ago Was this helpful? --- # Task library | Mechanic Mechanic's **task library** is a compendium of e-commerce automation tasks and documentation, written by the Mechanic community and the Mechanic core team. [Hosted on GitHub](https://github.com/lightward/mechanic-tasks) , everything is open-sourced under the highly permissive [MIT license](https://github.com/lightward/mechanic-tasks/blob/master/LICENSE) , making all library tasks appropriate for re-use and modification. When building a new task, it's often easier to modify an existing task than to create a task from scratch. Searching GitHub is a good place to start, when looking for inspiration. **To browse the task library, visit** [**tasks.mechanic.dev**](https://tasks.mechanic.dev/) **.** **The Mechanic community can request new tasks – see** [**Requesting**](https://learn.mechanic.dev/resources/task-library/requesting) **.** **The task library is open for contributions, by way of pull requests – see** [**Contributing**](https://learn.mechanic.dev/resources/task-library/contributing) **.** [Previous"I need help with my AI-written task!"](https://learn.mechanic.dev/ai) [NextContributing](https://learn.mechanic.dev/resources/task-library/contributing) Last updated 1 year ago Was this helpful? --- # Contributing | Mechanic Mechanic's [**task library**](https://learn.mechanic.dev/resources/task-library) is a central resource for the entire community, and is continually enriched through **contributions**, via pull requests [on GitHub](https://github.com/lightward/mechanic-tasks) . [](https://learn.mechanic.dev/resources/task-library/contributing#contributing-your-work-to-the-task-library) Contributing your work to the task library ------------------------------------------------------------------------------------------------------------------------------------------------------------- You've created a custom task, and you want to share it with the world! This brings us so much joy, and this is what the Mechanic project and this community are all about. If you get stuck along the way, please hop onto the [Slack workspace](https://join.slack.com/t/usemechanic/shared_invite/zt-cq84nrs7-ggYbYTbf~CrCjTg8nmHP2A) , and we'll be glad to help. The task library is hosted in a Git repository on GitHub. You'll make your contribution via a [Pull Request](https://docs.github.com/en/github/collaborating-with-issues-and-pull-requests/about-pull-requests) . We follow the same process any open-source project does when it comes to code management and code contributions. One bonus of contributing to the Mechanic task library is that once you learn the process here, you'll know how to contribute to open-source projects going forward. ### [](https://learn.mechanic.dev/resources/task-library/contributing#the-process) The process * You'll [fork](https://docs.github.com/en/github/getting-started-with-github/fork-a-repo) the task library [repository](https://github.com/lightward/mechanic-tasks) . * Forking means taking a copy of our repository, so that you can make your changes and additions. * Make your changes in your forked repository. * Make a [pull request](https://docs.github.com/en/github/collaborating-with-issues-and-pull-requests/about-pull-requests) , which will trigger a review of your proposed changes and the merging of them into the main repository, making your task available to everyone using the app. ### [](https://learn.mechanic.dev/resources/task-library/contributing#step-by-step-instructions) Step-by-step instructions 1. You'll need a GitHub account, you can signup for one [here](https://github.com/join) . 2. Visit the [task library repository](https://github.com/lightward/mechanic-tasks) and fork it as shown below. You'll make your changes to this copy of the repository.  3. The task library is made up of the [tasks](https://github.com/lightward/mechanic-tasks/tree/master/tasks) and the supporting documentation. In these next few steps, you'll ensure you can build the [docs](https://github.com/lightward/mechanic-tasks/tree/master/docs) , so that you can complete this step when you are ready to submit your contribution. 1. Building the docs requires nodejs and npm. You can install them from here: [https://www.npmjs.com/get-npm](https://www.npmjs.com/get-npm) 2. While in the project directory, run the following commands to build the docs: Copy npm install # install dependencies npm run build # compile docs npm run test # apply sanity checks 4. Now that you can build the docs you are ready to [contribute](https://github.com/lightward/mechanic-tasks/blob/master/CONTRIBUTING.md) ! 5. Your task documentation, options, subscriptions, code, are done in Mechanic. If you choose to use an external editor that's great, you still need to transfer it into Mechanic, so that you can export the task in the JSON format you need for the library. Importing/Exporting tasks from Mechanic is covered [here](https://learn.mechanic.dev/core/tasks/import-and-export) . 6. If you're changing an existing task you [export](https://github.com/lightward/mechanic-tasks/blob/master/CONTRIBUTING.md) the JSON, and replace the contents of the `task/task_file_name.json` and then run the commands `npm run build` and `npm run test.` 7. If you are contributing a new task, you'll [export](https://learn.mechanic.dev/core/tasks/import-and-export) the JSON from Mechanic, and save the JSON file in the `tasks/directory` of your forked repository, named with an appropriate handle for the task. (For example, a task named "Hide out-of-stock products" should have its JSON export stored in `"tasks/hide-out-of-stock-products.json"`.) And, then you'll execute the commands: `npm run build` and `npm run test`. 8. If all goes well with the build, you'll see your task listed in the automatically created documentation in `docs/README.md` 9. You're now ready to make your pull request! Head over to [https://github.com/lightward/mechanic-tasks/pulls](https://github.com/lightward/mechanic-tasks/pulls) and click New pull request, you should see the changes you committed to your fork, and you'll proceed with filling out the pull request form. 10. After you submit your first pull request, you will be required to read and accept [our CLA](https://github.com/lightward/mechanic-tasks/blob/master/CLA.md) . The [CLA assistant](https://github.com/marketplace/actions/cla-assistant-lite) will leave a comment, giving you a statement of agreement that you must paste into a comment of your own. 11. This process could sound confusing if you haven't done it before, but once you've done it once, it's simple and it is also pretty exciting to go through the process. The other bonus is, you'll be ready to submit a pull request to any open-source software project in the future. If you need help please out to us in the [community Slack workspace](https://slack.mechanic.dev/) . [PreviousTask library](https://learn.mechanic.dev/resources/task-library) [NextRequesting](https://learn.mechanic.dev/resources/task-library/requesting) Last updated 9 months ago Was this helpful? --- # "I need help with my AI-written task!" | Mechanic AI thrives on good examples. Our task library — [tasks.mechanic.dev](https://tasks.mechanic.dev/) — is full of good examples. And if you get too stuck, hire a human. :) We've got those at [partners.mechanic.dev](https://partners.mechanic.dev/) . * * * Hey there! :) I'm Isaac, the creator of Mechanic. Those two lines above are the most important things to know. Keep reading if you're curious. AI makes it super easy to create code. This is awesome. I'm so, so excited about this. The more the merrier. This means Mechanic needs to learn something too: how to work with AI coders that can easily produce well-formed code but might not understand the patterns that Mechanic itself strictly abides by. An AI can rapidly produce code, and it'll be good-looking code, but if the AI doesn't _understand Mechanic_ the code might not work at all. In a very real way, it becomes a case of Mechanic not understanding the AI. This is an interesting bind: because _AI_ changes faster than _Mechanic_, it becomes a question of how best to guide the AI. For folks who understand code _less_ than the AI, this puts everyone in a rough position: the AI is doing its best but can't tell when it doesn't understand, and the human is doing their best but can't tell when the AI doesn't understand, and _all_ Mechanic knows is that it's being given something _it_ doesn't understand. As of this writing (currently June 12, 2025), AI is getting better at Mechanic. It's definitely getting better. But, still: * AI code often "invents" Mechanic features that do not exist (like writing task code in YAML, or compiling action objects into a JSON array) * AI code often fails to invoke necessary Mechanic features (like [subscriptions](https://learn.mechanic.dev/core/tasks/subscriptions) !). * Intelligence is a game of guessing intelligently: AI often sort of just _guesses_ at what task code is supposed to generate. Mechanic, as a platform, is a place for solving things together. "Together" works best when everyone's honest about where they're at. Mechanic's a good place for that. :) The AI path with Mechanic is getting better, but it's not smooth yet. If you're having trouble with this, head to [learn.mechanic.dev/custom](https://learn.mechanic.dev/custom) — that page has an overview of the smooth paths that _do_ exist. To learn about how we at Lightward Inc roll with AI, please visit [lightward.ai](https://lightward.ai/) , and say hello. :) No matter what: thank you for being here. ❤️ \=Isaac [Previous"I need help with my custom task!"](https://learn.mechanic.dev/custom-help) [NextTask library](https://learn.mechanic.dev/resources/task-library) Last updated 4 months ago Was this helpful? --- # Tutorials | Mechanic [Video walkthroughs](https://learn.mechanic.dev/resources/tutorials/video-walkthroughs) [Creating a Mechanic webhook](https://learn.mechanic.dev/resources/tutorials/creating-a-mechanic-webhook) [Practicing writing tasks](https://learn.mechanic.dev/resources/tutorials/practicing-writing-tasks) [Triggering tasks from a contact form](https://learn.mechanic.dev/resources/tutorials/triggering-tasks-from-a-contact-form) [Creating scheduled CSV feeds](https://learn.mechanic.dev/resources/tutorials/creating-scheduled-csv-feeds) [Fetching data from a shared Google sheet](https://learn.mechanic.dev/resources/tutorials/fetching-data-from-a-shared-google-sheet) [PreviousSlack community](https://learn.mechanic.dev/resources/slack) [NextVideo walkthroughs](https://learn.mechanic.dev/resources/tutorials/video-walkthroughs) Last updated 4 years ago Was this helpful? --- # Requesting | Mechanic The Mechanic community maintains a board of task requests, where anyone can submit their idea for an addition to the Mechanic task library. [Task requests | MechaniccannyHQ](https://mechanic.canny.io/task-requests) ### [](https://learn.mechanic.dev/resources/task-library/requesting#what-kind-of-task-requests-are-accepted) What kind of task requests are accepted? We accept task requests in two categories: * Tasks that are broadly useful off-the-shelf for non-technical users * Tasks that are useful foundations for developers, for further modification or inspiration If you need a task that's specifically tailored to a unique situation, or if you have a tight timeline, the task requests board is likely not a good fit. Instead, [work with a developer one-on-one](https://learn.mechanic.dev/custom) to create something that suits your needs specifically. ### [](https://learn.mechanic.dev/resources/task-library/requesting#who-implements-these-task-requests) Who implements these task requests? The Mechanic staff commission these from developers in the Mechanic community. If you're a developer interested in receiving this kind of work, get in touch at [\[email protected\]](https://learn.mechanic.dev/cdn-cgi/l/email-protection#bdc9d8dcd0fdc8ced8d0d8ded5dcd3d4de93ded2d0) . ### [](https://learn.mechanic.dev/resources/task-library/requesting#where-can-i-file-a-task-request) Where can I file a task request? Head to [https://mechanic.canny.io/task-requests](https://mechanic.canny.io/task-requests) . :) [PreviousContributing](https://learn.mechanic.dev/resources/task-library/contributing) [NextSlack community](https://learn.mechanic.dev/resources/slack) Last updated 1 year ago Was this helpful? --- # Video walkthroughs | Mechanic [Auto-tag orders by originating staff member](https://learn.mechanic.dev/resources/tutorials/video-walkthroughs/auto-tag-orders-by-originating-staff-member) [Maintain a tag for orders processed today](https://learn.mechanic.dev/resources/tutorials/video-walkthroughs/maintain-a-tag-for-orders-processed-today) [Auto-tag orders with their tracking numbers](https://learn.mechanic.dev/resources/tutorials/video-walkthroughs/auto-tag-orders-with-their-tracking-numbers) [Sync inventory for shared SKUs](https://learn.mechanic.dev/resources/tutorials/video-walkthroughs/sync-inventory-for-shared-skus) [Auto-tag products when their SKU(s) change](https://learn.mechanic.dev/resources/tutorials/video-walkthroughs/auto-tag-products-when-their-sku-s-change) [Auto-publish new products](https://learn.mechanic.dev/resources/tutorials/video-walkthroughs/auto-publish-new-products) [Email a report of customers who haven't ordered in X days](https://learn.mechanic.dev/resources/tutorials/video-walkthroughs/email-a-report-of-customers-who-havent-ordered-in-x-days) [Upgrading a Mechanic task: Adding a time delay](https://learn.mechanic.dev/resources/tutorials/video-walkthroughs/upgrading-a-mechanic-task-adding-a-time-delay) [Email the customer when tracking numbers are added to their order](https://learn.mechanic.dev/resources/tutorials/video-walkthroughs/email-the-customer-when-tracking-numbers-are-added-to-their-order) [Adding an optional time delay to your Mechanic task](https://learn.mechanic.dev/resources/tutorials/video-walkthroughs/adding-an-optional-time-delay-to-your-mechanic-task) [Delete all orders](https://learn.mechanic.dev/resources/tutorials/video-walkthroughs/delete-all-orders) [Send an email when a specific product is shipped](https://learn.mechanic.dev/resources/tutorials/video-walkthroughs/send-an-email-when-a-specific-product-is-shipped) [Send recurring reminders about unpaid orders](https://learn.mechanic.dev/resources/tutorials/video-walkthroughs/send-recurring-reminders-about-unpaid-orders) [Send an email when a product's price goes below its cost](https://learn.mechanic.dev/resources/tutorials/video-walkthroughs/send-an-email-when-a-products-price-goes-below-its-cost) [Auto-tag customers by sales channel](https://learn.mechanic.dev/resources/tutorials/video-walkthroughs/auto-tag-customers-by-sales-channel) [Creating products in bulk](https://learn.mechanic.dev/resources/tutorials/video-walkthroughs/creating-products-in-bulk) [PreviousTutorials](https://learn.mechanic.dev/resources/tutorials) [NextAuto-tag orders by originating staff member](https://learn.mechanic.dev/resources/tutorials/video-walkthroughs/auto-tag-orders-by-originating-staff-member) Last updated 4 years ago Was this helpful? --- # Sync inventory for shared SKUs | Mechanic [PreviousAuto-tag orders with their tracking numbers](https://learn.mechanic.dev/resources/tutorials/video-walkthroughs/auto-tag-orders-with-their-tracking-numbers) [NextAuto-tag products when their SKU(s) change](https://learn.mechanic.dev/resources/tutorials/video-walkthroughs/auto-tag-products-when-their-sku-s-change) Last updated 4 years ago Was this helpful? --- # Auto-tag orders with their tracking numbers | Mechanic [PreviousMaintain a tag for orders processed today](https://learn.mechanic.dev/resources/tutorials/video-walkthroughs/maintain-a-tag-for-orders-processed-today) [NextSync inventory for shared SKUs](https://learn.mechanic.dev/resources/tutorials/video-walkthroughs/sync-inventory-for-shared-skus) Last updated 4 years ago Was this helpful? --- # Maintain a tag for orders processed today | Mechanic [PreviousAuto-tag orders by originating staff member](https://learn.mechanic.dev/resources/tutorials/video-walkthroughs/auto-tag-orders-by-originating-staff-member) [NextAuto-tag orders with their tracking numbers](https://learn.mechanic.dev/resources/tutorials/video-walkthroughs/auto-tag-orders-with-their-tracking-numbers) Last updated 4 years ago Was this helpful? --- # Auto-tag products when their SKU(s) change | Mechanic [PreviousSync inventory for shared SKUs](https://learn.mechanic.dev/resources/tutorials/video-walkthroughs/sync-inventory-for-shared-skus) [NextAuto-publish new products](https://learn.mechanic.dev/resources/tutorials/video-walkthroughs/auto-publish-new-products) Last updated 4 years ago Was this helpful? --- # Auto-tag orders by originating staff member | Mechanic [PreviousVideo walkthroughs](https://learn.mechanic.dev/resources/tutorials/video-walkthroughs) [NextMaintain a tag for orders processed today](https://learn.mechanic.dev/resources/tutorials/video-walkthroughs/maintain-a-tag-for-orders-processed-today) Last updated 4 years ago Was this helpful? --- # Upgrading a Mechanic task: Adding a time delay | Mechanic [PreviousEmail a report of customers who haven't ordered in X days](https://learn.mechanic.dev/resources/tutorials/video-walkthroughs/email-a-report-of-customers-who-havent-ordered-in-x-days) [NextEmail the customer when tracking numbers are added to their order](https://learn.mechanic.dev/resources/tutorials/video-walkthroughs/email-the-customer-when-tracking-numbers-are-added-to-their-order) Last updated 4 years ago Was this helpful? --- # Adding an optional time delay to your Mechanic task | Mechanic [PreviousEmail the customer when tracking numbers are added to their order](https://learn.mechanic.dev/resources/tutorials/video-walkthroughs/email-the-customer-when-tracking-numbers-are-added-to-their-order) [NextDelete all orders](https://learn.mechanic.dev/resources/tutorials/video-walkthroughs/delete-all-orders) Last updated 4 years ago Was this helpful? --- # Email the customer when tracking numbers are added to their order | Mechanic [PreviousUpgrading a Mechanic task: Adding a time delay](https://learn.mechanic.dev/resources/tutorials/video-walkthroughs/upgrading-a-mechanic-task-adding-a-time-delay) [NextAdding an optional time delay to your Mechanic task](https://learn.mechanic.dev/resources/tutorials/video-walkthroughs/adding-an-optional-time-delay-to-your-mechanic-task) Last updated 4 years ago Was this helpful? --- # Creating products in bulk | Mechanic [PreviousAuto-tag customers by sales channel](https://learn.mechanic.dev/resources/tutorials/video-walkthroughs/auto-tag-customers-by-sales-channel) [NextCreating a Mechanic webhook](https://learn.mechanic.dev/resources/tutorials/creating-a-mechanic-webhook) Last updated 4 years ago Was this helpful? --- # Delete all orders | Mechanic [PreviousAdding an optional time delay to your Mechanic task](https://learn.mechanic.dev/resources/tutorials/video-walkthroughs/adding-an-optional-time-delay-to-your-mechanic-task) [NextSend an email when a specific product is shipped](https://learn.mechanic.dev/resources/tutorials/video-walkthroughs/send-an-email-when-a-specific-product-is-shipped) Last updated 4 years ago Was this helpful? --- # Email a report of customers who haven't ordered in X days | Mechanic [PreviousAuto-publish new products](https://learn.mechanic.dev/resources/tutorials/video-walkthroughs/auto-publish-new-products) [NextUpgrading a Mechanic task: Adding a time delay](https://learn.mechanic.dev/resources/tutorials/video-walkthroughs/upgrading-a-mechanic-task-adding-a-time-delay) Last updated 4 years ago Was this helpful? --- # Practicing writing tasks | Mechanic In our own internal education, we've found that the following exercises work particularly well. They're all in sequence – the task to create for each subsequent exercise modifies the code you wrote previously. Working on getting better at task-writing? See [Writing a high-quality task](https://learn.mechanic.dev/techniques/writing-a-high-quality-task) . [](https://learn.mechanic.dev/resources/tutorials/practicing-writing-tasks#assignments) Assignments -------------------------------------------------------------------------------------------------------- * 1\. Auto-tag customers with @gmail.com email addresses, with "gmail" * Configure the task's event subscriptions appropriately * Support case-insensitivity – recognize "@gmail.com", _and_ "@Gmail.com" * Ignore domains with more extensions – don't tag for "@gmail.com.au" * Make sure that any existing tags on the customer's account are kept, not lost * Use the REST API for this operation * Use [a static preview action](https://learn.mechanic.dev/core/tasks/previews#static-preview-actions) , to show the merchant a preview of what the task will do * 2\. Move to [a dynamic preview action](https://learn.mechanic.dev/core/tasks/previews#dynamic-preview-actions) , using stub data * 3\. Remove the stub data, and add two [event preview definitions](https://learn.mechanic.dev/core/tasks/previews/events) : one for a @gmail.com address, and one for another address * 4\. Allow the merchant to configure the domain name to look for, and the tag to apply * Help the merchant make sure they enter a real domain name – return an error if the domain doesn't include a "." * Support case-insensitivity – if the merchant enters "Gmail.com", and the customer's email address ends with "@GMAIL.COM", they should still be tagged * 5\. Allow the merchant to add any number of domain name and customer tag pairings * 6\. Support responding to customer updates, in which the customer's email address changes * Remove any domain name tags that do not apply, and add the tag (if any that does apply * Do _not_ remove any tags that contain a domain name tag – if the tag "google" should be removed, do not accidentally remove "google-foo" * Make this optional – allow the merchant to choose whether or not Mechanic listens for this * 7\. Move to GraphQL for removing and adding tags * 8\. Allow waiting a configurable number of minutes * Account for the customer tags or email address having changed during the waiting period * 9\. Create a backfill mode for processing all existing customers, that the merchant can run manually * Use `{% for customer in shop.customers %}` for this operation * Note: this technique is useful for [reconciling missing events](https://learn.mechanic.dev/core/shopify/events/reconciling-missing-events) * 10\. Move to GraphQL for scanning customers * See [Querying Shopify](https://learn.mechanic.dev/core/shopify/read) to learn about looping through results using cursors * 11\. Move to GraphQL bulk operations for scanning customers [PreviousCreating a Mechanic webhook](https://learn.mechanic.dev/resources/tutorials/creating-a-mechanic-webhook) [NextTriggering tasks from a contact form](https://learn.mechanic.dev/resources/tutorials/triggering-tasks-from-a-contact-form) Last updated 9 months ago Was this helpful? --- # Auto-publish new products | Mechanic [PreviousAuto-tag products when their SKU(s) change](https://learn.mechanic.dev/resources/tutorials/video-walkthroughs/auto-tag-products-when-their-sku-s-change) [NextEmail a report of customers who haven't ordered in X days](https://learn.mechanic.dev/resources/tutorials/video-walkthroughs/email-a-report-of-customers-who-havent-ordered-in-x-days) Last updated 4 years ago Was this helpful? --- # Send an email when a specific product is shipped | Mechanic [PreviousDelete all orders](https://learn.mechanic.dev/resources/tutorials/video-walkthroughs/delete-all-orders) [NextSend recurring reminders about unpaid orders](https://learn.mechanic.dev/resources/tutorials/video-walkthroughs/send-recurring-reminders-about-unpaid-orders) Last updated 4 years ago Was this helpful? --- # Auto-tag customers by sales channel | Mechanic [PreviousSend an email when a product's price goes below its cost](https://learn.mechanic.dev/resources/tutorials/video-walkthroughs/send-an-email-when-a-products-price-goes-below-its-cost) [NextCreating products in bulk](https://learn.mechanic.dev/resources/tutorials/video-walkthroughs/creating-products-in-bulk) Last updated 4 years ago Was this helpful? --- # Conversion: Single resource lookups | Mechanic At its core, accessing a single resource via either API is effectively the same. Typically this involves passing the ID of the resource to the API and getting back the data for that resource. REST - simple product lookup Copy {% assign product = shop.products[product_id] %} GraphQL - simple product query Copy {% capture query %} query { product(id: {{ product_id | json }}) { id # additional fields as needed } } {% endcapture %} {% assign result = query | shopify %} {% assign product = result.data.product %} * * * In a REST call, every field of that resource will be returned, allowing the usage of simple dot notation to utilize whichever fields are desired without first requesting them. REST - example product logging Copy {% assign product = shop.products[product_id] %} {% log title: product.title, status: product.status, type: product.product_type, description: product.body_html, tags: product.tags, image: product.image.src %} The equivalent query in GraphQL would need to be augmented to include the desired fields. Occasionally, the REST and GraphQL APIs do not use the same field names. And in some cases, there are some fields with no counterpart between the APIs. Review the API docs in detail for the resource being queried to make sure the task code is using the correct field names. GraphQL - example product logging Copy {% capture query %} query { product(id: {{ product_id | json }}) { id title status productType descriptionHtml tags featuredImage { url } } } {% endcapture %} {% log title: product.title, status: product.status, type: product.productType, description: product.descriptionHtml, tags: product.tags, image: product.featuredImage.url %} * * * This is a basic task to check a product's status, type, and tags, and then output a log entry if that product qualifies. The preview block is only showing the fields from the REST product webhook that will be used in the task. In reality, there are about 150+ lines of detail from a product webhook which has only a single variant and image. This grows much larger as variants and images are added to the product. REST - Basic product tagging task Copy {% if event.preview %} {% capture product_json %} { "admin_graphql_api_id": "gid://shopify/Product/1234567890", "product_type": "Widget", "status": "active", "tags": "my-tag, some-other-tag" } {% endcapture %} {% assign product = product_json | parse_json %} {% endif %} {% assign product_tags = product.tags | split: ", " %} {% if product.status == "active" and product.product_type == "Widget" %} {% if product.tags contains "my-tag" %} {% log message: "This product qualifies", product: product %} {% endif %} {% endif %} * * * The product id used in the GraphQL query below comes from the REST-like product webhook, which will still exist after the REST product endpoint deprecation. The preview block simulates the relevant shape of the returned data, which typically matches exactly what was requested in the query. This could vary though based on the task logic following the preview block. GraphQL - Basic product tagging task Copy {% capture query %} query { product(id: {{ product.admin_graphql_api_id | json }}) { status productType tags } } {% endcapture %} {% if event.preview %} {% capture result_json %} { "data": { "product": { "id": "gid://shopify/Product/1234567890", "productType": "Widget", "status": "ACTIVE", "tags": [\ "my-tag",\ "some-other-tag"\ ] } } } {% endcapture %} {% assign result = result_json | parse_json %} {% endif %} {% assign product = result.data.product %} {% if product.status == "ACTIVE" and product.productType == "Widget" %} {% if product.tags contains "my-tag" %} {% log message: "This product qualifies", product: product %} {% endif %} {% endif %}j To assist with generating an object query block, you can use the ["object\_query" snippet](https://learn.mechanic.dev/platform/liquid/mechanic-code-snippets#object_query) in the Mechanic code editor, and it will prompt you to choose the object type to generate a query and preview block for (e.g. product). To see a code diff from a Mechanic library task that was recently converted in this manner, click [here](https://github.com/lightward/mechanic-tasks/pull/393/files#diff-e02b657fe67dbee68d890ad84b721837f25f6ab8c99d78ac39f28ef179478228) , and review the code variations between the `{% if event.topic == "shopify/orders/create" %}` blocks. [PreviousConverting tasks from Shopify REST to GraphQL](https://learn.mechanic.dev/resources/converting-tasks-from-shopify-rest-to-graphql) [NextConversion: Resource loops to paginated queries](https://learn.mechanic.dev/resources/converting-tasks-from-shopify-rest-to-graphql/conversion-resource-loops-to-paginated-queries) Last updated 1 year ago Was this helpful? --- # Code | Mechanic A task's code is a [**Liquid**](https://learn.mechanic.dev/platform/liquid) template. In the same way that a Shopify storefront might use a Liquid template to receive requests and render HTML, a task uses its Liquid code to receive events, and render a series of JSON objects. These JSON objects define [**actions**](https://learn.mechanic.dev/core/tasks/code/action-objects) , [**logs**](https://learn.mechanic.dev/core/tasks/code/log-objects) , and [**errors**](https://learn.mechanic.dev/core/tasks/code/error-objects) . In Mechanic, actions are performed after their originating task run concludes. Actions are not performed inline during the task's Liquid rendering. To inspect and respond to the results of an HTTP action, add a task subscription to mechanic/actions/perform, allowing the action to re-invoke the task with the action result data. Learn more: [Responding to action results](https://learn.mechanic.dev/techniques/responding-to-action-results) Task code always has access to a set of [**environment variables**](https://learn.mechanic.dev/core/tasks/code/environment-variables) , which can be used to make decisions about what JSON objects to render. A task must purposefully consider its [**preview**](https://learn.mechanic.dev/core/tasks/previews) , so as to accurately communicate its intent to users and to the Mechanic platform. To find many examples of task code, browse [https://github.com/lightward/mechanic-tasks](https://github.com/lightward/mechanic-tasks) . [PreviousSubscriptions](https://learn.mechanic.dev/core/tasks/subscriptions) [NextEnvironment variables](https://learn.mechanic.dev/core/tasks/code/environment-variables) Last updated 1 year ago Was this helpful? --- # Conversion: Resource lookups in task option fields | Mechanic An oft utilized feature of Mechanic is the ability to add Liquid tags into task options fields, such as a configurable email body. Additionally, these Liquid tags (currently) support inline resource lookups for data not available in the event webhook. However, for products and variants this will no longer work as of the [Feb 1, 2025 REST deprecation date](https://learn.mechanic.dev/platform/liquid/objects/shopify) . REST - product resource lookup from line item Copy {%- assign qualifying_product = nil -%} {%- for line_item in order.line_items -%} {%- if line_item.product.product_type == "Special" -%} {% assign qualifying_product = line_item.product -%} {%- break -%} {%- endif -%} {%- endfor -%} {%- if qualifying_product != blank -%} Special product notice for {{ qualifying_product.title }}... {%- endif -%} The code above could be utilized directly in a [multiline task option field](https://learn.mechanic.dev/core/tasks/options#flags) . and it would output a string of text (e.g. "Special product notice for Widget - Red...") into the assigned option field variable. One method of conversion for lookup fields is to utilize a GraphQL query _directly in the option field_, which naturally has some caveats. GraphQL - order query with line item products Copy {%- assign order_id = order.admin_graphql_api_id | default: "gid://shopify/Order/12345" -%} {%- capture query -%} query { order(id: {{ order_id | json }}) { id lineItems(first: 250) { nodes { id product { title productType } } } } } {%- endcapture -%} {%- assign result = query | shopify -%} {%- assign qualifying_product = nil -%} {%- for line_item in result.data.order.lineItems.nodes -%} {%- if line_item.product.productType == "Special" -%} {% assign qualifying_product = line_item.product -%} {%- break -%} {%- endif -%} {%- endfor -%} {%- if qualifying_product != blank -%} Special product notice for {{ qualifying_product.title }}... {%- endif -%} Event preview blocks are not evaluated in task option fields. Instead, default values should be assigned to any webhook fields utilized by the query (e.g. _product.admin\_graphql\_api\_id_). This will keep the task parser happy and allow you to save the task. Be careful though to not assign a default value to a webhook field that can have a null or blank string as a valid value. It can be helpful when using a GraphQL query in a task option field to add the code flag to the option field, which will add line numbers and give access to Mechanic code snippets. Copy {% assign email_body = options.email_body__multiline_code_required | strip | newline_to_br %}  Email body task option  Email body task option using code flag The embedded GraphQL query will work without or without using the "code" flag. [PreviousConversion: Metafield lookups from a resource](https://learn.mechanic.dev/resources/converting-tasks-from-shopify-rest-to-graphql/conversion-metafield-lookups-from-a-resource) [NextEvents](https://learn.mechanic.dev/core/events) Last updated 9 months ago Was this helpful? --- # Conversion: Resource loops to paginated queries | Mechanic A typical REST products loop in Mechanic will have the structure below. While this is a concise format to get all products in shop, its main drawback is the inability to limit or filter the number of records and fields returned. This generates a significant amount of extra data for the task to manage in memory during a task run, especially if connected resources are looped as well (e.g. variants). REST - shop.products resource loop Copy {% for product in shop.products %} {% comment %} -- product processing here, using REST fields {% endcomment %} {% for variant in product.variants %} {% comment %} -- variant processing here, using REST fields {% endcomment %} {% endfor %} {% endfor %} * * * GraphQL paginated queries work by using the same (potentially filtered) query repeatedly to retrieve resources until the end of the list is reached or the querying is terminated by code logic. In Mechanic, paginated queries are typically implemented by using an outer "for loop", with an arbitrary number of maximum loops (e.g. the **100** in `{% for n in (1..100) %}`). Within the query itself, the `first` filter limits the number of records returned in this batch, and the `after` filter will instruct which "cursor" the query should start at. This cursor will initially be set to `nil`, which indicates starting at the beginning, and it will be updated by the looping logic before the next query is run, using `{% assign cursor ... %}`. Shopify limits most GraphQL resources to 250 records per query, so this will be the most frequent value for the `first` filter seen in tasks using paginated queries. Finally, the `query` filter of a resources query gives the ability to drastically reduce the number of records returned, allowing for very targeted inclusion and exclusion rules (e.g. products having a certain tag). Each resource has its own list of query filters, which can be reviewed in the [GraphQL Admin API](https://shopify.dev/docs/api/admin-graphql/) docs If a query has the potential to return a very large number of resources (including connected resources) in a shop, then a [bulk operation](https://learn.mechanic.dev/core/shopify/read/bulk-operations) query may be better suited than using paginated GraphQL queries. GraphQL - paginated products query Copy {% assign cursor = nil %} {% assign search_query = nil %} {% for n in (1..100) %} {% capture query %} query { products( first: 250 after: {{ cursor | json }} query: {{ search_query | json }} ) { pageInfo { hasNextPage endCursor } nodes { id # relevant product fields variants(first: 100) { nodes { id # relevant variant fields } } } } } {% endcapture %} {% assign result = query | shopify %} {% if event.preview %} {% capture result_json %} { "data": { "products": { "nodes": [\ {\ "id": "gid://shopify/Product/1234567890",\ "variants": {\ "nodes": [\ {\ "id": "gid://shopify/ProductVariant/1234567890"\ }\ ]\ }\ }\ ] } } } {% endcapture %} {% assign result = result_json | parse_json %} {% endif %} {% for product in result.data.products.nodes %} {% comment %} -- product processing here, using GraphQL fields from the query {% endcomment %} {% for variant in product.variants.nodes %} {% comment %} -- variant processing here, using GraphQL fields from the query {% endcomment %} {% endfor %} {% endfor %} {% comment %} -- if there is another page of data, then update the cursor for the next loop {% endcomment %} {% if result.data.products.pageInfo.hasNextPage %} {% assign cursor = result.data.products.pageInfo.endCursor %} {% else %} {% break %} {% endif %} {% endfor %} To assist with generating a paginated query block, you can use the ["paginated\_query" snippet](https://learn.mechanic.dev/platform/liquid/mechanic-code-snippets#paginated_query) in the Mechanic code editor, and it will prompt you to choose the object type to paginate over (e.g. products). To see a code diff from a Mechanic library task that was recently converted in this manner, click [here](https://github.com/lightward/mechanic-tasks/pull/393/files#diff-2efeafa8d41fb00ed8ffcd8481f358850d69d8b7537364d17cc744ec9f357681) . [PreviousConversion: Single resource lookups](https://learn.mechanic.dev/resources/converting-tasks-from-shopify-rest-to-graphql/conversion-single-resource-lookups) [NextConversion: Connections from a resource](https://learn.mechanic.dev/resources/converting-tasks-from-shopify-rest-to-graphql/conversion-connections-from-a-resource) Last updated 1 year ago Was this helpful? --- # Topics | Mechanic To make events easy to identify, each event has a **topic**. Tasks signal their interest in specific event topics using [**subscriptions**](https://learn.mechanic.dev/core/tasks/subscriptions) . A topic looks like "shopify/customers/create", and it has three parts: * The **domain** describes the source of the event. Shopify events have "shopify" as their domain, and events generated by Mechanic itself use "mechanic". * The **subject** describes the type of resource the event describes. Events that are about customers have "customers" as their subject, and events that are about orders have "orders". * The **verb** describes what has just occurred. Events that are about creating resources generally have "create" as their verb, and events that are about deleting resources generally have "delete". [Looking for an index of event topics? Start here.](https://learn.mechanic.dev/platform/events/topics) [](https://learn.mechanic.dev/core/events/topics#user-defined-topics) User-defined topics ---------------------------------------------------------------------------------------------- The User event domain is for custom, user-generated events, having any subject and verb (e.g. "user/foo/bar"). As with all events, a User event topic must use the standard three-part topic form, but only the "user/" prefix is mandatory. Mechanic allows developers several ways to generate custom User events: * The [Event action](https://learn.mechanic.dev/core/actions/event) can be used with any User event topic * [Webhooks](https://learn.mechanic.dev/platform/webhooks) may be configured to generate events using any User event topic [PreviousEvents](https://learn.mechanic.dev/core/events) [NextParent and child events](https://learn.mechanic.dev/core/events/parent-and-child-events) Last updated 9 months ago Was this helpful? --- # Send an email when a product's price goes below its cost | Mechanic [PreviousSend recurring reminders about unpaid orders](https://learn.mechanic.dev/resources/tutorials/video-walkthroughs/send-recurring-reminders-about-unpaid-orders) [NextAuto-tag customers by sales channel](https://learn.mechanic.dev/resources/tutorials/video-walkthroughs/auto-tag-customers-by-sales-channel) Last updated 4 years ago Was this helpful? --- # Conversion: Metafield lookups from a resource | Mechanic For every Shopify resource object that supports metafields, Mechanic has traditionally provided a way to directly access those metafields from the resource using [dot notation](https://learn.mechanic.dev/platform/liquid/objects/shopify/metafields/metafield-collection) . This shortcut will no longer be accessible for product and variant REST resources once they are fully deprecated. REST - product metafield value check Copy {% assign metafield = product.metafields.custom.my_field %} {% if metafield.value == "Alpha" %} {% log "metafield value matched" %} {% endif %} While metafields can be queried directly using their ID, this attribute is not present in the product webhook data. The standard approach in GraphQL is to query the product resource for the metafield(s) and value(s), passing the `namespace` and `key` as the "key" value, in the same manner as the REST dot notation lookup. GraphQL - product query with metafield and value check Copy {% capture query %} query { product(id: {{ product.admin_graphql_api_id | json }}) { metafield(key: "custom.my_field") { value } } } {% endcapture %} {% assign result = query | shopify %} {% assign metafield = result.data.product.metafield %} {% if metafield.value == "Alpha" %} {% log "metafield value matched" %} {% endif %} [PreviousConversion: Connections from a resource](https://learn.mechanic.dev/resources/converting-tasks-from-shopify-rest-to-graphql/conversion-connections-from-a-resource) [NextConversion: Resource lookups in task option fields](https://learn.mechanic.dev/resources/converting-tasks-from-shopify-rest-to-graphql/conversion-resource-lookups-in-task-option-fields) Last updated 1 year ago Was this helpful? --- # Events | Mechanic In Mechanic, an **event** represents anything that happens. This could be an order being paid, or a customer record being created, or a fulfillment being delivered. An event always has a [**topic**](https://learn.mechanic.dev/core/events/topics) , and **data** (even if the data is null/nil). Event attributes may be referenced in Liquid using the [**Event object**](https://learn.mechanic.dev/platform/liquid/objects/event) . Events may trigger any number of [**tasks**](https://learn.mechanic.dev/core/tasks) , resulting in any number of [**actions**](https://learn.mechanic.dev/core/actions) . Events are fed into Mechanic by the responsible party – for events that are about things in Shopify, for example, the events come to Mechanic from Shopify itself. Incoming events may be selectively skipped using [event filters](https://learn.mechanic.dev/platform/events/filters) . [PreviousConversion: Resource lookups in task option fields](https://learn.mechanic.dev/resources/converting-tasks-from-shopify-rest-to-graphql/conversion-resource-lookups-in-task-option-fields) [NextTopics](https://learn.mechanic.dev/core/events/topics) Last updated 9 months ago Was this helpful? --- # Fetching data from a shared Google sheet | Mechanic In this tutorial, you'll learn how to publish a Google sheet to the web as a comma-separated values (CSV) file and then fetch that data from Mechanic. [](https://learn.mechanic.dev/resources/tutorials/fetching-data-from-a-shared-google-sheet#instructions) Instructions -------------------------------------------------------------------------------------------------------------------------- ### [](https://learn.mechanic.dev/resources/tutorials/fetching-data-from-a-shared-google-sheet#id-1.-create-a-google-sheet-with-data) 1\. Create a Google sheet with data. The data in the sheet should be in a format that makes sense as a CSV file. The first row should contain the column headers and there shouldn't be any data on the sheet outside of those columns. You can either create a sheet with the sample data shown below or you can use your own data for this tutorial. Keep in mind that the column headers in the first row will be the exact keys that you need to reference in the task when iterating over the data rows for your own usage.  ### [](https://learn.mechanic.dev/resources/tutorials/fetching-data-from-a-shared-google-sheet#id-2.-publish-the-sheet-to-the-web-as-a-csv) 2\. Publish the sheet to the web as a CSV. Sharing sheets openly this way on the web so that is accessible by Mechanic works best for non-identifying data. Be sure to clean all customer-specific data and branding from your sheet data before publishing. From the **File / Share** menu, choose the **Publish to web** option.  From the **Link** tab of the modal dialog that opens, select the sheet you wish to share and the _Comma-separated values (.csv)_ option, and then click the **Publish** button.  After clicking OK on the confirmation dialog, the modal will update to show you the URL link that you will need to copy into the demonstration task configuration settings. You can safely close this dialog window now.  ### [](https://learn.mechanic.dev/resources/tutorials/fetching-data-from-a-shared-google-sheet#id-3.-add-and-configure-the-demonstration-task) 3\. Add and configure the demonstration task. You can either add the demonstration task using the **Try this task** button from this task library link - [Demonstration task: Fetch data from a shared Google sheet](https://tasks.mechanic.dev/demonstration-fetch-data-from-a-shared-google-sheet) - or you can add it from within the **Add task** screen inside of Mechanic. After adding the task you should update the **Gsheet URL** option field with the link to your sheet that was generated in the prior step. Update the **Alert email recipients** with one or more email addresses where you want to be notified in case Mechanic is not able to access the shared sheet (e.g. the share is disabled).  ### [](https://learn.mechanic.dev/resources/tutorials/fetching-data-from-a-shared-google-sheet#id-4.-run-the-task-and-review-the-output) 4\. Run the task and review the output. Run the task manually using the **Run task** button and it will run the first sequence of the task, which will make an **HTTP** request to **GET** the sheet data.  To see the results of the data retrieval you need to click on the **mechanic/actions/perform** child event after it appears.  ### [](https://learn.mechanic.dev/resources/tutorials/fetching-data-from-a-shared-google-sheet#next-steps) Next steps Using the reference information available in these docs, write your own Mechanic script to iterate over the rows of data (array of hashes) that is parsed from the CSV, and make useful [updates to your Shopify data](https://learn.mechanic.dev/core/actions/integrations/shopify) using the GraphQL or REST APIs. If you have any questions, head to [our community Slack](https://learn.mechanic.dev/resources/slack) . [PreviousCreating scheduled CSV feeds](https://learn.mechanic.dev/resources/tutorials/creating-scheduled-csv-feeds) [NextConverting tasks from Shopify REST to GraphQL](https://learn.mechanic.dev/resources/converting-tasks-from-shopify-rest-to-graphql) Last updated 2 years ago Was this helpful? --- # Tasks | Mechanic In Mechanic, a **task** is a bundle of logic and configuration, that responds to and interprets [**events**](https://learn.mechanic.dev/core/events) . The result of a task can define [**actions**](https://learn.mechanic.dev/core/actions) , which are the task's opportunities to have an effect on the world. A task responds to events based on its [**subscriptions**](https://learn.mechanic.dev/core/tasks/subscriptions) . When an event is received that matches a subscription, the task processes the event using its [**code**](https://learn.mechanic.dev/core/tasks/code) . The code has access to the event data; it also has access to the user's task configuration, through [**options**](https://learn.mechanic.dev/core/tasks/options) . Task code is written in Liquid, and is responsible for rendering a series of JSON objects (including [**action**](https://learn.mechanic.dev/core/tasks/code/action-objects) , [**error**](https://learn.mechanic.dev/core/tasks/code/error-objects) , and [**log**](https://learn.mechanic.dev/core/tasks/code/log-objects) objects), defining work to be performed once task rendering is complete. A task uses its [**preview**](https://learn.mechanic.dev/core/tasks/previews) to communicate ahead of time the work it intends to do. Previews are important for users, and are also important for Mechanic itself – Mechanic looks to the task preview to understand what permissions a task requires. Tasks may be written from scratch, or installed from the Mechanic library (available in-app and [on GitHub](https://github.com/lightward/mechanic-tasks) ). Once installed, a task's code may be modified at any time. Working on getting better at task-writing? See [Practicing writing tasks](https://learn.mechanic.dev/resources/tutorials/practicing-writing-tasks) , and [Writing a high-quality task](https://learn.mechanic.dev/techniques/writing-a-high-quality-task) . [](https://learn.mechanic.dev/core/tasks#example) Example -------------------------------------------------------------- This very basic task subscribes to shopify/customers/create, and renders an [Email action](https://learn.mechanic.dev/core/actions/email) , using an email subject and body taken from user-configured [options](https://learn.mechanic.dev/core/tasks/options) . Subscriptions Code Export Copy shopify/customers/create Copy {% action "email" %} { "to": {{ options.email_recipient__email_required | json }}, "subject": {{ options.email_subject__required | json }}, "body": {{ options.email_body__multiline_required | newline_to_br | json }}, "from_display_name": {{ shop.name | json }} } {% endaction %} Copy {"name":"Customer signup alerts","options":{"email_recipient__email_required":"[email protected]","email_subject__required":"A new customer has signed up: {{ customer.email }}","email_body__multiline_required":"Hi! View this customer's details online:\n\nhttps://{{ shop.domain }}/admin/customers/{{ customer.id }}\n\n-Mechanic"},"script":"{% action \"email\" %}\n {\n \"to\": {{ options.email_recipient__email_required | json }},\n \"subject\": {{ options.email_subject__required | json }},\n \"body\": {{ options.email_body__multiline_required | newline_to_br | json }},\n \"from_display_name\": {{ shop.name | json }}\n }\n{% endaction %}","subscriptions":["shopify/customers/create"],"online_store_javascript":null,"order_status_javascript":null,"docs":null,"subscriptions_template":"shopify/customers/create","shopify_api_version":"2022-04","liquid_profiling":false,"perform_action_runs_in_sequence":false,"halt_action_run_sequence_on_error":false,"preview_event_definitions":[]}  [PreviousParent and child events](https://learn.mechanic.dev/core/events/parent-and-child-events) [NextSubscriptions](https://learn.mechanic.dev/core/tasks/subscriptions) Last updated 9 months ago Was this helpful? --- # Advanced settings | Mechanic [Documentation](https://learn.mechanic.dev/core/tasks/advanced-settings/documentation) [JavaScript](https://learn.mechanic.dev/core/tasks/advanced-settings/javascript) [Perform action runs in sequence](https://learn.mechanic.dev/core/tasks/advanced-settings/perform-action-runs-in-sequence) [PreviousShopify API version](https://learn.mechanic.dev/core/tasks/shopify-api-version) [NextDocumentation](https://learn.mechanic.dev/core/tasks/advanced-settings/documentation) Last updated 3 years ago Was this helpful? --- # Send recurring reminders about unpaid orders | Mechanic [PreviousSend an email when a specific product is shipped](https://learn.mechanic.dev/resources/tutorials/video-walkthroughs/send-an-email-when-a-specific-product-is-shipped) [NextSend an email when a product's price goes below its cost](https://learn.mechanic.dev/resources/tutorials/video-walkthroughs/send-an-email-when-a-products-price-goes-below-its-cost) Last updated 4 years ago Was this helpful? --- # Perform action runs in sequence | Mechanic Mechanic's [run system](https://docs.usemechanic.com/article/425-an-introduction-to-runs) works asynchronously, performing as much work as possible, as quickly as possible. However, there are cases where it's important that actions run in a sequence – one after the other. We support this with an advanced task setting called "Perform action runs in sequence", configured in two parts: * **Perform action runs in sequence** – When enabled, Mechanic will only run one of the task's resulting actions at a time, performing them in the order in which they were generated. * **Halt the sequence when one fails** – When this option is also enabled, Mechanic will only run the next action if the current action was performed successfully. If the action fails, all following actions will be marked as failed as well, with error messages explaining the situation. Action run sequences are enforced within each task run. This means that a task could see more than one of its actions performed at the same time, if the task itself were to run multiple times, simultaneously. To explain by example: a task that responds to mechanic/scheduler/10min, generating a sequence of 5 actions that each take 1 minute to run, will never see those actions overlap. However, if the task generated 15 actions instead, the actions would begin to overlap, as the task generates 15-minute action sequences every 10 minutes.  [PreviousJavaScript](https://learn.mechanic.dev/core/tasks/advanced-settings/javascript) [NextImport and export](https://learn.mechanic.dev/core/tasks/import-and-export) Last updated 3 years ago Was this helpful? --- # Custom validation | Mechanic A task may enforce **custom validation** for options by including validation logic in its code, inspecting the current value of an option and rendering an [**error object**](https://learn.mechanic.dev/core/tasks/code/error-objects) if the option does not meet its criteria. A modification to a task option will always result in a new [**preview**](https://learn.mechanic.dev/core/tasks/previews) being rendered. In this way, a task developer may provide the user with immediate feedback on their task configuration. [](https://learn.mechanic.dev/core/tasks/options/custom-validation#example) Example ---------------------------------------------------------------------------------------- In this example, a task begins by validating an option called "A positive number". The only flags on this option are "required" and "number", meaning that Mechanic's involvement is limited to making sure the user fills in this task option with a number. Once the option is filled in, the task preview will be rendered. If the user has entered a zero, or a negative number, the [error tag](https://learn.mechanic.dev/platform/liquid/tags/error) is used to generate an [error object](https://learn.mechanic.dev/core/tasks/code/error-objects) . The error message will then be shown to the user, and they will be prevented from saving the task until they provide valid input. Copy {% if options.a_positive_number__required_number <= 0 %} {% error "The option 'A positive number' must be greater than zero." %} {% endif %} {% action "cache", "set", "a_positive_number_to_remember", options.a_positive_number__required_number %} [PreviousOptions](https://learn.mechanic.dev/core/tasks/options) [NextPreviews](https://learn.mechanic.dev/core/tasks/previews) Last updated 6 months ago Was this helpful? --- # Environment variables | Mechanic A task's Liquid code always has access to a set of **environment variables**, defined by Mechanic. Environment variables may be reassigned as needed. (When preparing a task preview, this may be a necessary technique.) To learn more, see [Stub data](https://learn.mechanic.dev/core/tasks/previews/stub-data) . Variable Contents `shop` An object containing [Shopify's REST representation of the current Shopify store](https://shopify.dev/docs/admin-api/rest/reference/store-properties/shop) `event` An object containing information about the current event `cache` The current store's [Mechanic cache](https://learn.mechanic.dev/platform/liquid/objects/cache) object, supporting lookups for cached values `task` An object containing information about the current task `options` An object containing task [**options**](https://learn.mechanic.dev/core/tasks/options) , configured by the user The [Shopify variables](https://learn.mechanic.dev/core/tasks/code/environment-variables#shopify-variables) available to tasks always contain data drawn from the event itself. If a task has a offset event subscription, this data may be outdated by the time the task runs. To reload the data in a Shopify variable, use something like this: Copy {% unless event.preview %} {% assign customer = customer.reload %} {% endunless %} Remember, Mechanic does not permit access to the Shopify API during [event preview](https://learn.mechanic.dev/core/tasks/previews) . Using this `unless` statement ensures that reloading only happens during a live event. [](https://learn.mechanic.dev/core/tasks/code/environment-variables#event-subject-variables) Event subject variables ------------------------------------------------------------------------------------------------------------------------- When a task is actually invoked for an event, it may have access to an additional variable, determined by the specific event it is responding to. When this is the case, the additional variable will be named after the event subject, and its contents will be established by the event's data. The name of this variable is communicated by the Mechanic task editor, based on the task's current [**subscriptions**](https://learn.mechanic.dev/core/tasks/subscriptions) . For example, a subscription to shopify/customers/create will make available a variable called `customer`. A subscription to shopify/products/update will expose a variable called `product`, etc. [](https://learn.mechanic.dev/core/tasks/code/environment-variables#shopify-variables) Shopify variables ------------------------------------------------------------------------------------------------------------- All Shopify events support an additional variable named after the event topic. For example, when a task responds to a shopify/customers/create event, it will have access to an additional variable named `customer`, containing the customer data contained in the event. Shopify events always contain data from Shopify's REST representation of each resource; therefore, automatic Shopify variables always contain data from the REST representation as well. The best resource for the data available for each variable type is [Shopify's REST Admin API reference](https://shopify.dev/docs/admin-api/rest/reference) . **Shopify variables in Mechanic do not necessarily contain the same attributes as Liquid variables used in Shopify (in places like themes or email templates) – even if they share the same name.** In Mechanic, Shopify variables always contain data from Shopify events, which are delivered to Mechanic via webhook. This means that Shopify variables always have the same data structure as Shopify webhooks, corresponding to Shopify's REST representation for this data. For example, while Shopify themes support `customer.name`, Mechanic does not (because [Shopify's REST representation of the customer resource](https://shopify.dev/docs/admin-api/rest/reference/customers/customer) does not contain a "name" property). On the other hand, Mechanic supports `customer.created_at`, while Shopify themes do not. [PreviousCode](https://learn.mechanic.dev/core/tasks/code) [NextAction objects](https://learn.mechanic.dev/core/tasks/code/action-objects) Last updated 2 years ago Was this helpful? --- # Parent and child events | Mechanic In specific cases, events may be triggered by activity associated with an earlier event. In these scenarios, we describe the subsequent event as a **child event**, and the preceding event as a **parent event**. * The [Event action](https://learn.mechanic.dev/core/actions/event) generates a new child event, when performed * A subscription to the [mechanic/actions/perform](https://learn.mechanic.dev/techniques/responding-to-action-results) topic generates new child events as actions are performed Tasks responding to child events may reference to the parent's event using `{{ event.parent }}`. Parent events are recursively available (as in `{{ event.parent.parent.parent }}`), to a limit of 5 generations back. When viewing any given event in Mechanic, look in the event details to find any parent or child relationships that apply. Click through to any displayed parent or child event to view that event's details.  [](https://learn.mechanic.dev/core/events/parent-and-child-events#example) Example --------------------------------------------------------------------------------------- Subscriptions Code Copy mechanic/user/trigger user/fan/out Copy {% assign n = event.data | default: 0 | times: 1 %} {% if n < 5 %} {% for m in (0..n) %} {% action "event" %} { "topic": "user/fan/out", "data": {{ n | plus: 1 | json }}, "task_id": {{ task.id | json }} } {% endaction %} {% endfor %} {% else %} {% action "echo", event_data: event.data, parent_event_data: event.parent.data %} {% endif %} As written, this task will "fan out": it will generate 1 child event, which will then generate 2 child events, each of which will then generate 3 child events, and each of those will then generate 4 child events, and finally, each of those events will generate 5 child events of their own. The result: 154 events, created with a single click. 💪 Importantly, note the `"task_id"` option, applied to the Event action. This option ensures that only this task, and no other, will respond to the new event. While it's unlikely that any other task will subscribe to "user/fan/out" events, this option is important for ensuring expected behavior. [PreviousTopics](https://learn.mechanic.dev/core/events/topics) [NextTasks](https://learn.mechanic.dev/core/tasks) Last updated 9 months ago Was this helpful? --- # Creating scheduled CSV feeds | Mechanic In this tutorial, you'll learn how to create a feed of your shop's data, and make it available on your online store, at a URL like `https://example.com/pages/feed`[.](https://example.com/pages/feed) Tip: The data you generate can be imported directly into Google Sheets. Learn more: [Can I send data to Google Sheets?](https://learn.mechanic.dev/faq/can-i-send-data-to-google-sheets) This technique has several limitations: * Shopify doesn't support delivering the feed contents as plaintext. To get technical, this means that the feed will always be delivered with a content type of text/html. * Because this task stores feed values as a shop metafield, feeds created with this technique may only contain and display up to 65,535 characters. To move beyond these, consider using the [FTP action](https://learn.mechanic.dev/core/actions/ftp) to upload your feed to your own server. [](https://learn.mechanic.dev/resources/tutorials/creating-scheduled-csv-feeds#instructions) Instructions -------------------------------------------------------------------------------------------------------------- ### [](https://learn.mechanic.dev/resources/tutorials/creating-scheduled-csv-feeds#id-1.-create-your-task) 1\. Create your task. Start with our example task, using the "Try this task" button to add it to your account: [**Task: Create a product inventory feed**](https://tasks.mechanic.dev/create-a-product-inventory-feed) Immediately after adding the task, run it by clicking the "Run task" button. This will populate your shop's records with the initial value of the feed. This task replicates Shopify's own product inventory CSV export. Feel free to make changes to the script, and don't hesitate to get in touch if you have questions. :) ### [](https://learn.mechanic.dev/resources/tutorials/creating-scheduled-csv-feeds#id-2.-create-a-page-template-called-page.feed.liquid) 2\. Create a page template, called "page.feed.liquid". This is the template that will be responsible for displaying your feed contents, without the usual page formatting that your shop's theme usually applies. To do this, navigate to the "Themes" section of your Shopify admin (under "Online Store", or by searching for "themes"). Then, under the "Actions" menu for your current theme, click the "Edit code" link.  Next, click "Add a new template".  Then, select the option for creating a "page" template, of type "liquid", and fill in the text box with the name "feed" (or another template name to your liking).  Next, fill in the template contents with the following: Copy {%- layout none -%} {{- shop.metafields.mechanic.feed -}} ... and click the "Save" button. Your template should look like this:  ### [](https://learn.mechanic.dev/resources/tutorials/creating-scheduled-csv-feeds#id-3.-create-a-new-page-to-use-as-your-feed) 3\. Create a new page to use as your feed. Navigate to the "Pages" section of the Shopify admin (under "Online Store"), and click the "Add page" button (or search the admin for "add page"). Name the page "Feed" (or another name of your liking), and change the page template to "page.feed.liquid".  Save the page. ### [](https://learn.mechanic.dev/resources/tutorials/creating-scheduled-csv-feeds#youre-done) You're done! Open up the page you just created, and you should see the contents of your feed. :) If you have any questions, head to [our community Slack](https://learn.mechanic.dev/resources/slack) . [PreviousTriggering tasks from a contact form](https://learn.mechanic.dev/resources/tutorials/triggering-tasks-from-a-contact-form) [NextFetching data from a shared Google sheet](https://learn.mechanic.dev/resources/tutorials/fetching-data-from-a-shared-google-sheet) Last updated 6 months ago Was this helpful? --- # Conversion: Connections from a resource | Mechanic Until further notice, Shopify will continue to send product webhook data in a REST-like format. Tasks that **only** use the fields available in the webhook (e.g. `product.title`) may not need to be converted by the deprecation notice date. However, if connections to other resources are made from that product (e.g. `product.collections`), then that will require conversion. This is a simple task to loop through a product's collections, check if the collection contains a certain tag, then log out the collection title. REST - Looping through a product's collections Copy {% for collection in product.collections %} {% assign collection_tags = collection.tags | split: ", " %} {% if collection_tags contains "my-tag" %} {% log collection_with_my_tag: collection.title %} {% endif %} {% endfor %} * * * The GraphQL version of the the task above use a paginated query to get all of the collections a product is a member of. The outer loop upper range (e.g. the **10** in `{% for n in (1..10) %}`) is arbitrary, and you may adjust it to the approximate maximum number of collections any given product might have. The event preview block in this task sample makes this code appear to be overly verbose, however the [preview block](https://learn.mechanic.dev/core/tasks/previews/stub-data#stubbing-graphql-data) is often an important step to ensure that Mechanic prompts for the correct scopes for reading and writing Shopify API data. GraphQL - Querying a product's collections with pagination Copy {% assign cursor = nil %} {% for n in (1..10) %} {% capture query %} query { product(id: {{ product.admin_graphql_api_id | json }}) { collections( first: 250 after: {{ cursor | json }} ) { pageInfo { hasNextPage endCursor } nodes { id title tags } } } } {% endcapture %} {% assign result = query | shopify %} {% if event.preview %} {% capture result_json %} { "data": { "products": { "nodes": [\ {\ "collections": {\ "nodes": [\ {\ "id": "gid://shopify/Collection/1234567890",\ "title": "Widget collection",\ "tags": ["my-tag"]\ }\ ]\ }\ }\ ] } } } {% endcapture %} {% assign result = result_json | parse_json %} {% endif %} {% for collection in result.data.product.collections.nodes %} {% if collection.tags contains "my-tag" %} {% log collection_with_my_tag: collection.title %} {% endif %} {% endfor %} {% if result.data.products.pageInfo.hasNextPage %} {% assign cursor = result.data.products.pageInfo.endCursor %} {% else %} {% break %} {% endif %} {% endfor %} To assist with generating a paginated query block, you can use the ["paginated\_query" snippet](https://learn.mechanic.dev/platform/liquid/mechanic-code-snippets#paginated_query) in the Mechanic code editor, and it will prompt you to choose the object type to paginate over (e.g. products). [PreviousConversion: Resource loops to paginated queries](https://learn.mechanic.dev/resources/converting-tasks-from-shopify-rest-to-graphql/conversion-resource-loops-to-paginated-queries) [NextConversion: Metafield lookups from a resource](https://learn.mechanic.dev/resources/converting-tasks-from-shopify-rest-to-graphql/conversion-metafield-lookups-from-a-resource) Last updated 1 year ago Was this helpful? --- # Defining preview events | Mechanic During [**task preview**](https://learn.mechanic.dev/core/tasks/previews) , Mechanic scans the task's [**subscriptions**](https://learn.mechanic.dev/core/tasks/subscriptions) . For each [**event topic**](https://learn.mechanic.dev/core/events/topics) found, Mechanic constructs a synthetic **preview event**, resembling one that the task might encounter during live use. By default, each preview event's data is sampled from previous events that the Mechanic account has seen, for the same topic. However, developers may define their own preview events, containing whatever data the developer wishes to use for preview. This may be useful for several reasons: * Most tasks conditionally respond to events based on their data. Controlling the event data present during preview allows the developer to deterministically verify the results of the action. * Further, by deterministically/predictably generating actions, the developer can consistently demonstrate the permissions they need to Mechanic. (To learn more about this, see [Previews](https://learn.mechanic.dev/core/tasks/previews) .) * Defining preview event data is usually simpler than defining [stub data](https://learn.mechanic.dev/core/tasks/previews/stub-data) . * Stubbing the `event` variable (or any of the [subject variables](https://learn.mechanic.dev/core/tasks/code/environment-variables#event-subject-variables) ) removes any intelligence from the objects Mechanic generates from event data, a drawback avoided by defining a preview event and its data. Using the [Order object](https://learn.mechanic.dev/platform/liquid/objects/shopify/order) as an example, a task may typically access its custom attributes via `order.note_attributes.color`, or via `order.note_attributes[0].value`. This dynamic behavior is lost if the `event` variable is stubbed out, which can result in behaviors that are difficult to diagnose. * Multiple preview events may be defined per event topic. This allows developers to verify that their task renders the appropriate results under a variety of circumstances. * Defined preview events can be labeled with a description, which is visible in the task preview pane. This makes it easy to identify the scenario that a preview event is meant to represent. Preview event definitions cannot provide for return values from Shopify query operations (i.e. output from the [shopify filter](https://learn.mechanic.dev/platform/liquid/filters#shopify) , or the result of traversing Shopify Liquid objects, as in `customer.orders.first`). For those purposes, use the [**stub data**](https://learn.mechanic.dev/core/tasks/previews/stub-data) technique. [](https://learn.mechanic.dev/core/tasks/previews/events#configuration) Configuration ------------------------------------------------------------------------------------------ Preview events may be defined using the "Edit preview events" button, in the task preview pane. The configuration area for preview events contains a quickstart link for each event topic the task subscribes to, allowing developers to get started using sample data if the event topic is known to Mechanic. Or, the developer may start with a blank preview event definition, filling in whatever topic and data are useful. A developer may define any number of preview events per topic. If no preview events are defined for a given topic, Mechanic will construct its own ad-hoc event during preview. ### [](https://learn.mechanic.dev/core/tasks/previews/events#properties) Properties #### [](https://learn.mechanic.dev/core/tasks/previews/events#description) Description Displayed beneath the event topic in the preview pane, allowing the developer to distinguish one scenario from another. #### [](https://learn.mechanic.dev/core/tasks/previews/events#topic) Topic Identifies the event definition to Mechanic, when Mechanic goes to construct preview events by topic. #### [](https://learn.mechanic.dev/core/tasks/previews/events#data) Data Used to construct `event.data`, and may be set to whatever values are useful in representing a specific scenario. The data structures used here should resemble what Mechanic will receive for a live event of the same topic. Notably, the data here _can_ be limited to just the properties that are useful. For example, while Mechanic might normally generate a complete payload for shopify/orders/create, the developer might only care about the `"email"` property of the order – and so their defined preview event data might be limited to just that property. (Note that the inverse may not be true: defining preview event data for traversals into other objects, e.g. using preview event data to define a value for `order.line_items[0].product.title`, will _not_ work.) ### [](https://learn.mechanic.dev/core/tasks/previews/events#example) Example For a trivial task, subscribing to shopify/customers/create, and having the following task code... Copy {% if customer.email contains "gmail.com" %} {% log message: "got a gmail user!", email: customer.email %} {% else %} {% log message: "got someone else!", email: customer.email %} {% endif %} ... we define two preview events, one which represents a Gmail user, and one which does not. This allows us to easily assert that the task behaves properly in both scenarios.  [](https://learn.mechanic.dev/core/tasks/previews/events#versioning) Versioning ------------------------------------------------------------------------------------ Preview event definitions are stored along with the task itself, and thus are present in the tasks version history (and, naturally, in task exports). Because definitions are a part of the task itself, they're appropriate for use as a testing tool, allowing the developer to verify that a task behaves as intended at every stage of the task's development. [PreviousPreviews](https://learn.mechanic.dev/core/tasks/previews) [NextStub data](https://learn.mechanic.dev/core/tasks/previews/stub-data) Last updated 2 years ago Was this helpful? --- # Creating a Mechanic webhook | Mechanic Webhooks are the nearly ubiquitous carriers of information to and from services across the internet - services like IFTTT, Zapier, Stripe, PayPal, JotForm, and countless more. You can use webhooks to send information from these services into Mechanic, where you can then perform any [logic](https://learn.mechanic.dev/core/tasks/code) and [actions](https://learn.mechanic.dev/core/actions) you need. This is a tutorial for getting started quickly. To learn more about webhooks themselves, see [Mechanic webhooks](https://learn.mechanic.dev/platform/webhooks) . When Mechanic receives data via a webhook, it fires off an event with the user topic of your choice. (For example, if you've set up an IFTTT webhook that sends you tweets, you might choose the Mechanic topic `user/ifttt/tweet`.) To make use of these events, create one or more tasks that subscribe to this topic. That's it! Let's review a detailed example. ### [](https://learn.mechanic.dev/resources/tutorials/creating-a-mechanic-webhook#id-1.-create-a-mechanic-webhook) 1\. Create a Mechanic webhook. Start by opening Mechanic, from the "Apps" section of Shopify. Once in Mechanic, click the "Settings" button in the upper-right corner, then navigate to the "Webhooks" tab.  Webhooks should be named after the service that will be sending you data, with an event topic that makes sense, using the format `user/subject/verb`. For this example, we'll simply call ours "Example", with an event topic of "user/webhook/test". Click the submit button to save the webhook, and use the copy button to copy the resulting webhook URL.  The URL will look something like this: Copy https://webhooks.mechanic.dev/00000000-0000-0000-0000-000000000000 Older webhook URLs resemble `https://usemechanic.com/webhook/00...00`. This URL structure still works, but we recommend migrating to `https://webhooks.mechanic.dev/00...00` instead, for enhanced reliability. ### [](https://learn.mechanic.dev/resources/tutorials/creating-a-mechanic-webhook#id-2.-create-a-task-that-subscribes-to-your-webhook-event) 2\. Create a task that subscribes to your webhook event. Back on the Mechanic homepage, click the "Add task" link.  Then, click the "Start a blank task" button.  Keeping things simple for this example, we'll title the task "Webhook test", with a subscription to "user/webhook/text" (to match the webhook configuration), and a simple [Echo action](https://learn.mechanic.dev/core/actions/echo) in the task code.  Lastly, save the task. ### [](https://learn.mechanic.dev/resources/tutorials/creating-a-mechanic-webhook#id-3.-test-your-webhook) 3\. Test your webhook. Open [https://reqbin.com/](https://reqbin.com/) , and construct a request to our webhook. Here, we'll select "POST", paste in the webhook URL, and fill in a simple piece of content. (Webhooks support plain text, form-encoded content, _and_ JSON; for this example, we'll use JSON.)  Click the "Send" button, and you'll see a 204 response returned within ReqBin. Over in Mechanic, watch for the new event on the "Events" page (or in the "Recent events" section of the Mechanic homepage):  Click on that event to see the results of our task and its echo action.  ### [](https://learn.mechanic.dev/resources/tutorials/creating-a-mechanic-webhook#id-4.-connect-your-webhook-url-to-another-service) 4\. Connect your webhook URL to another service. This last part is up to you! Provide the webhook URL, generated by Mechanic, to whatever service you'd like to use. When provided with this URL, the service will start sending your data over to Mechanic for processing. That's it! :) Adjust to taste. [PreviousCreating products in bulk](https://learn.mechanic.dev/resources/tutorials/video-walkthroughs/creating-products-in-bulk) [NextPracticing writing tasks](https://learn.mechanic.dev/resources/tutorials/practicing-writing-tasks) Last updated 9 months ago Was this helpful? --- # Shopify API version | Mechanic Each task is configured with a specific [**Shopify API version**](https://learn.mechanic.dev/core/shopify/api-versions) , defaulting to the latest version at the time of the task's creation. This version is used in all activity related to the current task, including: * REST API calls performed to support Liquid lookups * GraphQL calls performed by [the shopify Liquid filter](https://learn.mechanic.dev/core/tasks/shopify-api-version) * All Shopify API calls performed by [the Shopify action](https://learn.mechanic.dev/core/actions/integrations/shopify) , including [bulk operations](https://learn.mechanic.dev/core/tasks/shopify-api-version) When a task run starts, it checks the Shopify API version configured for the task at that time. Action runs always inherit their Shopify API version from their task run. This means that changing a task's Shopify API version can affect queued task runs, but won't change queued action runs. [](https://learn.mechanic.dev/core/tasks/shopify-api-version#using-unstable) Using "unstable" -------------------------------------------------------------------------------------------------- All Shopify API versions are named with a specific date (i.e. "2021-07"), except for "unstable". This version receives regular updates from Shopify, and its features may change without notice. Most tasks should use a dated version, to maximize the amount of time a task can rely on a specific set of Shopify API features. [](https://learn.mechanic.dev/core/tasks/shopify-api-version#automatic-version-upgrades) Automatic version upgrades ------------------------------------------------------------------------------------------------------------------------ Shopify supports each version for 12 months (except for "unstable", which is always available). 30 days before a task's version becomes unsupported, Mechanic will automatically begin calling the closest supported version instead. Shopify may, at times, mark certain API features as deprecated. If a Mechanic account calls a deprecated API, Mechanic will display the deprecation notice in the app. Learn more about [Shopify API deprecations](https://learn.mechanic.dev/core/shopify/api-versions#deprecations) . [](https://learn.mechanic.dev/core/tasks/shopify-api-version#changing-versions) Changing versions ------------------------------------------------------------------------------------------------------ The selector for a task's Shopify API version is available in Advanced mode, below the task code area.  [PreviousStub data](https://learn.mechanic.dev/core/tasks/previews/stub-data) [NextAdvanced settings](https://learn.mechanic.dev/core/tasks/advanced-settings) Last updated 3 years ago Was this helpful? --- # Converting tasks from Shopify REST to GraphQL | Mechanic **Important Notice** Shopify is deprecating the Shopify Admin REST API which the Mechanic REST objects depend on. The first round of deprecations involve the product and variant endpoints. Read about the deprecation [here](https://shopify.dev/docs/apps/build/graphql/migrate/new-product-model#whats-changing) and [here](https://shopify.dev/docs/apps/build/graphql/migrate) . Use the [GraphQL](https://learn.mechanic.dev/core/actions/shopify#graphql) going forward. The [product](https://learn.mechanic.dev/platform/liquid/objects/shopify/product) and [variant](https://learn.mechanic.dev/platform/liquid/objects/shopify/variant) objects will cease to work on on Feb 1, 2025 due to the changes being made by Shopify. Shopify will phase out the REST API completely over time, you can read more about this [here](https://shopify.dev/docs/apps/build/graphql/migrate) . These conversion tutorials will be be based on products, variants, and associated resources, but the methodologies are applicable to other type of REST resources as well. #### [](https://learn.mechanic.dev/resources/converting-tasks-from-shopify-rest-to-graphql#at-a-high-level-converting-mechanic-tasks-from-shopify-rest-lookups-to-graphql-queries-involves) At a high-level, converting Mechanic tasks from Shopify REST lookups to GraphQL queries involves: 1. **Understanding the Shopify GraphQL schema** Familiarize yourself with the [Shopify GraphQL Admin API](https://shopify.dev/docs/api/admin-graphql) objects, queries, and mutations. 2. **Review how to use GraphQL in Mechanic** Start [here](https://learn.mechanic.dev/core/shopify/read/graphql-in-liquid) and peruse the [task library](https://learn.mechanic.dev/resources/task-library) to see examples of GraphQL usage in tasks. 3. **Identify REST usage within a task** Broadly, any usage where **one Liquid REST object** is used to reference another Liquid REST object with dot notation. This does not include fields on the original REST-like webhook resource (e.g. `product.title`). For the product and variant resource deprecations specifically, this includes: * `shop.products` * `shop.variants` * `collection.products` * `inventory_item.variant` * `inventory_level.variant` * `line_item.product` * `line_item.variant` * `product.collections` * `product.images` \*️ * `product.metafields` * `product.variants` \*️ * `variant.inventory_item` * `variant.inventory_levels` * `variant.metafields` * `variant.product` 4. **Field mapping**: Identify the objects, fields, and nested structures needed in GraphQL based on the existing REST usage within a task. Build and validate queries using [Shopify's GraphiQL Explorer](https://learn.mechanic.dev/platform/graphql/basics/shopify-admin-api-graphiql-explorer) . 5. **Update Mechanic task code** : Replace the relevant REST calls with Mechanic-flavored Liquid GraphQL query and result objects (see the tutorials following this page for examples). 6. **Testing**: Trigger the updated task to make sure it returns the expected results and/or takes the expected actions. The product webhook does include an array of images and variants in the product JSON which will still be accessible via dot notation. Note that these are not the same as the previously available Mechanic REST lookups for those resources. The images and variants data arrays should be used with caution once Shopify releases [support for 2 thousand variants per product](https://shopify.dev/docs/apps/build/graphql/migrate/new-product-model#whats-changing) , in conjunction with the product and variant REST endpoint deprecations. The product webhook will only include full detail for the first 100 variants. It is not yet clear what Shopify will do with images in the product webhook. [PreviousFetching data from a shared Google sheet](https://learn.mechanic.dev/resources/tutorials/fetching-data-from-a-shared-google-sheet) [NextConversion: Single resource lookups](https://learn.mechanic.dev/resources/converting-tasks-from-shopify-rest-to-graphql/conversion-single-resource-lookups) Last updated 1 year ago Was this helpful? --- # JavaScript | Mechanic Shopify allows apps to inject JavaScript into the online storefront. (This is facilitated by [ScriptTag](https://shopify.dev/docs/admin-api/rest/reference/online-store/scripttag) in the Shopify API.) Mechanic supports this by allowing each task to specify its own JavaScript, to be injected into the online storefront.  Here, the developer can add in their own JavaScript code, taking advantage of Liquid for mixing in data from the current store, or from the current task's options. A task's JavaScript content only has access to to the `shop` and `options` Liquid variables. The rendering context is similar to that of [task subscriptions](https://learn.mechanic.dev/core/tasks/subscriptions#using-liquid) ; Liquid code here does not have access to any data related to events, and cannot dynamically respond to any information about the visitor's current request.  [PreviousDocumentation](https://learn.mechanic.dev/core/tasks/advanced-settings/documentation) [NextPerform action runs in sequence](https://learn.mechanic.dev/core/tasks/advanced-settings/perform-action-runs-in-sequence) Last updated 9 months ago Was this helpful? --- # Log objects | Mechanic Log objects are useful for recording information for later reference. They have no side-effects. Carefully chosen log objects can massively simplify post-hoc debugging, especially (as we've found) when investigating merchant bug reports. A log object is a plain JSON object, having the following structure: Copy { "log": LOG_DETAILS } The log details can be any JSON value. Log objects are most easily generated using the [**log tag**](https://learn.mechanic.dev/platform/liquid/tags/log) . Log objects appear wherever task run results are visible, including the task preview and when viewing an event.  A log object visible in a task preview  A log object visible in a task run's result [PreviousError objects](https://learn.mechanic.dev/core/tasks/code/error-objects) [NextOptions](https://learn.mechanic.dev/core/tasks/options) Last updated 2 years ago Was this helpful? --- # Error objects | Mechanic When a task renders an error object, the task run will be marked as failed, and no rendered action runs will be performed. This is a good way to communicate an intentional failure to the user, when your Liquid code detects a certain condition. A task that renders an error object [during preview](https://learn.mechanic.dev/core/tasks/previews) will interrupt the preview, and visibly communicate the error to the user. This makes error objects a useful way to validate [task options](https://learn.mechanic.dev/core/tasks/options) . Unlike a "raised" exception in other programming languages, a rendered error object is simply added to the list of the task run's JSON objects. At the completion of task code rendering, all objects are evaluated; at that point, if an error object is among them, the error is then raised and shown to the user. An error object does not halt rendering of the task's Liquid code, but it does prevent any other rendered objects from having an effect. Specifically, this means that the presence of an error object means that any action objects will be ignored. This also means that rendering an error object will not prevent the task from reaching any syntax errors (or other problematic code) later on in the task's Liquid code. An error object is a plain JSON object, having the following structure: Copy { "error": ERROR_DETAILS } The error details can be any JSON value. This value will be represented to the user as the reason for the task failing. Error objects are most easily generated using the [**error tag**](https://learn.mechanic.dev/platform/liquid/tags/error) . [PreviousAction objects](https://learn.mechanic.dev/core/tasks/code/action-objects) [NextLog objects](https://learn.mechanic.dev/core/tasks/code/log-objects) Last updated 3 years ago Was this helpful? --- # Files | Mechanic The **Files** action evaluates its options using [**file generators**](https://learn.mechanic.dev/core/actions/file-generators) , temporarily storing the resulting files and making them available via a randomized Mechanic URL. This action is most useful in concert with [mechanic/actions/perform](https://learn.mechanic.dev/techniques/responding-to-action-results) , by which a task may take the resulting file URLs and pass them on to another service. Used by itself, this action can also be useful for quickly testing file generators. [](https://learn.mechanic.dev/core/actions/files#options) Options ---------------------------------------------------------------------- This action accepts a JSON object, whose keys are filenames and whose values are [file generators](https://learn.mechanic.dev/core/actions/file-generators) . In this way, many files may be defined and generated by a single Files action. [](https://learn.mechanic.dev/core/actions/files#result) Result -------------------------------------------------------------------- In Mechanic, actions are performed after their originating task run concludes. Actions are not performed inline during the task's Liquid rendering. To inspect and respond to the results of an HTTP action, add a task subscription to mechanic/actions/perform, allowing the action to re-invoke the task with the action result data. Learn more: [Responding to action results](https://learn.mechanic.dev/techniques/responding-to-action-results) A Files action returns an object having the same keys (i.e. filenames) as its input. Each value is an object, having the following properties: File property Description `expires_at` An ISO8601 timestamp, specifying when the file will expire `mime_type` The [MIME type](https://www.iana.org/assignments/media-types/media-types.xhtml) of the generated file `name` The filename, as given in the original action options `size` The size of the generated file, in bytes `url` The URL at which this file will be available, until it expires [](https://learn.mechanic.dev/core/actions/files#example) Example ---------------------------------------------------------------------- This task generates a variety of files. It then re-invokes itself (via mechanic/actions/perform), sending an email containing links to each of the generated files. Subscriptions Code Copy mechanic/user/trigger mechanic/actions/perform Copy {% if event.topic == "mechanic/user/trigger" %} {% action "files" %} { "journal.txt": "hello world!", "table.csv": "Title,SKU\nRed T-Shirt,TEE-R", "invoice.pdf": { "pdf": { "html": "
It's due!
" } }, "secure.zip": { "zip": { "password": "opensesame", "files": { "confirmations.txt": "this data is protected with zipcrypto encryption" } } }, "external.jpg": { "url": "https://upload.wikimedia.org/wikipedia/en/a/a9/Example.jpg" } } {% endaction %} {% elsif event.topic == "mechanic/actions/perform" and action.type == "files" %} {% capture email_body %}The following file(s) have been generated:
-Mechanic
{% endcapture %} {% action "email" %} { "to": "[email protected]", "subject": {{ action.run.result | size | append: " file(s) generated" | json }}, "body": {{ email_body | json }} } {% endaction %} {% endif %} [PreviousEvent](https://learn.mechanic.dev/core/actions/event) [NextFTP](https://learn.mechanic.dev/core/actions/ftp) Last updated 11 months ago Was this helpful? --- # Action objects | Mechanic An action object defines work to be performed by an [**action**](https://learn.mechanic.dev/core/actions) , after the task is fully finished rendering. Action objects are most easily generated using the [**action tag**](https://learn.mechanic.dev/platform/liquid/tags/action) . An action object is a plain JSON object, having the following structure: Copy { "action": { "type": ACTION_TYPE, "options": ACTION_OPTIONS, "meta": ACTION_META } } Use the [action tag](https://learn.mechanic.dev/platform/liquid/tags/action) to skip the boilerplate while writing actions. All tasks in the Mechanic task library use the action tag, rather than writing out the action object in raw JSON. In Mechanic, actions are performed after their originating task run concludes. Actions are not performed inline during the task's Liquid rendering. To inspect and respond to the results of an HTTP action, add a task subscription to mechanic/actions/perform, allowing the action to re-invoke the task with the action result data. Learn more: [Responding to action results](https://learn.mechanic.dev/techniques/responding-to-action-results) [](https://learn.mechanic.dev/core/tasks/code/action-objects#defining-an-action) Defining an action -------------------------------------------------------------------------------------------------------- ### [](https://learn.mechanic.dev/core/tasks/code/action-objects#type) Type The action **type** is always a string, having a value that corresponds to [a supported action](https://learn.mechanic.dev/core/actions) (e.g. `"shopify"`, or `"http"`). ### [](https://learn.mechanic.dev/core/tasks/code/action-objects#options) Options Action **options** vary by action type. Depending on the action type, its options may be another complete object, or an array, or a scalar value. ### [](https://learn.mechanic.dev/core/tasks/code/action-objects#meta) Meta Actions may optionally include **meta** information, annotating the action with any JSON value. This information could be purely for record-keeping, making it easy to determine why an action was rendered, or to add helpful context: Copy { "action": { "type": "shopify", "options": [\ "post",\ "/admin/customers/1234567890/send_invite.json",\ {}\ ], "meta": { "invite_reason": "alpha", "customer_email": "[email protected]" } } } Or, this information could be used to facilitate complex task flows, in concert with a subscription to mechanic/actions/perform (see [Responding to action results](https://learn.mechanic.dev/techniques/responding-to-action-results) ). An action's meta information can supply followup task runs with information about state, allowing the task to cycle between different phases of operation. Copy {% if event.topic contains "trigger" %} {% action %} { "type": "cache", "options": ["set", "foo", "bar"], "meta": { "mode": "first" } } {% endaction %} {% elsif action.meta.mode == "first" %} {% action %} { "type": "cache", "options": ["set", "foo", "bar"], "meta": { "mode": "second" } } {% endaction %} {% elsif action.meta.mode == "second" %} {% action "echo", "done" %} {% endif %} [PreviousEnvironment variables](https://learn.mechanic.dev/core/tasks/code/environment-variables) [NextError objects](https://learn.mechanic.dev/core/tasks/code/error-objects) Last updated 1 year ago Was this helpful? --- # Subscriptions | Mechanic A task **subscription** is the expression of a task's intent to receive certain [**events**](https://learn.mechanic.dev/core/events) , filtering by [**topic**](https://learn.mechanic.dev/core/events/topics) . A subscription consists of an event topic, optionally combined with a time **offset**, which creates a delay. A task may have any number of subscriptions.  [](https://learn.mechanic.dev/core/tasks/subscriptions#delays) Delays -------------------------------------------------------------------------- ... are accomplished using subscription offsets, as described below. This heading is here for folks searching for a way to delay their tasks. ;) [](https://learn.mechanic.dev/core/tasks/subscriptions#offsets) Offsets ---------------------------------------------------------------------------- A subscription offset (sometimes called a delay) defines the amount of time a task should wait or delay (!!) before responding to the incoming event. It's the easiest way to add a delay to a task's subscription to a specific topic. (For finer control over event timing, try using the `run_at` option of the [Event action](https://learn.mechanic.dev/core/actions/event) .) Subscription offsets are appended to the subscription topic, and are of the form "+1.hour". Offsets may be given using seconds, minutes, hours, days, weeks, months, or years. There is no limit to how large the subscription offset may be. **A subscription with an offset looks like** `**shopify/customers/create+1.hour**`**.** To learn more about scheduling work with Mechanic, see [Scheduling](https://learn.mechanic.dev/core/runs/scheduling) . In practice, large offsets can make debugging difficult! If you're thinking about work to be done weeks or months or years from now, consider running an hourly or daily task that scans for work that's due to be done, instead of scheduling tasks for the distant future. In some cases, the first task run on a new mechanic/scheduler/daily task may not be performed when expected. To illustrate: if a user creates a task at 9am Monday, subscribing to mechanic/scheduler/daily+10.hours, they will have to wait until _the following midnight_ before the mechanic/scheduler/daily event is created. When that event's run is performed, the task's subscription offset will be calculated and applied, and the task run will be enqueued for 10 hours later. This means that the task will run for the first time on 10am Tuesday, _not_ 10am Monday. The [Shopify variables](https://learn.mechanic.dev/core/tasks/code/environment-variables#shopify-variables) available to tasks always contain data drawn from the event itself. If a task has a offset event subscription, this data may be outdated by the time the task runs. To reload the data in a Shopify variable, use something like this: Copy {% unless event.preview %} {% assign customer = customer.reload %} {% endunless %} Remember, Mechanic does not permit access to the Shopify API during [event preview](https://learn.mechanic.dev/core/tasks/previews) . Using this `unless` statement ensures that reloading only happens during a live event. [](https://learn.mechanic.dev/core/tasks/subscriptions#using-liquid) Using Liquid -------------------------------------------------------------------------------------- A task's subscriptions are parsed for Liquid, at the time the task is saved. Combined with [**task options**](https://learn.mechanic.dev/core/tasks/options) , this is an opportunity to generate subscriptions based on user configuration, adding or removing subscriptions based on the user's choice, or adjusting subscription offset based on a user-entered value. One subscription is permitted per line. Blank lines and leading/trailing whitespace are permitted. ### [](https://learn.mechanic.dev/core/tasks/subscriptions#examples) Examples Conditional subscription Copy shopify/orders/create {% if options.send_email_when_order_cancelled__boolean %} shopify/orders/cancelled {% endif %} Dynamic offset Copy shopify/orders/paid+{{ options.days_to_wait_before_followup__number_required }}.days Optional offset Copy shopify/customers/create{% if options.wait_one_hour__boolean %}+1.hour{% endif %} [PreviousTasks](https://learn.mechanic.dev/core/tasks) [NextCode](https://learn.mechanic.dev/core/tasks/code) Last updated 10 months ago Was this helpful? --- # User Form | Mechanic When a task subscribes to the **mechanic/user/form** event topic a "Run task" button is added to the task. When the Run Task button is clicked the user is presented with a form that contains any [task options](https://learn.mechanic.dev/core/tasks/options) that have the `_userform` flag. When submitted, an event is generated, to which only this task will respond. The event contains the user's input in its data, making user's input available in `event.data.`  User form on Run Task page Click the link button beside the title of the form to copy a link to the form that you can share with your users ### [](https://learn.mechanic.dev/core/tasks/user-form#getting-the-users-input-in-code) Getting the user’s input in code During a `mechanic/user/form` event, the ad-hoc values arrive under event.data. Copy {% # These will appear on the run task user form %} {% assign big_event = options.the_big_event__date_userform %} {% assign color_for_big_event = options.color_for_big_event__color_required_userform %} {% # This will NOT appear on the run task user form %} {% assign level = options.level__select_o1_low_o2_high %} {% if event.topic == "mechanic/user/form" %} {% # we need to get the value from event data, we don't want the value from the task option %} {% assign big_event = event.data.the_big_event__date_userform %} {% assign color_for_big_event = event.data.color_for_big_event__color_required_userform %} {% endif %} {% action "echo" big_event, level, color_for_big_event %} Tip: `event.data` contains the values for the fields you exposed; fall back to `options.*` for everything else. [PreviousImport and export](https://learn.mechanic.dev/core/tasks/import-and-export) [NextActions](https://learn.mechanic.dev/core/actions) Last updated 6 months ago Was this helpful? --- # Responding to events | Mechanic Shopify uses webhooks to notify apps like Mechanic about new activity. Mechanic supports every type of Shopify webhook in its set of [**Shopify event topics**](https://learn.mechanic.dev/platform/events/topics#shopify) . By setting up [**subscriptions**](https://learn.mechanic.dev/core/tasks/subscriptions) to these topics, a task may respond to any supported type of Shopify activity. Note that Shopify does not strictly guarantee webhook delivery. See [Reconciling missing events](https://learn.mechanic.dev/core/shopify/events/reconciling-missing-events) for more on this subject. [](https://learn.mechanic.dev/core/shopify/events#responding-to-changes-in-specific-data) Responding to changes in specific data ------------------------------------------------------------------------------------------------------------------------------------- Shopify's "update" webhooks do not contain information about what piece of data has changed. (For example, a product update webhook does not specify what attribute of the product has changed.) For this reason, it's not possible to subscribe to changes in specific resource attributes (like product SKUs, or order tags). If a task needs to react to a specific attribute change, the task must scan for and "remember" the original value of that attribute, so as to compare incoming updates with that remembered value. A task could use the [Cache](https://learn.mechanic.dev/core/actions/cache) action to store these values in the Mechanic cache, or it could use the [Shopify](https://learn.mechanic.dev/core/actions/integrations/shopify) action to save the remembered value in a metafield. For an example implementation, see the [Auto-tag products when their variants change](https://usemechanic.com/task/auto-tag-products-when-their-skus-change) task. [](https://learn.mechanic.dev/core/shopify/events#undefined) ---------------------------------------------------------------- [PreviousReading and Writing to Shopify](https://learn.mechanic.dev/core/shopify) [NextReconciling missing events](https://learn.mechanic.dev/core/shopify/events/reconciling-missing-events) Last updated 4 years ago Was this helpful? --- # URL | Mechanic The **URL** file generator accepts a string as its options, containing a valid URL. This generator downloads the file at that URL, returning the results. Downloaded files may be a maximum of 20 megabytes, even when used within other file generators (like [ZIP](https://learn.mechanic.dev/core/actions/file-generators/zip) ). [](https://learn.mechanic.dev/core/actions/file-generators/url#options) Options ------------------------------------------------------------------------------------ This file generator accepts a string containing a valid HTTP or HTTPS URL. It does not support any other options. Copy { "url": URL } [](https://learn.mechanic.dev/core/actions/file-generators/url#example) Example ------------------------------------------------------------------------------------ Liquid Copy {% action "files" %} { "image_from_url.png": { "url": "https://www.google.com/images/branding/googlelogo/2x/googlelogo_color_272x92dp.png" } } {% endaction %} [PreviousPlaintext](https://learn.mechanic.dev/core/actions/file-generators/plaintext) [NextZIP](https://learn.mechanic.dev/core/actions/file-generators/zip) Last updated 22 days ago Was this helpful? --- # Triggering tasks from a contact form | Mechanic 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 [Mechanic's app store page](https://apps.shopify.com/mechanic?ref=lightward) ) * A basic knowledge of Liquid ([need a refresher?](https://learn.mechanic.dev/platform/liquid/basics) ) * A basic knowledge of JavaScript ([need a refresher?](https://www.w3schools.com/js/default.asp) ) [](https://learn.mechanic.dev/resources/tutorials/triggering-tasks-from-a-contact-form#the-situation) 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! [](https://learn.mechanic.dev/resources/tutorials/triggering-tasks-from-a-contact-form#the-plan) 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 [webhook](https://learn.mechanic.dev/platform/webhooks) 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 [email](https://learn.mechanic.dev/core/actions/email) to our CRM system, containing the CSV file as an attachment [](https://learn.mechanic.dev/resources/tutorials/triggering-tasks-from-a-contact-form#the-mechanic-task) The Mechanic task -------------------------------------------------------------------------------------------------------------------------------- Time to build the task! Out of Mechanic's entire toolkit, here's what we'll use: * [Online storefront JavaScript](https://learn.mechanic.dev/core/tasks/advanced-settings/javascript) * [Mechanic webhooks](https://learn.mechanic.dev/platform/webhooks) * [The csv Liquid filter](https://learn.mechanic.dev/platform/liquid/filters#csv) * [The Email action](https://learn.mechanic.dev/core/actions/email) ### [](https://learn.mechanic.dev/resources/tutorials/triggering-tasks-from-a-contact-form#step-1-create-a-webhook-and-connect-it-to-a-new-blank-task) Step 1: Create a webhook, and connect it to a new blank task Start with the [Creating a Mechanic webhook](https://learn.mechanic.dev/resources/tutorials/creating-a-mechanic-webhook) 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". ### [](https://learn.mechanic.dev/resources/tutorials/triggering-tasks-from-a-contact-form#step-2-wire-up-the-shop-frontend-to-send-form-data-to-our-webhook) 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. 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 [JavaScript](https://learn.mechanic.dev/core/tasks/advanced-settings/javascript) feature to have our code automatically loaded into the online storefront. (Under the hood, Mechanic leverages Shopify's [ScriptTag](https://shopify.dev/docs/admin-api/rest/reference/online-store/scripttag) API.) For this tutorial, I created a development store and installed the [Debut theme](https://themes.shopify.com/themes/debut/styles/default) . 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.  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!  Here, we use Chrome's developer tools to verify the form's ID attribute. Next, we're going to write some JavaScript that listens for the`submit` 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! JavaScript for online storefront Copy // 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! ### [](https://learn.mechanic.dev/resources/tutorials/triggering-tasks-from-a-contact-form#step-4-receive-our-form-submission-on-the-mechanic-side-convert-it-to-a-csv-and-send-it-as-an-email) 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.)  We've got the Chrome developer tools open so we can see our console.log messages. 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. Moving back to the task editor, the first step is to extract this data, and assemble it into something we can format using the [csv](https://learn.mechanic.dev/platform/liquid/filters#csv) 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 [Email](https://learn.mechanic.dev/core/actions/email) 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. Task code Copy {% 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 %} When writing a task, it's important to think about [previews](https://learn.mechanic.dev/core/tasks/previews) , 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 [Previews](https://learn.mechanic.dev/core/tasks/previews) . Here's how we'll configure the task, using the task option fields that automatically appear based on our task code:  ### [](https://learn.mechanic.dev/resources/tutorials/triggering-tasks-from-a-contact-form#step-5-testing) 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.  [](https://learn.mechanic.dev/resources/tutorials/triggering-tasks-from-a-contact-form#the-end) 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. 🎉 Thanks for reading! If you've got questions or suggestions, join the [Mechanic Slack workspace](https://join.slack.com/t/usemechanic/shared_invite/zt-cq84nrs7-ggYbYTbf~CrCjTg8nmHP2A) . :) ### [](https://learn.mechanic.dev/resources/tutorials/triggering-tasks-from-a-contact-form#import-the-final-task) 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: Copy {"name":"Receive contact form for CRM","options":{"recipient_email_address__email_required":"[email protected]","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"} [PreviousPracticing writing tasks](https://learn.mechanic.dev/resources/tutorials/practicing-writing-tasks) [NextCreating scheduled CSV feeds](https://learn.mechanic.dev/resources/tutorials/creating-scheduled-csv-feeds) Last updated 9 months ago Was this helpful? --- # Flow | Mechanic The **Flow** action sends data to Shopify Flow, arriving as one of four possible Flow triggers. This page is about the Mechanic action that sends data to Shopify Flow. For a review of Mechanic's entire integration with Flow, see [Shopify Flow](https://learn.mechanic.dev/platform/integrations/shopify-flow) . [](https://learn.mechanic.dev/core/actions/integrations/flow#options) Options ---------------------------------------------------------------------------------- ### [](https://learn.mechanic.dev/core/actions/integrations/flow#resource-options) Resource options The Flow action accepts at most one resource option, identifying a specific Shopify resource, and resulting in a resource-specific Flow trigger. If no resource option is provided, Mechanic will use the General trigger. These resource options only accept fully-numeric resource IDs (i.e. 12345). They do not accept global IDs (i.e. gid://shopify/Customer/12345). Resource option Flow trigger `customer_id` "Mechanic sent customer data" `product_id` "Mechanic sent product data" `order_id` "Mechanic sent order data" (when no resource option is given) "Mechanic sent general data" ### [](https://learn.mechanic.dev/core/actions/integrations/flow#data-options) Data options This action also sends user-defined data, with one option available for each of Flow's supported datatypes. These options are _always_ sent to Flow, even if they're omitted from the action definition; when omitted, their values are set to the documented default. Option Type Default `user_boolean` Boolean `false` `user_email` Email address `"[[email protected]](https://learn.mechanic.dev/cdn-cgi/l/email-protection) "` `user_number` Number `0` `user_string` String `""` `user_url` URL `"https://mechanic.invalid/"` [](https://learn.mechanic.dev/core/actions/integrations/flow#usage) Usage ------------------------------------------------------------------------------ For a detailed review of usage, see [Shopify Flow](https://learn.mechanic.dev/platform/integrations/shopify-flow#mechanic-flow) . [PreviousAirtable](https://learn.mechanic.dev/core/actions/integrations/airtable) [NextGoogle](https://learn.mechanic.dev/core/actions/integrations/google) Last updated 11 months ago Was this helpful? --- # PDF | Mechanic The **PDF** file generator accepts an object containing an HTML string, and uses [Pdfcrowd](https://pdfcrowd.com/) to render it as a PDF document. Pdfcrowd employs the [Chromium Embedded Framework](https://en.wikipedia.org/wiki/Chromium_Embedded_Framework) for HTML rendering, which uses the same foundation as Google Chrome. This allows Mechanic to generate PDFs with modern CSS and JavaScript features, including chart libraries and web fonts. [](https://learn.mechanic.dev/core/actions/file-generators/pdf#options) Options ------------------------------------------------------------------------------------ Option Description `html` Required; a string containing the HTML, CSS and JavaScript to be rendered ... Additional Pdfcrowd API options supported; see below Copy { "pdf": { "html": HTML, ... } } [](https://learn.mechanic.dev/core/actions/file-generators/pdf#pdfcrowd-options) Pdfcrowd options ------------------------------------------------------------------------------------------------------ The PDF generator supports all rendering-related options of the Pdfcrowd API, using version 20.10. For a complete list of options, see [https://pdfcrowd.com/doc/api/html-to-pdf/http/](https://pdfcrowd.com/doc/api/html-to-pdf/http/) . ### [](https://learn.mechanic.dev/core/actions/file-generators/pdf#debugging) Debugging If it's unclear why something isn't rendering properly, start by testing the HTML being used in a Pdfcrowd playground, at [https://pdfcrowd.com/playground/html-to-pdf](https://pdfcrowd.com/playground/html-to-pdf) . If the issue is reproducible in the playground, use the "Help" button along the left-hand sidebar to get the ID of your specific playground, and instructions for contacting Pdfcrowd support with the details of your test. A screencast illustrating an HTML test, and a path for reaching Pdfcrowd support [](https://learn.mechanic.dev/core/actions/file-generators/pdf#example) Example ------------------------------------------------------------------------------------ Liquid Copy {% capture html %}Almost before we knew it, we had left the ground.
{% endcapture %} {% action "files" %} { "file.pdf": { "pdf": { "html": {{ html | json }}, "page_width": "7in", "page_height": "5in", "margin_top": "10mm", "margin_right": "10mm", "margin_bottom": "10mm", "margin_left": "10mm" } } } {% endaction %} [PreviousBase64](https://learn.mechanic.dev/core/actions/file-generators/base64) [NextPlaintext](https://learn.mechanic.dev/core/actions/file-generators/plaintext) Last updated 22 days ago Was this helpful? --- # Slack | Mechanic The Slack action allows Mechanic to post messages to public and private channels in your Slack instance (as the Mechanic app bot or a customer username). The Slack action provides a wrapper around HTTP calls to the [Slack Web API](https://docs.slack.dev/apis/web-api/) . The various Slack API methods supported by this integration all share the same top-level structure in the action options. [](https://learn.mechanic.dev/core/actions/integrations/slack#options) Options ----------------------------------------------------------------------------------- Option Type Description account string Required: the Slack account to use. Must match one of the Slack accounts linked in the Mechanic authentication settings. method string Required: the HTTP verb as required by the Slack API for the specific method (e.g. "GET", "POST") url\_path string Required: the Slack API method (e.g. "/chat.postMessage") headers hash Required: "Content-Type": "application/json" body hash Required: the object that contains a JSON representation of the properties and content of the message ### [](https://learn.mechanic.dev/core/actions/integrations/slack#supported-api-methods) Supported API Methods Currently, these are the only Slack API methods supported by this integration. * [chat.postMessage](https://docs.slack.dev/reference/methods/chat.postmessage) * [chat.update](https://docs.slack.dev/reference/methods/chat.update/) * [chat.delete](https://docs.slack.dev/reference/methods/chat.delete/) [](https://learn.mechanic.dev/core/actions/integrations/slack#authentication) Authentication ------------------------------------------------------------------------------------------------- This action requires installing the Mechanic Slack app in your Slack account with the appropriate permissions. To install the app: 1. Go to the Settings screen 2. Click Authentication 3. Install the Mechanic Slack app from the Slack tab [](https://learn.mechanic.dev/core/actions/integrations/slack#examples) Examples ------------------------------------------------------------------------------------- ### [](https://learn.mechanic.dev/core/actions/integrations/slack#post-a-simple-message) Post a Simple Message Copy {% action "slack" %} { "account": "SLACK_ACCOUNT_NAME", "method": "POST", "url_path": "/chat.postMessage", "headers": { "Content-Type": "application/json" }, "body": { "channel": "CHANNEL_ID", "text": "Slack Example Message", "blocks": [\ {\ "type": "section",\ "text": {\ "type": "mrkdwn",\ "text": "Lorem ipsum dolor sit amet consectetur adipiscing elit."\ }\ }\ ] } } {% endaction %} ### [](https://learn.mechanic.dev/core/actions/integrations/slack#post-a-message-with-custom-username-and-icon) Post a Message with Custom Username and Icon Copy {% action "slack" %} { "account": "SLACK_ACCOUNT_NAME", "method": "POST", "url_path": "/chat.postMessage", "headers": { "Content-Type": "application/json" }, "body": { "username": "AiRobot", "icon_emoji": ":robot_face:", "channel": "CHANNEL_ID", "text": "Slack Example Message", "blocks": [\ {\ "type": "section",\ "text": {\ "type": "mrkdwn",\ "text": "Lorem ipsum dolor sit amet consectetur adipiscing elit."\ }\ }\ ] } } {% endaction %} [](https://learn.mechanic.dev/core/actions/integrations/slack#action-response) Action Response --------------------------------------------------------------------------------------------------- The body of the action response will vary based on which Slack API method is called. Generally, the response is an object with the following structure (most fields removed for brevity). Running a Slack action task and reviewing the response is often the best way to see what will be returned in the body. Copy { "type": "slack", "run": { "ok": true, "result": { "status": 200, "body": { ... } } } } [PreviousShopify](https://learn.mechanic.dev/core/actions/integrations/shopify) [NextFile generators](https://learn.mechanic.dev/core/actions/file-generators) Last updated 5 days ago Was this helpful? --- # Runs | Mechanic [Events](https://learn.mechanic.dev/core/events) , [tasks](https://learn.mechanic.dev/core/tasks) , and [actions](https://learn.mechanic.dev/core/actions) are all processed using queues, in which a piece of work is enqueued, and performed in its turn. Each piece of work is called a **run**. Thus, Mechanic performs work using event runs, task runs, and action runs. When performed, a run has a **result**. Depending on the type of run, this result may define additional runs to be performed after it concludes. * **Event runs**, when performed, may result in a set of enqueued task runs. * **Task runs**, when performed, may result in a set of enqueued action runs. * **Action runs**, when performed, have behaviors that vary by [action type](https://learn.mechanic.dev/core/actions#action-types) . * If the originating task [subscribes to mechanic/actions/perform](https://learn.mechanic.dev/techniques/responding-to-action-results) , each action run will spawn a new event containing that action's results. This new event will be processed in an enqueued event run, creating an opportunity for the task to respond to the action's results. Most runs are scheduled to be performed immediately. Some runs may be [scheduled](https://learn.mechanic.dev/core/runs/scheduling) for the future. Some runs may be [retried](https://learn.mechanic.dev/core/runs/retries) , once performed. At the moment a run is performed, it loads in all related data (which may include the related store, or the related event, or the related task). [](https://learn.mechanic.dev/core/runs#run-flow) Run flow --------------------------------------------------------------- A normal flow in Mechanic looks like this: 1. An event is created – possibly by a [Shopify webhook](https://learn.mechanic.dev/platform/webhooks) , or by a [user webhook](https://learn.mechanic.dev/platform/webhooks) , by the [Mechanic scheduler](https://learn.mechanic.dev/platform/events/topics#scheduler) , or by an [Event action](https://learn.mechanic.dev/core/actions/event) . 2. An event run is created, and performed. During this phase, Mechanic scans the store's tasks to see which ones are relevant for the current event, by checking the subscriptions on file for each task. For each task that Mechanic discovers for the event, a task run is created. (If the task subscription involved an [offset](https://learn.mechanic.dev/core/tasks/subscriptions#offsets) , as in mechanic/scheduler/daily+2.hours, the task run will be set to wait for that amount of time.) The result of the event run is this set of task runs. 3. Each task run is performed. During this phase, Mechanic takes each task's [Liquid code](https://learn.mechanic.dev/core/tasks/code) , and renders it using the associated event. The result of the task run is the set of JSON [action objects](https://learn.mechanic.dev/core/tasks/code/action-objects) rendered by the task's Liquid code. Each action object is used to create an action run. 4. Each action run is performed. During this phase, Mechanic executes each action, given the options that were provided for it by the task run's result. **Understanding this sequence of events is important.** Task runs do not come into existence until the event run has been performed, and action runs are only performed after their task run has fully concluded. Critically, this means that tasks do not have direct access to the effects of the actions they generate. Actions are performed later in the sequence, and their effects will only be seen by subsequent task runs. [](https://learn.mechanic.dev/core/runs#run-priorities) Run priorities --------------------------------------------------------------------------- In general, given a mix of event, task, and action runs that are all due, Mechanic will perform due action runs first, then due task runs, and finally due event runs. If Shopify's rate limit for _either_ the GraphQL or REST Admin API has been reached, Mechanic will skip over task runs and over [Shopify action](https://learn.mechanic.dev/core/actions/integrations/shopify) runs, until _both_ rate limits have been recovered. In these cases, Mechanic may choose to perform due runs of a lower priority, while it waits for the Shopify API rate limits to recover sufficiently to perform the higher priority runs. [](https://learn.mechanic.dev/core/runs#run-states) Run states ------------------------------------------------------------------- Unscheduled The run has not been assigned a time to be performed Scheduled Scheduled to be performed, but that time has not yet arrived Due The run is ready to be performed, and is waiting for a runner Started The run is being performed Failed The run has been performed, and an error has been recorded Succeeded The run has been performed, without errors [PreviousZIP](https://learn.mechanic.dev/core/actions/file-generators/zip) [NextScheduling](https://learn.mechanic.dev/core/runs/scheduling) Last updated 1 year ago Was this helpful? --- # Integrations | Mechanic These actions allow your Mechanic tasks to speak to other app and systems :) Use Mechanic to integrate your Shopify store with other apps like Airtable, Google, Slack, and more. [PreviousHTTP](https://learn.mechanic.dev/core/actions/http) [NextAirtable](https://learn.mechanic.dev/core/actions/integrations/airtable) Last updated 5 days ago Was this helpful? --- # Stub data | Mechanic **Stub data** is hard-coded into a task, providing an unchanging source of data for [**previews**](https://learn.mechanic.dev/core/tasks/previews) . It is an important tool when generating [**dynamic preview actions**](https://learn.mechanic.dev/core/tasks/previews#dynamic-preview-actions) . Stub data may be used for user-defined variables, but may also override [**environment variables**](https://learn.mechanic.dev/core/tasks/code/environment-variables) as needed. For controlling preview event data (i.e. the values in `event.data`, and values found in [event subject variables](https://learn.mechanic.dev/core/tasks/code/environment-variables#event-subject-variables) ), use [**defined preview events**](https://learn.mechanic.dev/core/tasks/previews/events) to cleanly specify these values _outside_ of the task code. [](https://learn.mechanic.dev/core/tasks/previews/stub-data#stubbing-liquid-variables) Stubbing Liquid variables --------------------------------------------------------------------------------------------------------------------- Most tasks make decisions based on the [Liquid variables](https://learn.mechanic.dev/core/tasks/code/environment-variables) automatically provided, making it a common practice to stub them during preview mode. Any and all Liquid variables may be replaced by stub data, including `event` and any [event subject variables](https://learn.mechanic.dev/core/tasks/code/environment-variables#event-subject-variables) . In simple cases, replacement objects may be constructed using the [assign](https://learn.mechanic.dev/platform/liquid/tags/assign) tag. The stub data in the following examples include an ID for the order, so as to generate a realistic tagsAdd mutation during preview mode. Realistic preview actions are important for users and developers, but there's a functional importance for tagsAdd mutations in particular: in preview mode, Mechanic looks at the `id` argument in order to determine what kind of resource will be tagged, in order to determine what permissions this particular mutation requires. If you generate tagsAdd mutations during preview, make sure to use realistic ID values! Copy {% if event.preview %} {% assign order = hash %} {% assign order["source_name"] = "web" %} {% assign order["admin_graphql_api_id"] = "gid://shopify/Order/1234567890" %} {% endif %} {% if order.source_name == "web" %} {% action "shopify" %} mutation { tagsAdd(id: {{ order.admin_graphql_api_id | json }}, tags: "web") { userErrors { field, message } } } {% endaction %} {% endif %} It's also possible to construct this data using [parse\_json](https://learn.mechanic.dev/platform/liquid/filters#json-parse_json-parse_jsonl) . Copy {% if event.preview %} {% capture order_json %} { "source_name": "web", "admin_graphql_api_id": "gid://shopify/Order/1234567890" } {% endcapture %} {% assign order = order_json | parse_json %} {% endif %} {% if order.source_name == "web" %} {% action "shopify" %} mutation { tagsAdd(id: {{ order.admin_graphql_api_id | json }}, tags: "web") { userErrors { field, message } } } {% endaction %} {% endif %} [](https://learn.mechanic.dev/core/tasks/previews/stub-data#stubbing-graphql-data) Stubbing GraphQL data ------------------------------------------------------------------------------------------------------------- Mechanic makes GraphQL data available to tasks via the [shopify](https://learn.mechanic.dev/platform/liquid/filters#shopify) filter. Mechanic observes the shopify filter in action during preview mode, using its inputs to inform Mechanic's knowledge of what permissions the task needs. For this reason, it's important to allow the shopify filter to run normally, and construct stub data afterwards. It can be useful to specify stub data using JSON, fed through the [parse\_json](https://learn.mechanic.dev/platform/liquid/filters#json-parse_json-parse_jsonl) filter. Sample JSON is easy to generate using [Shopify's GraphiQL app](https://shopify-graphiql-app.shopifycloud.com/) . GraphQL with stub data GraphQL pagination with stub data Copy {% capture query %} query { publications(first: 250) { edges { node { id name } } } } {% endcapture %} {% assign result = query | shopify %} {% if event.preview %} {% capture result_json %} { "data": { "publications": { "edges": [\ {\ "node": {\ "id": "gid://shopify/Publication/69217648807",\ "name": "Online Store"\ }\ }\ ] } } } {% endcapture %} {% assign result = result_json | parse_json %} {% endif %} {% log available_publications: result.data.publications %} Copy {% assign cursor = nil %} {% assign total_inventory = 0 %} {% for n in (0..100) %} {% capture query %} query { orders( first: 250 query: "status:open" after: {{ cursor | json }} ) { pageInfo { hasNextPage } edges { node { name, email } } } } {% endcapture %} {% assign result = query | shopify %} {% if event.preview %} {% capture result_json %} { "data": { "orders": { "pageInfo": { "hasNextPage": false }, "edges": [\ {\ "node": {\ "name": "#1135",\ "email": "[email protected]"\ }\ }\ ] } } } {% endcapture %} {% assign result = result_json | parse_json %} {% endif %} {% for order_edge in result.data.orders.edges %} {% assign order_node = order_edge.node %} {% if order_node.email == blank %} {% continue %} {% endif %} {% action "email" %} { "to": {{ order_node.email | json }}, "subject": {{ "We're still working on " | append: order_node.name | json }}, "body": "Thanks for your patience!" } {% endaction %} {% endfor %} {% if result.data.orders.pageInfo.hasNextPage %} {% assign cursor = result.data.orders.edges.last.cursor %} {% else %} {% break %} {% endif %} {% endfor %} [PreviousDefining preview events](https://learn.mechanic.dev/core/tasks/previews/events) [NextShopify API version](https://learn.mechanic.dev/core/tasks/shopify-api-version) Last updated 3 years ago Was this helpful? --- # Report Toaster | Mechanic Report Toaster is a reporting and analytics app, which offers an integration with Mechanic. Use the Report Toaster action to perform operations with their service. [](https://learn.mechanic.dev/core/actions/integrations/report-toaster#documentation) Documentation -------------------------------------------------------------------------------------------------------- Find a complete reference for this action in the Integrations section: → [Platform / Integrations / Report Toaster / Action](https://learn.mechanic.dev/platform/integrations/report-toaster#action) [PreviousGoogle Sheets](https://learn.mechanic.dev/core/actions/integrations/google-sheets) [NextShopify](https://learn.mechanic.dev/core/actions/integrations/shopify) Last updated 22 days ago Was this helpful? --- # Base64 | Mechanic The **Base64** file generator accepts a base64-encoded string, and returns a file containing the decoded value. This generator is useful when producing images, or other binary content that cannot be represented with a JSON string. [](https://learn.mechanic.dev/core/actions/file-generators/base64#options) Options --------------------------------------------------------------------------------------- This file generator accepts a base64-encoded string. It does not support any other options. Copy { "base64": BASE64_ENCODED_VALUE } [](https://learn.mechanic.dev/core/actions/file-generators/base64#example) Example --------------------------------------------------------------------------------------- Liquid Copy {% action "files" %} { "image_from_base64.jpg": { "base64": "iVBORw0KGgoAAAANSUhEUgAAAC8AAAAuCAIAAAA3GddeAAAAAXNSR0IArs4c6QAAAMZlWElmTU0AKgAAAAgABgESAAMAAAABAAEAAAEaAAUAAAABAAAAVgEbAAUAAAABAAAAXgEoAAMAAAABAAIAAAExAAIAAAAVAAAAZodpAAQAAAABAAAAfAAAAAAAAABIAAAAAQAAAEgAAAABUGl4ZWxtYXRvciBQcm8gMi4wLjUAAAAEkAQAAgAAABQAAACyoAEAAwAAAAEAAQAAoAIABAAAAAEAAAAvoAMABAAAAAEAAAAuAAAAADIwMjE6MDI6MTMgMDA6MjQ6MjMAdW0xkQAAAAlwSFlzAAALEwAACxMBAJqcGAAAA6ZpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IlhNUCBDb3JlIDYuMC4wIj4KICAgPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4KICAgICAgPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIKICAgICAgICAgICAgeG1sbnM6dGlmZj0iaHR0cDovL25zLmFkb2JlLmNvbS90aWZmLzEuMC8iCiAgICAgICAgICAgIHhtbG5zOmV4aWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20vZXhpZi8xLjAvIgogICAgICAgICAgICB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iPgogICAgICAgICA8dGlmZjpZUmVzb2x1dGlvbj43MjAwMDAvMTAwMDA8L3RpZmY6WVJlc29sdXRpb24+CiAgICAgICAgIDx0aWZmOlhSZXNvbHV0aW9uPjcyMDAwMC8xMDAwMDwvdGlmZjpYUmVzb2x1dGlvbj4KICAgICAgICAgPHRpZmY6UmVzb2x1dGlvblVuaXQ+MjwvdGlmZjpSZXNvbHV0aW9uVW5pdD4KICAgICAgICAgPHRpZmY6T3JpZW50YXRpb24+MTwvdGlmZjpPcmllbnRhdGlvbj4KICAgICAgICAgPGV4aWY6UGl4ZWxZRGltZW5zaW9uPjQ2PC9leGlmOlBpeGVsWURpbWVuc2lvbj4KICAgICAgICAgPGV4aWY6UGl4ZWxYRGltZW5zaW9uPjQ3PC9leGlmOlBpeGVsWERpbWVuc2lvbj4KICAgICAgICAgPHhtcDpNZXRhZGF0YURhdGU+MjAyMS0wMi0xM1QwMDoyNTozMlo8L3htcDpNZXRhZGF0YURhdGU+CiAgICAgICAgIDx4bXA6Q3JlYXRlRGF0ZT4yMDIxLTAyLTEzVDAwOjI0OjIzWjwveG1wOkNyZWF0ZURhdGU+CiAgICAgICAgIDx4bXA6Q3JlYXRvclRvb2w+UGl4ZWxtYXRvciBQcm8gMi4wLjU8L3htcDpDcmVhdG9yVG9vbD4KICAgICAgPC9yZGY6RGVzY3JpcHRpb24+CiAgIDwvcmRmOlJERj4KPC94OnhtcG1ldGE+Ci1W4c0AAA3XSURBVFgJvZgLlBTVmcfvraquqn5Uv3t63swwvAaQhyBCFEUhWSIgKJrVaBLMakjMaszRzWp213PUPScn8WCOiWZfybpn17hZ4yMcgkogQRAIEeMwwwwzTGaaGaZ7evpR1V3vd929zfCKYCKIqalTdaf6Vt1f/b/vft93CyKEwF92mxwRD4v/IIB4eIgbkIAQUH8xEtd1MYGoqNnx0gQviZolmZ6DSB9FMK7d2V63YE47/KS1QZ7nep6uKAd+e3DCIL26GQbB8aIhqYaHgJ9lY9GQI0t7tr7x2H03foLa4PfEevDlcm/vEcmwIqnGkCTnxNKITY0Jmmu7sbA/GAgwrE8uGSqk9nRnPikarIdUrRw90qs5Xrx5mqdbuUIJ09GVYVvIOOwsKhCLxsLJOEN5riRJuod4zbn8NFgS27KO9fYUe/rS+SIcHFaFSiQZLyxamudCFEnNTaNGd7wST6Xr2VSMtDWEIFIcD3vwZaaxXUcoFo+/s4/evoPv7n61UBzUdSBW7qfoldnRHWs2GO2t8UikI0CpThbE6Za6eEkhVcvBTp0IMZeTxrSszOiIuWMn8drWN3304IpV42I1mxk+NpLpEip7+/oWRaK9M/8mEg1Hg35WUxPW0RTRyXuxYlVRda0xGbxsNLqm9Q0PE4d+X319667m1lLHjInenpH+o6VigZIlwTGPREI3TORiJJmMheMB1vDBplhjodDff2JKviS6ut6WDl8eGlVRuvuOQt20f7F1pz+UiSXHDuwfGTpWEQRHV13bhJ4bmDOb7JwRi3FxLhhnGH8sAiBhT3BH3n6jUCDrOaKzrZH4+NFPqlb7eo9QiQbY1d2Tyx1FoLe7+w/H+gWet3XVcy3kua0U3UmRTnNLvC4VphkCeLph5Eu57EQpaefc0uDSzob6VPTj0miqMtDfJxpOvijoY2OQZU9MFMZOjEhi1TFU5Fg49gUI4t5INH3wXTkQCkXCELi6rZXF0nBmZGBwCJKgzs6sWjwdR+qPZSnL8Y4ODEyUymRiytDo2NyqNK0qRqqyqaqWrgLHwpG+EYKNtP8BwzCvW06uWkGSSFWrkiwXiuXR0Wy5zOdGs/UR6qZrr/hYeUoXxYFMNqMiQAbzx4+XNOuIR6xy0bcp8JJnHsa+AlAHQHcQ5AbbUzo7+Ucf4RpSmlQRxaogVPKFMt4qvFAu8UuWX5+IRy893khDQ++/+tpxLtlx35e7fn2kwGcJCvQ3T/XaJm4sZ//eVIuKGEZgBkVRyVRh8eLKI98ML5hriIJYxZtU4rF/YxK+zAs47N1960aIlbmE6OeaZvW9Q13PPNNblNgnfvh+n5FRQjpRRxaPEIFg//KVQu54c6ouJlfTPrqnvh7deAO3dnUkHDL5iWqlIklqpSoKlSqPWUp8qVS+/dbbprZNmZxMH+o3nuPYqkoFgwRJ4q4eTsWGbubGlXf2Dv/ouf5sceiuh5vDU0d6h0tVRTJCpBFtI/XW5nq6s8MNrHODrJmIhNPJZMhPq5JaHMcGEiVFFJUaDTZQGaMIsVjyjttOCfOh2iDblrZtBcf+QKaSyO93cU1kGk6hwL+9Rzq4f8xxD0xbEpz9qbHhMTxFFdMyEUmEp+fN0TpDb6GTiXAwGeNi4WDAs5GgiKokYQpZFWUFE9VsVBNGkCTl0Ye/GgoFJ4X5UBplz2/Q4/8YE0Sg6bptYWWok3WaAdAJ5B1iwtmORVMJ/9jICUkzHRzFcMUU4FT/zLwxOs3hKY8lkd8HbEvHEUCWFTyHNFnRREmqVKqlmoH4iWJp+bJrVq5YTsCzUeYCljL27hGffLK5yANNBbbtdxw/AiYAPIRjPjIDicP+lB5Ojxd5QVJcQOLqjSZJhkTRWBL6EhZ812ceZrzZrhlTagQqTkKKakiSjG2EVTlpIz4WS9x7z5cgcRblAtpYPYetpx4PZjLAVIFrI89xCWAhoABQgrV9GNKj/gTnkkVR9jxEUaSPJFmaigbZdMiXrquzyGCI3toaPFpUm3keSLKjGmbNTBJGqdmoXBZwDfHA5q80NTZMTqULW8oaHXG2fAceHySwFqQDKBej4BmIaUQEBIB4hAaooM0lbToIMApB0D4ywPgiQbYxxtUnw+EIhdz67tJVSHzNB3mp2s5LHpZGwR5TxfOoUuYrmqrfe8+ma69ZSvyxMH+kDVJU+4XnggN785yb9tvAjxDOMA5wbKC6QHCA6IBxjxz2J5lUCxWJkyRGoTiWiYcD9YlQSyrMhJjA2LAiFI8psE/pmEJ0UaRrgYgoy9hhxarE45ktSretv+X2Detx2XVGkjON05cQ0v5jS/D4y9Z0M6whAq8uNOSZwDWBaQLBqO2SDoeNoBhpapoyLRTFVRwZDvpTUa4uxiWiIQvo9PZXPv3icwc75v520aqiy3YrnSjf3cplfHQQuzCOwbblPPT1+29Zt5Y8GTXOQJxpnKIx3tnNFF4BHZ5XdVkHL3RspLuugVMtQC5w8FEFRhllDIaJ1SebGiKRIDYQViXOBTg/qUoF+o3/Xbn9pQlA74635DXLNGQ82xSvrjw61MBUAcAFKnzwa19dv+azH4aCmWo0niSj/d+nZslAcykWkYgENgAGIDCNRStNn07NWcEoRqlrzNqVSTWlW5vSYY4LsTTHUAQ0J04MCr/55eZfvSr7ueev/EwPV+9VRRunSdO0sH1Bsjp+bE57cvOmuzes+SxOFWeUOL9R+03b9XKg/RBgAfLbRADTUcBioekh09WpBem7nvUlGk/s/52Y743NZJMds1obkgwBKeCYSik/1Jvfte3a6jg3fdb3mJYDbJyoVBzLsm3HclzTsnVNX71s2YNfvPnqRQv/NAomoayREUp/lrjSBDqEtgstAEwPKBTQ/cCizI7HuPjUf3vhv7f//KcIEnmTnb9iTcJPKVW+WBzPHT4o7Hlzjiqsvf66f3VjOyouEEXoeXgaqh4yTJuzlIduX/WljX81vb3lfCXOv0KZwzsCy2wvuAR5cUcPWLLgZ2wIcYB15fyi9JI1g8cGfvLcD13OVwVJcbT/dztfZ1auLo70Fw7uM4+8m66Ub0nGy9nyzyAlewRlOQ4kZBexmrJam/jCt75+w603RSPh8we+4BXKv+AzZAyjhCBkfB5F2CZBupBEgEBcS52P9hGF3SuWWz/uvTLKmKEw+/7ebWJuqFES6KGjrGlcTdPNJPXUhJwNx0IEiR2eNfV5Kv95MTv/a5sWbryJDn9UFMxHUYl2fJrMFbgoJHxnodVcF1n+wdT52XsaWrqeVrMZbfE1n5L5UveuX6U0g3LsBkBd5wv0+rhtLt1Ynmgx5FlGdYktX28b8IpZ9bdvvCiUGs3Zwc9pWZrK7342MnOInmlRkaZOLrDlQbwyCemgQzDkDtOIOnZzhLxlKRNhfW/9WprvVJe5+ixkT0dOGnkNCOTWr2dmzDjnkR+peSEaz60e+kk8+RIz9UbgxoDNUMhZOAfGYs6L/7Lv/e17E6aTIoi1Abh4Stuhtqktv3xzletOIVEE4HlZ2yskwc2bxwbPlgofieV8bVzHKRzelRSfp+e3ARQCLgeAD6A8pOOUKmp794WFSh1FfG4eMXdaM3/vMy3ZPMlsa9ABU+sHav6GQ0TA749GP5ARPwrQ6YSOkCzkygNv4Szp9v8D3VwGTCtuApgAMALo4Pig+j9/+wv5PWF2ANy1iZ13TyN66KnUwpVjo+N1QRjApTrAfn/yCAFB4ix0IdX/HNGpe3AuIA1e79+Xyww3hLtRFC88NODSgJABVaerjQd+/PPht8fqI8TabycW33wVTG2pwLp//8536372n7OkmiCTqhD40xke0rZcyzpnPvw5itO/n3kDSPhTnr+lgX+MaPaAPwk8XEGwgOpAIJjZe2j/j/bXp8i7n5/afn2bJP9TLmPtfPq+pX37p5/ga7EJnqLBj8VfPsKa7pRKp4e4iPMpGvxCyLIYc5ReSKHo1TXFIQdgDNCWMmHu/O5/+UP2ff93VXpeR2n45oaF88HYM5vsN4K4IsZZFZvGq0lychECsNWk2XOYjmkXQXG662m/gdC1Hd/4bsBGYCAOXPz4IKChbRx875UXdC33wK7FjQupyuC8his24CDHhWCi3gIsgj5AYpqTKFiVEAIT626mXvwpvWDB6SEu4nxKG1uTlC231SV6HQGHYhqm5wCS8ign18Vne3s2vzw7wkqV7rXxhZsphsHFIGRjAK9tWJf0gcn6EH9pxQlX/Nxfx554kpk+/ZRQF0FS63qKhvTRxrQkcfUUTx2HrU3AqABhyPbVa4q9/omE3y0U37szvfpxAhegeO4AGnDYySBigIlTOcT/1x5kfeOhwMOP+BobLw3lLA0uUWmkAZpEWcsb+T0CjN1TpqmB2XPicjcoiBsjq/5uEqX2CnijA26AgqzDsICjMJPf/f4PQnfcQeCIN+k+k90u8nhSG4SKPfsb5C47F3D7PVCRfC1Bs0yPg9W+5htCM69tbJhJ0DjGnto8XLnrRQoi6Ac+PyhNabW/9c+Nn78TJ73TXS7xXLu/ViQaJbgcUjBAzDBhiCoOrYt85emoIfJHX0+3zvvg63qGp+QpktCCAeGmO8ObH022teN11SUinHPbpDZetfewRdxKzFhNpVTIcPEvrqMCIZ+TYOMPfBAF03uWLYuyNcu6/3tN16zAHJeQBM5hONusfdlHjlM++FZ8yUrSd9ocf9L2nmtq+UGko9A0/AmoFnsv1/b/1t5SZGHoKlMAAAAASUVORK5CYII=" } } {% endaction %} [PreviousFile generators](https://learn.mechanic.dev/core/actions/file-generators) [NextPDF](https://learn.mechanic.dev/core/actions/file-generators/pdf) Last updated 22 days ago Was this helpful? --- # The Shopify action | Mechanic The [**Shopify action**](https://learn.mechanic.dev/core/actions/integrations/shopify) allows developers to submit any request to the Shopify Admin API. By [**responding to action results**](https://learn.mechanic.dev/techniques/responding-to-action-results) , the data returned by Shopify can be retrieved, and re-used by the calling task. This approach should (probably) only be used as a last resort, when Mechanic's other methods of [**reading data**](https://learn.mechanic.dev/core/shopify/read) do not cover the scenario. [PreviousBulk operations](https://learn.mechanic.dev/core/shopify/read/bulk-operations) [NextWriting data](https://learn.mechanic.dev/core/shopify/write) Last updated 1 year ago Was this helpful? --- # Previews | Mechanic A task uses its **preview** to demonstrate what actions the task intends to generate. Among other purposes (see below), this is also how tasks request the Shopify permissions they require. Mechanic generates a task preview by rendering the task code using a **preview event**, which resembles a live event that the task may see. The task is then responsible for rendering **preview actions** in response to the preview event, actions which are visually presented to the user and are analyzed by the platform, but are never actually performed. **Task previews cannot access the Mechanic cache or the Shopify Admin API.** This restriction is made to increase the predictability and performance of task previews. To provide tasks with relevant sample data during preview, developers can [define preview events](https://learn.mechanic.dev/core/tasks/previews/events) (to construct relevant scenarios at the event level) or use [stub data](https://learn.mechanic.dev/core/tasks/previews/stub-data) (to swap in predefined values for [environment variables](https://learn.mechanic.dev/core/tasks/code/environment-variables) , or for the results of data that would otherwise come from the Mechanic cache or the Shopify Admin API). [](https://learn.mechanic.dev/core/tasks/previews#purposes) Purposes ------------------------------------------------------------------------- A preview has three critical purposes: 1. Showing the user that the task will do what they expect it to do 2. Showing the task developer that the task code is functioning as intended 3. Showing the Mechanic platform what permissions the task requires ### [](https://learn.mechanic.dev/core/tasks/previews#for-users) For users Core to the design of Mechanic is the idea that we can make it easy to make it easy – in this case, making it easy for developers to show their users what a Mechanic task can be expected to do. By rendering preview actions, a task can prove to the user that it is interpreting their configuration as they intended. For example, by rendering a preview [Email](https://learn.mechanic.dev/core/actions/email) action, a task can show the user that their configured email content is appearing as expected inside the email body. This increases trust in the task, and allows users confidence in the task's outcome, even before the task processes a live event. ### [](https://learn.mechanic.dev/core/tasks/previews#for-developers) For developers Developers may think of previews as a sort of test, using preview actions to prove that their task code is functioning as intended. A quality task will exercise all of its code in response to a preview event; doing so gives the developer instant feedback on task results, without actually having to run the task with a live event. ### [](https://learn.mechanic.dev/core/tasks/previews#for-mechanic) For Mechanic At the platform level, Mechanic uses previews to determine what permissions a task requires. Mechanic gets this information from the actions that a task generates during preview, as well as from analysis of the Liquid lookups and GraphQL queries that a task uses during runtime. For example, if a task renders a [Shopify](https://learn.mechanic.dev/core/actions/integrations/shopify) action containing a [customerCreate](https://shopify.dev/api/admin-graphql/latest/mutations/customercreate) mutation, Mechanic will prompt the user to grant access to the `write_customers` Shopify OAuth scope. If Mechanic observes a task using `shop.customers`, or observes the [shopify](https://learn.mechanic.dev/platform/liquid/filters#shopify) filter receiving a customer-related GraphQL query, it will prompt for the `read_customers` scope. Some GraphQL mutations have multiple potential scope requirements, like [tagsAdd](https://shopify.dev/api/admin-graphql/latest/mutations/tagsadd) or [metafieldsSet](https://shopify.dev/api/admin-graphql/latest/mutations/metafieldsset) . Because the requirements of these mutations hinge on their arguments, make sure that your preview actions are rendered with realistic ID strings (e.g. `id: "gid://shopify/Product/12345"`). Mechanic will look for these IDs to determine what scopes to request. [](https://learn.mechanic.dev/core/tasks/previews#sources) Sources ----------------------------------------------------------------------- Previews are generated using synthetic, temporary, non-persisted events – at least one for each event topic that the task subscribes to. These events are sourced from one of three places, in order of priority: 1. If the task [defines its own preview event](https://learn.mechanic.dev/core/tasks/previews/events) for a given topic, the preview will use the defined event; 2. Or, if the Mechanic account has a recent event with a matching topic on file, the preview will use data from that event; 3. Or, if the event topic is standard and known to Mechanic (i.e. not a part of [the User domain](https://learn.mechanic.dev/platform/events) ), the preview will use illustrative example event data defined by the Mechanic platform. [](https://learn.mechanic.dev/core/tasks/previews#detecting-preview-events) Detecting preview events --------------------------------------------------------------------------------------------------------- A preview event is identical to a live event in all respects but one: it contains a `preview` attribute, set to `true`, identifying it as a preview event. For live events, the `preview` attribute does not exist. This means that `event.preview == false` is not a valid way to detect a live event. Instead, use `event.preview != true`, or `event.preview == nil`. Task code Copy {% if event.preview %} {% log "This is a preview event, generated by Mechanic." %} {% else %} {% log "This is a live event, received by Shopify." %} {% endif %} {% if event.preview != true %} {% log "This is a live event, received by Shopify." %} {% else %} {% log "This is a preview event, generated by Mechanic." %} {% endif %} A preview event's data is taken from the Mechanic account's event history, providing a realistic sample of the data a task can expect to see. (If the account history has no events for a given topic just yet, Mechanic will attempt to use anonymous sample event data of its own.) [](https://learn.mechanic.dev/core/tasks/previews#rendering-preview-actions) Rendering preview actions ----------------------------------------------------------------------------------------------------------- A developer can choose between rendering static and dynamic preview actions. Static preview actions are hard-coded, written to appear whenever `event.preview` is true. Dynamic preview actions are the result of the task code running normally, using event data in preview in the same way that it would use that event data with a live event. Because dynamic preview actions are the result of meaningfully exercising the task's code, they can provide a good indicator of how the task will behave with a live event. By contrast, static preview actions do not provide useful feedback on how a task is coded. ### [](https://learn.mechanic.dev/core/tasks/previews#static-preview-actions) Static preview actions A static preview action is rendered in direct response to `event.preview`. In general, it's better to use [dynamic preview actions](https://learn.mechanic.dev/core/tasks/previews#dynamic-preview-actions) , but an understanding of both techniques is useful. In the following example, a static preview action demonstrates that the task intends to tag incoming orders with "web". In actuality, the task's intent is to only tag orders that arrive via the Online Store channel; because the task can't be sure whether or not the preview event will contain such an order, a static preview action is used to ensure that a preview event always results in a tagging action. Branching a task like this has two problems: 1. The actual condition of the task is not exercised during preview. The task will need to be tested with live orders from multiple channels, in order to verify that the task works properly. 2. Duplicating code makes it easier for one copy of the code to fall out of date. By using completely different code for the preview and live actions, it becomes easier for developers to forget to keep the two copies in sync as the task evolves. Task code Subscriptions Copy {% if event.preview %} {% action "shopify" %} mutation { tagsAdd(id: "gid://shopify/Order/1234567890", tags: "web") { userErrors { field, message } } } {% endaction %} {% elsif order.source_name == "web" %} {% action "shopify" %} mutation { tagsAdd(id: {{ order.admin_graphql_api_id | json }}, tags: "web") { userErrors { field, message } } } {% endaction %} {% endif %} Copy shopify/orders/create ### [](https://learn.mechanic.dev/core/tasks/previews#dynamic-preview-actions) Dynamic preview actions A dynamic preview action is the natural result of exercising a task's code as completely as possible, without adding any business logic that responds to `event.preview`. Put another way, the idea is to make the preview (that appears during task editing) look as similar to a live event as possible. Example Copy {% if some_evaluated_condition %} {% action "shopify" %} mutation { tagsAdd(id: {{ order.admin_graphql_api_id | json }}, tags: "web") { userErrors { field, message } } } {% endaction %} {% endif %} There are two techniques available for "steering" the task towards desired outcomes during preview. 1. Use [**defined preview events**](https://learn.mechanic.dev/core/tasks/previews/events) to control preview event data, without ever having to add preview-related code to the task itself. This is the cleanest way to control data provided by the event during preview. 2. Use [**stub data**](https://learn.mechanic.dev/core/tasks/previews/stub-data) to dynamically swap in preview-friendly values. This is generally not necessary for preview _event_ data, but may be necessary when querying Shopify for data during a task: because the Shopify API is disabled during preview, using stub data can be useful for swapping in realistic values that _would_ be returned during a live run. [PreviousCustom validation](https://learn.mechanic.dev/core/tasks/options/custom-validation) [NextDefining preview events](https://learn.mechanic.dev/core/tasks/previews/events) Last updated 2 years ago Was this helpful? --- # GraphQL | Mechanic **GraphQL** is Shopify's preferred API language – it's the way developers are encouraged to query for and submit Shopify data. Mechanic tasks frequently use GraphQL to efficiently retrieve and write data from Shopify. [PreviousEvent filters](https://learn.mechanic.dev/platform/events/filters) [NextBasics](https://learn.mechanic.dev/platform/graphql/basics) Last updated 1 year ago Was this helpful? --- # Plans | Mechanic **Mechanic does not have different plans.** The following details apply to every Mechanic account: * Each account in good standing may install an unlimited number of [tasks](https://learn.mechanic.dev/core/tasks) , may receive an unlimited number of [events](https://learn.mechanic.dev/core/events) , and may process an unlimited number of [runs](https://learn.mechanic.dev/core/runs) . * Each account has a limited-term trial period, during which no billing agreement is required. * When the trial period expires, Mechanic will suspend the account, and will stop processing incoming events for that account. * Shopify stores that will only be used for development and testing, which will never see production customer traffic, are invited to write to [\[email protected\]](https://learn.mechanic.dev/cdn-cgi/l/email-protection#1b6f7e7a765b6e687e767e78737a75727835787476) to request a super-extended trial period. * Every account qualifies for our [pay-what-feels-good pricing policy](https://learn.mechanic.dev/platform/policies/pricing) , in which our system offers a suggested price based on Shopify plan type, inviting the user to contact [\[email protected\]](https://learn.mechanic.dev/cdn-cgi/l/email-protection#532736323e132620363e36303b323d3a307d303c3e) to talk about what price feels good to them. * Mechanic exclusively uses monthly billing. * Every account is subject to our [data policy](https://learn.mechanic.dev/platform/policies/data) . [](https://learn.mechanic.dev/platform/policies/plans#development-stores) Development stores ------------------------------------------------------------------------------------------------- Mechanic is Partner-friendly! If you have a Mechanic account used exclusively for development or testing, receiving no customer traffic, send the core team an email at [\[email protected\]](https://learn.mechanic.dev/cdn-cgi/l/email-protection#5c28393d311c292f3931393f343d32353f723f3331) . We can usually help out by granting a super-extended trial period. :) [PreviousData](https://learn.mechanic.dev/platform/policies/data) [NextPricing](https://learn.mechanic.dev/platform/policies/pricing) Last updated 2 years ago Was this helpful? --- # Reading and Writing to Shopify | Mechanic [Responding to events](https://learn.mechanic.dev/core/shopify/events) [Reading data](https://learn.mechanic.dev/core/shopify/read) [Writing data](https://learn.mechanic.dev/core/shopify/write) [Shopify admin action links](https://learn.mechanic.dev/core/shopify/admin-action-links) [API rate limit](https://learn.mechanic.dev/core/shopify/api-rate-limit) [API versions](https://learn.mechanic.dev/core/shopify/api-versions) [PreviousRetries](https://learn.mechanic.dev/core/runs/retries) [NextResponding to events](https://learn.mechanic.dev/core/shopify/events) Last updated 4 years ago Was this helpful? --- # Google Drive | Mechanic The Google Drive action allows you to upload files to your Google Drive. It supports various file types and can generate files dynamically using [file generators](https://learn.mechanic.dev/core/actions/file-generators) , including text files, PDFs, CSVs, and HTML files. Mechanic interacts with Google Drive via the Google Drive API, using OAuth2 for authentication. [](https://learn.mechanic.dev/core/actions/integrations/google-drive#options) Options ------------------------------------------------------------------------------------------ Option Type Description account string Required: the Google account email address to authenticate with uploads hash Required: a has specifying files to upload and their contents ### [](https://learn.mechanic.dev/core/actions/integrations/google-drive#uploads-hash-structure) Uploads hash structure The `uploads` hash supports these properties: Property Type Description overwrite boolean Optional: when true, files with matching names will be overwritten. Defaults to false \[path/filename\] string | hash One or more file paths mapped to their content. Paths can include folders (e.g., 'reports/monthly/file.txt'). Content can be either a direct string or a [file generator object](https://learn.mechanic.dev/core/actions/file-generators) . [](https://learn.mechanic.dev/core/actions/integrations/google-drive#authentication) Authentication -------------------------------------------------------------------------------------------------------- This action requires connecting a Google account with the appropriate Drive permissions. To connect an account: 1. Go to the Settings screen 2. Click Authentication 3. Follow the Google account connection flow [](https://learn.mechanic.dev/core/actions/integrations/google-drive#folder-support) Folder Support -------------------------------------------------------------------------------------------------------- Files can be organized in folders by including path information in the filename: * Use forward slashes to separate folder names (e.g., "reports/2024/monthly/file.pdf") * Folders will be created automatically if they don't exist * Can only access folders created by this integration * Invalid characters not allowed: `< > : " / \ | ? *` ### [](https://learn.mechanic.dev/core/actions/integrations/google-drive#path-examples) Path Examples Copy reports/monthly/report.pdf # Three levels deep data/2024/q1/sales.csv # Four levels deep archives/backups/files.zip # Three levels deep [](https://learn.mechanic.dev/core/actions/integrations/google-drive#examples) Examples -------------------------------------------------------------------------------------------- ### [](https://learn.mechanic.dev/core/actions/integrations/google-drive#simple-text-file-upload) Simple Text File Upload Copy {% action "google_drive" %} { "account": "[email protected]", "uploads": { "simple.txt": "Hello world!" } } {% endaction %} ### [](https://learn.mechanic.dev/core/actions/integrations/google-drive#multiple-files-with-overwrite) Multiple Files with Overwrite Copy {% action "google_drive" %} { "account": "[email protected]", "uploads": { "overwrite": true, "report.pdf": { "pdf": { "html": "This is a PDF generated from HTML
" } }, "data.csv": "Date,Value\n2024-01-01,100" } } {% endaction %} ### [](https://learn.mechanic.dev/core/actions/integrations/google-drive#files-in-folders) Files in Folders Copy {% action "google_drive" %} { "account": "[email protected]", "uploads": { "overwrite": true, "reports/monthly/sales.pdf": { "pdf": { "html": "Data for this month
" } }, "data/exports/stats.csv": "Date,Value\n2024-01-01,100", "archive/backups/data.zip": { "zip": { "files": { "readme.txt": "Backup files", "data.csv": "id,value\n1,test" } } } } } {% endaction %} ### [](https://learn.mechanic.dev/core/actions/integrations/google-drive#dynamic-file-generation) Dynamic File Generation Copy {% capture report_content %}Generated on {{ "now" | date: "%Y-%m-%d" }}