App-exclusive prices on Shopify through the API
This page describes how a development team, whether the merchant’s, their ERP’s or a third party’s, can manage app-exclusive prices on a Shopify store on their own. It is useful, for example, to sync prices from an ERP or a PIM without going through the Reskyt editor.
It applies to Shopify only. The only public Reskyt API documented is the push notifications one.
How it works
Section titled “How it works”The exclusive price of a variant is stored in a standard Shopify metafield on that variant. Two pieces read it:
| Piece | What it does with the metafield |
|---|---|
| Reskyt Shopify Function | Applies the price at checkout |
| Reskyt theme block | Shows the price on the product page and in collections, in Liquid themes |
No database or API of Reskyt’s own is involved. Anyone with write access to the store’s Admin API can write the metafield.
Prerequisites
Section titled “Prerequisites”- An automatic discount already created in the store. The metafield only has an effect at checkout if the automatic discount “Reskyt — Precio Catálogo App”, which the Function is bound to, exists. It is created the first time someone saves a Catalog price rule from the Reskyt app editor in Shopify (Promotions → New rule). Doing it once per store is enough. If the editor has never been used, writing the metafield has no effect.
- An app of your own, private or custom, with the
write_productsscope on that store, to use the Admin GraphQL API. - The Reskyt app embed active in the theme, required so that app orders are identified as such. See Shopify.
The metafield
Section titled “The metafield”| Field | Value |
|---|---|
Resource (ownerType) | PRODUCTVARIANT. Written per variant, not per product |
| Namespace | reskyt |
| Key | catalog_price |
| Type | json |
{ "price": 19.99, "ruleId": "my-integration", "customerTag": "wholesale", "from": "2026-01-01T00:00:00Z", "until": "2026-01-31T23:59:59Z"}| Field | Required | Description |
|---|---|---|
price | Yes | Fixed price, not a percentage, that the app user will see and pay for that variant, in the store currency |
ruleId | No | Free identifier for your own tracking. Reskyt does not use it |
customerTag | No | Shopify customer tag, not the native Segment. If set, the price only applies to logged-in customers who have that exact tag. If omitted, it applies to any app user |
from, until | No | Informational only. The Function does not read them: as long as the metafield exists, the price is active |
When the price applies
Section titled “When the price applies”The price is applied at checkout when all of these conditions are met:
- The order comes from the app, that is, the cart has the
__reskyt_app_originattribute set totrue. - The metafield
priceis lower than the unit price the line currently has in the cart. If it is equal or higher, no discount is applied. - If the value includes
customerTag, the customer is logged in and has that tag.
Date window
Section titled “Date window”from and until are not enforced. If the price must have a start and an end
date, the integration must write the metafield at the start and delete it at the
end, for example with a scheduled process of your own.
Writing the price
Section titled “Writing the price”1. Get the variant identifier from its SKU
query FindVariantBySku($query: String!) { productVariants(first: 1, query: $query) { nodes { id } }}Variables:
{ "query": "sku:YOUR-SKU" }2. Write the metafield
mutation SetCatalogPrice($metafields: [MetafieldsSetInput!]!) { metafieldsSet(metafields: $metafields) { metafields { id } userErrors { field message } }}Variables:
{ "metafields": [ { "ownerId": "gid://shopify/ProductVariant/1234567890", "namespace": "reskyt", "key": "catalog_price", "type": "json", "value": "{\"price\":19.99,\"customerTag\":\"wholesale\"}" } ]}The value field is a string containing the JSON. Request limits and the number
of metafields per operation are those of the Shopify Admin API.
Removing the price
Section titled “Removing the price”For a variant to stop having an exclusive price, the metafield must be deleted.
Leaving it at 0 or null does not deactivate it.
mutation DeleteCatalogPrice($metafields: [MetafieldIdentifierInput!]!) { metafieldsDelete(metafields: $metafields) { deletedMetafields { key namespace ownerId } userErrors { field message } }}Variables:
{ "metafields": [ { "ownerId": "gid://shopify/ProductVariant/1234567890", "namespace": "reskyt", "key": "catalog_price" } ]}Showing the price in the store
Section titled “Showing the price in the store”Writing the metafield makes the price be charged at checkout. Whether the buyer sees it in the store is a separate matter, and it does not affect the charge.
| Storefront | What to do |
|---|---|
| Liquid theme | The Reskyt theme block shows the price. There is also an equivalent code snippet for custom themes |
| Headless, such as Hydrogen or Oxygen | The frontend reads the metafield from the Storefront API and shows it where appropriate |
Headless storefront and app origin
Section titled “Headless storefront and app origin”In a Liquid theme, the Reskyt theme extension adds the __reskyt_app_origin
attribute to the cart when it detects the app. In a headless storefront that
extension does not run. The team building the frontend must add the same
attribute when it detects that the visitor comes from the app, using the
Storefront API cartAttributesUpdate mutation:
{ "key": "__reskyt_app_origin", "value": "true" }Without that attribute, checkout does not apply the exclusive price.
Summary
Section titled “Summary”- Confirm that a Catalog price rule has been created once from the Reskyt editor in the store.
- Create an app of your own with the
write_productsscope and get its token. - For each variant, look up its identifier by SKU and write
reskyt.catalog_priceas JSON. - If the price has an end date, delete the metafield from your own process.
- If you use a
customerTag, check that the tag is already used in an editor rule. - If the storefront is headless, add the
__reskyt_app_origincart attribute and show the price from your frontend.