Tag and segment customers by days since last order, with Mechanic.

Mechanic is a development and ecommerce automation platform for Shopify. :)

Tag and segment customers by days since last order

This task will run daily to query all customers in your shop who have placed an order, tagging each with whichever configured tag is paired with the closest "days since last order" threshold. This is great way to keep tabs on who is active in your store, and who you might need to reconnect with.

Runs Occurs every day at midnight (in local time) and Occurs when a user manually triggers the task. Configuration includes customer tags and days since last order and create customer segments by tag.

15-day free trial – unlimited tasks

Documentation

This task will run daily to query all customers in your shop who have placed an order, tagging each with whichever configured tag is paired with the closest "days since last order" threshold. This is great way to keep tabs on who is active in your store, and who you might need to reconnect with.

Optionally, you can select "Create customer segments by tag" to have the task create new customer segments for each tag configured in this task.

The task comes preconfigured with a "Burn" tag set at 365 or more days since last order, a "Churn" tag set at 180 days or but less than 365 days since last order, an "At Risk" tag set at 90 days or more but less than 180 days since last order, and an "Active" tag for anyone who has placed an order more recently than 90 days. Change these configuration values to whatever makes the most sense for your business.

Note: the order in which you enter the tags and days since pairs does not matter; at run time, the task will first sort by the largest days since value, and then make tagging decisions accordingly.

Developer details

Mechanic is designed to benefit everybody: merchants, customers, developers, agencies, Shopifolks, everybody.

That’s why we make it easy to configure automation without code, why we make it easy to tweak the underlying code once tasks are installed, and why we publish it all here for everyone to learn from.

(By the way, have you seen our documentation? Have you joined the Slack community?)

Open source
View on GitHub to contribute to this task
Subscriptions
mechanic/scheduler/daily
mechanic/user/trigger
Tasks use subscriptions to sign up for specific kinds of events. Learn more
Options
customer tags and days since last order (keyval, number, required) , create customer segments by tag (boolean)
Code
{% assign customer_tags_and_days_since_last_order = options.customer_tags_and_days_since_last_order__keyval_number_required %}
{% assign create_customer_segments_by_tag = options.create_customer_segments_by_tag__boolean %}
{% assign now_s = "now" | date: "%s" %}

{% assign tag_rules = array %}

{% for keyval in customer_tags_and_days_since_last_order %}
  {% assign customer_tag = keyval[0] %}
  {% assign days_since_last_order = keyval[1] %}

  {% if customer_tag == blank or days_since_last_order == blank or days_since_last_order < 0 %}
    {% continue %}
  {% endif %}

  {% assign tag_rule = hash %}
  {% assign tag_rule["days"] = days_since_last_order %}
  {% assign tag_rule["tag"] = customer_tag %}
  {% assign tag_rule["threshold_s"]
    = days_since_last_order
    | times: -86400
    | plus: now_s
  %}
  {% assign tag_rules = tag_rules | push: tag_rule %}
{% endfor %}

{% assign tag_rules = tag_rules | sort: "days" | reverse %}
{% assign configured_tags = tag_rules | map: "tag" %}

{% if tag_rules == blank %}
  {% error "Please enter at least one customer tag and days since last order combo." %}
{% endif %}

