Abra Metafields for Headless Storefronts
This guide documents the data structure and API access patterns for using Abra promotions in headless storefronts.
IMPORTANT: To access ANY of the namespaces below via the Storefront API, the Tapcart integration MUST be enabled in Abra settings. This integration creates the necessary Metafield Definitions with public read access for both promotions and discounts.
Namespaces & Keys
The following namespaces are exposed to the Storefront API. Queries must use the full resolved namespace format including the App ID (1795063809).
Main Promotion Data:
Namespace: app--1795063809--tapcart-promotions
Key: PUBLICIndividual Discounts:
Namespace: app--1795063809--discounts
Key: <discount-slug>Query Example
Query the active promotion configuration using the Storefront API:
query GetAbraPromotion {
shop {
promotionMetafield: metafield(
namespace: "app--1795063809--tapcart-promotions"
key: "PUBLIC"
) {
value
}
}
}The API returns a JSON string containing the promotion object.
Promotion Object Structure
The parsed JSON object contains the following fields:
{
"title": "Summer Sale - 20% Off",
"startsAt": "2025-06-01T00:00:00.000Z",
"endsAt": "2025-08-31T23:59:59.000Z",
"status": "ACTIVE",
"origin": "ABRA",
"discountCodes": ["summer20"],
"redeemCode": "SUMMER20",
"schema": { ... },
"discounts": { ... },
"affiliate": { ... }
}Field Descriptions:
title | string | Promotion title |
startsAt | string | null | ISO start date |
endsAt | string | null | ISO end date |
status | string | Promotion status |
discountCodes | string[] | List of associated discount slugs |
schema | object | UI configuration grouped by template |
discounts | object | Discount details keyed by ID |
affiliate | object | Affiliate information (if applicable) |
UI Schema Configuration
The schema object organizes banner and block settings by page template type.
"schema": {
"all": {
"banner-default": {
"id": "block-uuid",
"type": "banner",
"name": "default",
"persistentState": true,
"states": [
{
"id": "state-uuid",
"state": "DEFAULT",
"icon": "percent",
"text": "Use code {{code}} for {{discount_value}} off!"
}
]
}
},
"cart": {
"banner-cart": { ... }
}
}This structure allows mapping specific banner configurations to different routes in your application (e.g., cart page vs. product page).
Discount Types & Values
The discounts object contains detailed configuration for each discount.
The discountValue field structure depends on the type property.
Percentage discount
"discountValue": {
"type": "DiscountPercentage",
"percentage": 20
}Fixed amount discount
"discountValue": {
"type": "DiscountAmount",
"discountAmount": {
"amount": "10.00",
"currencyCode": "USD",
"cents": 1000
},
"appliesOnEachItem": false
}Discount on Quantity
"discountValue": {
"type": "DiscountOnQuantity",
"quantity": 3,
"effect": {
"type": "DiscountPercentage",
"percentage": 15
}
// effect can also be "DiscountAmount" structure
}Free Gift
"discountValue": {
"type": "Gift",
"products": [
{
"handle": "free-tote-bag",
"variants": [
{
"id": "gid://shopify/ProductVariant/987654321",
"displayName": "Free Tote Bag - Black"
}
]
}
],
"compound": false,
"countsTowardEligibility": false
}Free Shipping
"discountValue": {
"type": "DiscountCodeFreeShipping"
}Tiered Discount
"discountValue": {
"type": "TieredDiscount",
"globalPrerequisite": "SUBTOTAL", // or "QUANTITY"
"tiersType": "PERCENTAGE_OFF", // or "FREE_GIFT"
"compound": false,
"entitled": {
"type": "AllDiscountItems",
"all": true
},
"tiers": [
{
"prerequisite": {
"minimumRequirement": {
"greaterThanOrEqualToSubtotal": {
"amount": "50.00",
"currencyCode": "USD",
"cents": 5000
}
// or greaterThanOrEqualToQuantity: 3
}
},
"value": {
"type": "DiscountPercentage",
"percentage": 10
}
// or value type "Gift" with products array
}
]
}Multi-effect Tiered Discount
"discountValue": {
"type": "MultiEffectTiers",
"globalPrerequisite": "SUBTOTAL", // or "QUANTITY"
"compound": false,
"tiers": [
{
"prerequisite": { ... },
"discounts": [
// Can contain multiple discount types per tier
{
"type": "ProductDiscount",
"value": [{
"entitled": { ... },
"value": { "type": "DiscountPercentage", "percentage": 10 }
}]
},
{
"type": "GWPDiscount",
"value": { "type": "Gift", "products": [...] }
},
{
"type": "OrderDiscount",
"value": { "type": "DiscountAmount", ... }
},
{
"type": "FreeShippingDiscount",
"entitledCountries": "all" // or ["US", "CA"]
}
]
}
]
}Buy X Get Y (BXGY)
"discountValue": {
"type": "BXGY",
"customerBuys": {
"items": {
"all": false,
"products": ["handle-1"],
"variants": ["id-1"]
},
"value": {
"type": "DiscountQuantity", // or "DiscountPurchaseAmount"
"quantity": 1
}
},
"customerGets": {
"items": { ... },
"value": {
"type": "DiscountPercentage",
"percentage": 50
}
}
}Volume Discount
"discountValue": {
"type": "VolumeDiscount",
"globalPrerequisite": ["QUANTITY"],
"tiersType": "PERCENTAGE_OFF",
"compound": false,
"customerGets": {
"appliesTo": "ELIGIBLE_VARIANTS",
"all": false,
"products": [...]
},
"minimumPurchaseAmountPerProductVariant": false,
"tiers": [
{
"prerequisite": { "minimumRequirement": { "greaterThanOrEqualToQuantity": 5 } },
"value": { "type": "DiscountPercentage", "percentage": 10 }
}
]
}Eligibility & Prerequisites
Discounts include fields defining scope and requirements:
"entitled": {
"type": "DiscountCollections", // or "DiscountProducts", "AllDiscountItems"
"collectionIds": ["gid://shopify/Collection/123"],
"collections": ["collection-handle"],
"products": ["product-handle"],
"variants": ["gid://shopify/ProductVariant/123"],
"markets": ["US", "CA"], // ISO country codes or market names
"shopifyMarketIds": ["gid://shopify/Market/123"],
"currency": "USD" // If restricted to specific currency
}
"prerequisite": {
"minimumRequirement": {
"greaterThanOrEqualToSubtotal": {
"amount": "50.00",
"currencyCode": "USD",
"cents": 5000
},
"greaterThanOrEqualToQuantity": 3
},
"customerSelection": {
"type": "SEGMENTS", // or "ALL"
"ids": ["gid://shopify/Segment/123"]
},
"tapCartExclusive": false,
"posExclusive": false
}Block Config Structure
All blocks share these base fields:
{
"id": "block-uuid",
"type": "banner",
"name": "default",
"template": "all",
"persistentState": true,
"onevent": "cart:updated",
"discountIds": ["uuid-1", "uuid-2"],
"states": [ ... ],
"statesByDiscountGroup": { ... }
}Fields:
id | string | Unique block ID |
type | string | Block type identifier |
name | string | Block name |
template | string | Template target (e.g. "all") |
persistentState | boolean | Whether state persists |
onevent | string | undefined | Event trigger |
discountIds | string[] | null | Associated discount IDs |
states | array | undefined | State configurations |
statesByDiscountGroup | object | undefined | States grouped by discount ID |
Block State Structure
Each state in the states array:
{
"id": "state-uuid",
"state": "DEFAULT",
"icon": "percent",
"text": "Save 20% on your order!",
"link": "/collections/sale",
"html": "<span>Custom HTML</span>",
"message": "Free shipping unlocked!",
"tier_summary": "Spend $50 more for 15% off",
"disabled": false
}Fields:
id | string | Unique state ID |
state | string | State identifier (e.g., "DEFAULT") |
icon | string | undefined | Icon name |
text | string | undefined | Display text |
link | string | undefined | Link URL |
html | string | undefined | Custom HTML content |
message | string | undefined | Message text |
tier_summary | string | undefined | Tier progress summary |
disabled | boolean | undefined | Whether state is disabled |
Template Variables
Text fields in the schema support the following variables for dynamic replacement:
General | {{code}}, {{discount_value}}, {{redeemCode}} |
Cart | {{subtotal_price}}, {{total_discount}}, {{final_subtotal_price}} |
Tiers | {{discount.progress_total}}, {{discount.progress_remaining}} |
Product | {{final_price}}, {{compare_at_price}}, {{title}} |
Available Features
Using this data, the following features can be implemented:
Announcement bars and page-specific banners
Progress bars for tiered discounts and free gifts
Discount value display and savings calculations
Countdown timers using start/end dates
Free gift product rendering
Auto-application of discount codes