Shopify Metafields & Tags Reference
Appstle Subscriptions uses Shopify metafields and tags to store subscription data, power storefront widgets, and enable automation workflows. This reference covers every metafield and tag — what it contains, when it's set, and how to use it in your integration.
All metafields use the namespace appstle_subscription and are managed through the Shopify GraphQL Admin API (MetafieldsSetMutation).
Metafields are set on four Shopify resource types:
| Resource | Count | Visibility | Description |
|---|---|---|---|
| Shop | 15+ keys | Public (readable by any app/theme) | App configuration, widget settings, selling plans, build-a-box data |
| Selling Plan | 1 key | Public | Individual selling plan metadata |
| Order | 1 key | Public | Subscription context for each order |
| Customer | 1 key | Public | All subscription contracts for the customer |
Namespace visibility: All subscription metafields use the
appstle_subscriptionnamespace (no$app:prefix), which means they are readable by other apps, themes, and Liquid templates.
Shop metafields store the app's configuration and are used by the storefront widget, checkout extensions, and build-a-box features. They are written by SubscribeItScriptUtils.updateShopMetafieldsForSettings() whenever a merchant saves settings.
When updated: On every settings save in the Appstle admin, and on selling plan or bundle configuration changes. Updates are synchronous (immediate).
| Property | Value |
|---|---|
| Type | json |
| Resource | Shop |
| Purpose | Core widget and app configuration — UI settings, asset paths, selling plans, labels, and validation rules |
| Updated when | Merchant saves settings in the Appstle admin |
{
"widgetEnabled": true,
"sellingPlans": [...],
"assetPaths": {
"js": "https://cdn.appstle.com/...",
"css": "https://cdn.appstle.com/..."
},
"labels": {...},
"validationRules": {...}
}| Property | Value |
|---|---|
| Type | multi_line_text_field |
| Resource | Shop |
| Purpose | Localized UI labels and translations for the subscription widget |
| Updated when | Merchant saves label translations |
| Also set by | LabelTranslationsServiceImpl |
| Property | Value |
|---|---|
| Type | json |
| Resource | Shop |
| Purpose | Shop metadata including money format, feature flags, and API tokens |
| Property | Value |
|---|---|
| Type | json |
| Resource | Shop |
| Purpose | Free-product selling plans only |
| Also set by | SubscriptionGroupServiceImpl |
| Property | Value |
|---|---|
| Type | json |
| Resource | Shop |
| Purpose | ALL selling plans (including paid plans) |
| Also set by | SubscriptionGroupServiceImpl |
Note: The key uses camelCase (
all_Selling_Plans) — this is intentional and must not be changed.
| Property | Value |
|---|---|
| Type | json |
| Resource | Shop |
| Purpose | Checkout validation rules including duplicate subscription restrictions and per-customer limits |
{
"preventDuplicateSubscriptions": true,
"maxSubscriptionsPerCustomer": 5,
"rules": [...]
}| Property | Value |
|---|---|
| Type | multi_line_text_field |
| Resource | Shop |
| Purpose | Custom widget template HTML (only set if merchant has configured a custom template) |
| Property | Value |
|---|---|
| Type | json |
| Resource | Shop |
| Purpose | Map of all available widget templates |
Build-a-box bundles use multiple metafield keys for configuration, styling, and validation.
| Key | Type | Purpose |
|---|---|---|
bundle | json | Bundle/build-a-box configuration |
bab_subscription_css | json | Build-a-box subscription CSS styles |
bab_customization_css | json | Build-a-box customization CSS |
bab_validation_info | json | Build-a-box validation rules |
bab_setting_info | json | Build-a-box settings |
bab_info_0, bab_info_1, ... | json | Individual build-a-box bundle details (one per enabled bundle, dynamically indexed) |
total_bab | integer | Total count of enabled build-a-box bundles |
Build-a-box metafields are set by
SubscriptionBundlingServiceImpl.handleMetafieldInputBuildABoxInfo(). Thebab_info_*keys are zero-indexed — if a merchant has 3 bundles, the keys will bebab_info_0,bab_info_1, andbab_info_2.
| Property | Value |
|---|---|
| Type | json |
| Resource | Selling Plan |
| Purpose | Individual selling plan metadata (frequency, billing policy, discounts) |
| Set by | SubscriptionGroupServiceImpl.createOrUpdateSellingPlans() |
| Updated when | Selling plan is created or modified |
{
"frequencyCount": 1,
"frequencyInterval": "MONTH",
"billingPolicy": {
"interval": "MONTH",
"intervalCount": 1
},
"discountType": "PERCENTAGE",
"discountValue": 10.0
}| Property | Value |
|---|---|
| Type | json |
| Resource | Order |
| Purpose | Full subscription contract context for this order |
| Set by | AbstractSubscriptionService.updateOrderMetafields() (subscription-async) |
| Updated when | Order is created via subscription (initial or recurring billing) |
| Update mechanism | Queued via SQS, processed asynchronously |
This metafield contains a complete snapshot of the subscription context at the time the order was created:
{
"customer": {
"id": "gid://shopify/Customer/1234567890"
},
"subscriptionContract": {
"id": "gid://shopify/SubscriptionContract/9876543210",
"status": "ACTIVE",
"sellingPlanIds": ["gid://shopify/SellingPlan/111"],
"sellingPlanNames": ["Monthly Subscription - 10% off"],
"variantIds": ["gid://shopify/ProductVariant/222"],
"variantNames": ["Default Title"],
"currentCycle": 3,
"groupPlanNames": ["Subscribe & Save"],
"cancellationReason": null
},
"lineItems": [
{
"variantId": "gid://shopify/ProductVariant/222",
"title": "Premium Coffee Beans",
"productId": "gid://shopify/Product/333",
"sellingPlanId": "gid://shopify/SellingPlan/111",
"sellingPlanName": "Monthly Subscription - 10% off",
"sku": "COFFEE-PREMIUM-1KG"
}
],
"firstOrder": {
"id": "gid://shopify/Order/444",
"createdAt": "2025-01-15T10:30:00Z"
}
}| Property | Value |
|---|---|
| Type | json |
| Resource | Customer |
| Purpose | All subscription contracts for this customer with full details |
| Set by | AbstractSubscriptionService.setCustomerMetaFields() (subscription-async) |
| Updated when | Any subscription contract changes (created, updated, paused, cancelled, billing attempt) |
| Update mechanism | Queued via SQS (subscription-update-customer-metafields.fifo), processed asynchronously by subscription-async |
[
{
"id": "gid://shopify/SubscriptionContract/9876543210",
"status": "ACTIVE",
"sellingPlanNames": ["Monthly Subscription - 10% off"],
"nextBillingDate": "2025-04-15T10:30:00Z",
"lineItems": [
{
"title": "Premium Coffee Beans",
"variantId": "gid://shopify/ProductVariant/222",
"sku": "COFFEE-PREMIUM-1KG"
}
]
}
]Important: Customer metafield updates are asynchronous. The main app queues the update via SQS, and subscription-async processes it. There may be a delay of a few seconds between a contract change and the metafield reflecting that change.
Appstle Subscriptions applies tags to Orders and Customers. All tags are merchant-configurable through the Appstle admin under Settings.
Tags are applied using the Shopify GraphQL Admin API (TagsAddMutation / TagsRemoveMutation).
Order tags are static strings (no Liquid template support) that identify whether an order is the initial subscription order or a recurring billing order.
| Property | Value |
|---|---|
| Config field | firstTimeOrderTag |
| Default value | appstle_subscription_first_order |
| Applied when | Initial subscription order is created |
| Applied by | SubscriptionCreateService and AbstractSubscriptionService.updateShopifyOrderDetails() (subscription-async) |
| Removed | Never (order tags are permanent) |
| Property | Value |
|---|---|
| Config field | recurringOrderTag |
| Default value | appstle_subscription_recurring_order |
| Applied when | Each subsequent billing attempt creates an order |
| Applied by | SubscriptionBillingAttemptService and AbstractSubscriptionService.updateShopifyOrderDetails() (subscription-async) |
| Removed | Never (order tags are permanent) |
- Go to Appstle Admin → Settings
- Find the Order Tags section
- Enter your desired tag values (or keep the defaults)
- Save settings
Backfill endpoint: If you need to apply tags to orders that were created before tags were configured, use the
MiscellaneousResource.applyMissedOrderTags()API endpoint to backfill missed tags.
Customer tags are dynamic — they change as subscription status changes. They support Liquid template syntax with access to subscription data variables, enabling highly customized tags.
Customer tags follow a strict priority hierarchy — only one status tag is active at a time:
ACTIVE > PAUSED > INACTIVE| Status | Condition | Config Field | Default Value |
|---|---|---|---|
| Active | Customer has 1+ ACTIVE subscription contracts | customerActiveSubscriptionTag | appstle_subscription_active_customer |
| Paused | Customer has PAUSED contracts but no active ones | customerPausedSubscriptionTag | appstle_subscription_paused_customer |
| Inactive | Customer has no active or paused contracts (all cancelled) | customerInActiveSubscriptionTag | appstle_subscription_inactive_customer |
When status changes:
- The previous status tag is removed (
TagsRemoveMutation) - The new status tag is added (
TagsAddMutation) - The applied tag is tracked in the database (
SubscriptionContractDetails.customerTagcolumn)
| Event | Action |
|---|---|
| Subscription contract created | Active tag applied |
| Subscription paused | Active tag removed → Paused tag applied (if no other active contracts) |
| Subscription resumed | Paused tag removed → Active tag applied |
| Subscription cancelled | Tag recalculated based on remaining contracts |
| Billing attempt succeeds/fails | Tag recalculated |
| Subscription event processed | Tag recalculated |
Processing locations:
- subscription app:
SubscriptionContractDetailsServiceImpl.updateCustomerTags() - subscription-async:
AbstractSubscriptionService.updateCustomerTags()— triggered from contract creation, status changes, and subscription events - Deferred updates: Via AWS Step Functions state machine for batched/delayed processing
Customer tags support Liquid template syntax, allowing you to create dynamic, data-driven tags. Wrap variables in double curly braces: {{variable}}.
| Variable | Type | Description | Example Value |
|---|---|---|---|
{{customer.id}} | String | Shopify customer GID | gid://shopify/Customer/1234567890 |
{{contract.id}} | String | Subscription contract GID | gid://shopify/SubscriptionContract/9876 |
{{contract.sellingPlanIds}} | String | Comma-separated selling plan IDs | gid://shopify/SellingPlan/111 |
{{contract.sellingPlanNames}} | String | Comma-separated selling plan names | Monthly Subscription - 10% off |
{{contract.variantIds}} | String | Comma-separated variant IDs | gid://shopify/ProductVariant/222 |
{{contract.variantNames}} | String | Comma-separated variant names | Default Title |
{{contract.currentCycle}} | Number | Current billing cycle number | 3 |
{{contract.cancellationReason}} | String | Cancellation reason (if cancelled) | Too expensive |
{{order.id}} | String | First order GID | gid://shopify/Order/444 |
{{order.createdAt}} | String | First order creation date (ISO 8601) | 2025-01-15T10:30:00Z |
Line item variables (available within the contract context):
| Variable | Type | Description |
|---|---|---|
sellingPlanId | String | Selling plan ID for this line item |
variantId | String | Variant ID |
variantTitle | String | Variant title |
title | String | Product title |
productId | String | Product ID |
sku | String | SKU |
sellingPlanName | String | Selling plan name |
Static tag (default behavior):
appstle_subscription_active_customerDynamic tag with selling plan name:
active_subscriber_{{contract.sellingPlanNames}}Result: active_subscriber_Monthly Subscription - 10% off
Dynamic tag with cycle count:
subscriber_cycle_{{contract.currentCycle}}Result: subscriber_cycle_3
Dynamic tag with product SKU:
subscribed_to_{{contract.variantNames}}Result: subscribed_to_Default Title
- Go to Appstle Admin → Settings
- Find the Customer Tags section
- Enter your desired tag templates for each status (Active, Paused, Inactive)
- Use Liquid variables for dynamic tags, or plain text for static tags
- Save settings
In addition to metafields and tags, Appstle supports Shopify's native customAttributes on subscription contracts. These are key-value pairs that can be transferred from the original order.
These settings control whether data from the original order is copied to the subscription contract:
| Setting | Default | Description |
|---|---|---|
transferOrderNotesToSubscription | true | Transfer order notes (text memo) to the subscription contract |
transferOrderNoteAttributesToSubscription | true | Transfer order note attributes (key-value pairs) to the subscription contract |
transferOrderLineItemAttributesToSubscription | true | Transfer line item attributes to the subscription contract |
Configure these in Appstle Admin → Settings.
| Resource | Namespace | Key | Type | Visibility |
|---|---|---|---|---|
| Shop | appstle_subscription | setting | json | Public |
| Shop | appstle_subscription | widget_template_html | multi_line_text_field | Public |
| Shop | appstle_subscription | all_widget_template_html | json | Public |
| Shop | appstle_subscription | bundle | json | Public |
| Shop | appstle_subscription | labels | multi_line_text_field | Public |
| Shop | appstle_subscription | selling_plans | json | Public |
| Shop | appstle_subscription | checkout_validation | json | Public |
| Shop | appstle_subscription | bab_subscription_css | json | Public |
| Shop | appstle_subscription | bab_customization_css | json | Public |
| Shop | appstle_subscription | shop_info | json | Public |
| Shop | appstle_subscription | bab_validation_info | json | Public |
| Shop | appstle_subscription | bab_setting_info | json | Public |
| Shop | appstle_subscription | all_Selling_Plans | json | Public |
| Shop | appstle_subscription | bab_info_[0-N] | json | Public |
| Shop | appstle_subscription | total_bab | integer | Public |
| Selling Plan | appstle_subscription | selling_plan | json | Public |
| Order | appstle_subscription | details | json | Public |
| Customer | appstle_subscription | subscriptions | json | Public |
| Resource | Tag | Default Value | Liquid Support | Permanent? |
|---|---|---|---|---|
| Order | First-time order tag | appstle_subscription_first_order | No | Yes |
| Order | Recurring order tag | appstle_subscription_recurring_order | No | Yes |
| Customer | Active subscription tag | appstle_subscription_active_customer | Yes | No (swapped on status change) |
| Customer | Paused subscription tag | appstle_subscription_paused_customer | Yes | No (swapped on status change) |
| Customer | Inactive subscription tag | appstle_subscription_inactive_customer | Yes | No (swapped on status change) |
| Mutation | Purpose |
|---|---|
MetafieldsSetMutation | Create or update metafields on any resource |
TagsAddMutation | Add tags to orders or customers |
TagsRemoveMutation | Remove tags from customers |
Can I read subscription metafields from my Liquid theme?
Yes. All subscription metafields use the appstle_subscription namespace (no $app: prefix), so they are accessible in Liquid templates via {{ shop.metafields.appstle_subscription.setting }}, {{ customer.metafields.appstle_subscription.subscriptions }}, etc.
How quickly are customer metafields updated after a contract change?
Customer metafield updates are queued via SQS (subscription-update-customer-metafields.fifo) and processed asynchronously. Typical latency is a few seconds, but during high-traffic periods it may take longer.
Can I use the same tag template for Active and Paused?
Technically yes, but this defeats the purpose of mutual exclusivity. The system removes one and adds the other — if they're identical, the tag will be briefly removed and re-added, which could cause issues with automations that react to tag changes.
What happens to customer tags when a customer has multiple subscriptions?
Tags follow a priority hierarchy: Active > Paused > Inactive. If a customer has both active and paused subscriptions, the active tag takes precedence. The inactive tag is only applied when ALL contracts are cancelled.
Can I backfill order tags for existing orders?
Yes. Use the applyMissedOrderTags API endpoint to backfill tags on orders that were created before your tag configuration was set up.