{% if event.topic == "mechanic/user/trigger" or event.topic contains "mechanic/scheduler/" %}
  {% comment %}
    -- get IDs of all customers who have placed an order
  {% endcomment %}

  {% assign cursor = nil %}
  {% assign customer_ids = array %}

  {% for n in (1..100) %}
    {% capture query %}
      query {
        customerSegmentMembers(
          first: 1000
          after: {{ cursor | json }}
          query: "number_of_orders > 0"
        ) {
          pageInfo {
            hasNextPage
            endCursor
          }
          edges {
            node {
              id
            }
          }
        }
      }
    {% endcapture %}

    {% assign result = query | shopify %}

    {% assign customer_segment_members = result.data.customerSegmentMembers.edges | map: "node" %}

    {% comment %}
      -- remove the "SegmentMember" portion from IDs for easier use in querying each customer for additional data not available in the segment resource
    {% endcomment %}

    {% for customer_segment_member in customer_segment_members %}
      {% assign customer_ids[customer_ids.size] = customer_segment_member.id | remove: "SegmentMember" %}
    {% endfor %}

    {% if result.data.customerSegmentMembers.pageInfo.hasNextPage %}
      {% assign cursor = result.data.customerSegmentMembers.pageInfo.endCursor %}
    {% else %}
      {% break %}
    {% endif %}
  {% endfor %}

  {% unless event.preview %}
    {% log
      count_of_customers_who_have_placed_an_order: customer_ids.size,
      tag_rules: tag_rules
    %}
  {% endunless %}

  {% if event.preview %}
    {% assign customer_ids[0] = "gid://shopify/Customer/1234567890" %}
  {% endif %}

  {% for customer_id in customer_ids %}
    {% comment %}
      -- get customer tags and last order date
    {% endcomment %}

    {% capture query %}
      query {
        customer(id: {{ customer_id | json }}) {
          id
          tags
          lastOrder {
            processedAt
          }
        }
      }
    {% endcapture %}

    {% assign result = query | shopify %}

    {% if event.preview %}
      {% capture result_json %}
        {
          "data": {
            "customer": {
              "id": "gid://shopify/Customer/1234567890",
              "lastOrder": {
                "processedAt": {{ tag_rules[0].threshold_s | json }}
              }
            }
          }
        }
      {% endcapture %}

      {% assign result = result_json | parse_json %}
    {% endif %}

    {% assign customer = result.data.customer %}

    {% comment %}
      -- determine the tag that the customer should have based their last order date
    {% endcomment %}

    {% assign customer_last_order_processed_at_s = customer.lastOrder.processedAt | date: "%s" | times: 1 %}
    {% assign tag_should_have = nil %}

    {% for tag_rule in tag_rules %}
      {% if customer_last_order_processed_at_s <= tag_rule.threshold_s %}
        {% assign tag_should_have = tag_rule.tag %}
        {% break %}
      {% endif %}
    {% endfor %}

    {% unless tag_should_have == blank or customer.tags contains tag_should_have %}
      {% action "shopify" %}
        mutation {
          tagsAdd(
            id: {{ customer.id | json }}
            tags: {{ tag_should_have | json }}
          ) {
            node {
              ... on Customer {
                id
                defaultEmailAddress {
                  emailAddress
                }
                lastOrder {
                  name
                  processedAt
                }
              }
            }
            userErrors {
              field
              message
            }
          }
        }
      {% endaction %}
    {% endunless %}

    {% comment %}
      -- remove any other configured tags from the customer if needed
    {% endcomment %}

    {% assign tags_to_remove = array %}

    {% for configured_tag in configured_tags %}
      {% unless configured_tag == tag_should_have %}
        {% if customer.tags contains configured_tag %}
          {% assign tags_to_remove = tags_to_remove | push: configured_tag %}
        {% endif %}
      {% endunless %}
    {% endfor %}

    {% if tags_to_remove != blank %}
      {% action "shopify" %}
        mutation {
          tagsRemove(
            id: {{ customer.id | json }}
            tags: {{ tags_to_remove | json }}
          ) {
            node {
              ... on Customer {
                id
                defaultEmailAddress {
                  emailAddress
                }
                lastOrder {
                  name
                  processedAt
                }
              }
            }
            userErrors {
              field
              message
            }
          }
        }
      {% endaction %}
    {% endif %}
  {% endfor %}

  {% comment %}
    -- if task option enabled, then create customer segments for each configured tag (if they don't already exist)
  {% endcomment %}

  {% if create_customer_segments_by_tag %}
    {% assign cursor = nil %}
    {% assign segments = array %}

    {% for n in (1..10) %}
      {% capture query %}
        query {
          segments(
            first: 250
            after: {{ cursor | json }}
          ) {
            pageInfo {
              hasNextPage
              endCursor
            }
            nodes {
              id
              name
              query
            }
          }
        }
      {% endcapture %}

      {% assign result = query | shopify %}

      {% assign segments
        = result.data.segments.nodes
        | default: array
        | concat: segments
      %}

      {% if result.data.segments.pageInfo.hasNextPage %}
        {% assign cursor = result.data.segments.pageInfo.endCursor %}
      {% else %}
        {% break %}
      {% endif %}
    {% endfor %}

    {% for tag_rule in tag_rules %}
      {%- capture segment_rule -%}
        customer_tags CONTAINS '{{ tag_rule.tag }}'
      {%- endcapture -%}

      {% assign matched_segment
        = segments
        | where: "name", tag_rule.tag
        | where: "query", segment_rule
      %}

      {% if matched_segment != blank %}
        {% log
          message: "This customer segment already exists.",
          matched_segment: matched_segment
        %}
        {% continue %}
      {% endif %}

      {% action "shopify" %}
        mutation {
          segmentCreate(
            name: {{ tag_rule.tag | json }}
            query: {{ segment_rule | json }}
          ) {
            segment {
              id
              name
              query
            }
            userErrors {
              field
              message
            }
          }
        }
      {% endaction %}
    {% endfor %}
  {% endif %}
{% endif %}
Task code is written in Mechanic Liquid, an extension of open-source Liquid enhanced for automation. Learn more
Defaults
Customer tags and days since last order
{"Burn" => "365", "Churn" => "180", "At Risk" => "90", "Active" => "0"}
Create customer segments by tag
true