You can now set Promotions in Crystallize via the Shop API. Learn more about what a promotion entails. Note that you can also set up promotions in the Crystallize App itself.
All operations on this page run on the /{tenant}/cart/admin endpoint and require an access token with the cart:admin scope.
Let’s start off with looking at a mutation, followed by going deeper into each of the fields in the mutation:
mutation CreatePromotion {
setPromotions(
input: [
{
identifier: "summer_sale_2024"
name: "Summer Sale 2024"
description: "Get amazing discounts on summer essentials!"
periods: [
{ start: "2024-06-01T00:00:00Z", end: "2024-08-31T23:59:59Z" }
]
triggers: {
minValue: 50.00
customerGroups: ["crystal_customers"]
}
targets: {
topics: ["summer_wear"]
exclusions: { skus: ["beach-shorts-blue", "beach-shorts-white"] }
}
limitations: {
maxUsage: 1000
maxUsagePerCustomer: 2
cumulativeDiscount: false
repeatable: true
}
mechanism: {
type: Percentage
value: 20.0
}
}
]
) {
identifier
name
description
# Add other fields you want to return after creation
}
}mutation CreatePromotion {
setPromotions(
input: [
{
identifier: "summer_sale_2024"
name: "Summer Sale 2024"
description: "Get amazing discounts on summer essentials!"
periods: [
{ start: "2024-06-01T00:00:00Z", end: "2024-08-31T23:59:59Z" }
]
triggers: {
minValue: 50.00
customerGroups: ["crystal_customers"]
}
targets: {
topics: ["summer_wear"]
exclusions: { skus: ["beach-shorts-blue", "beach-shorts-white"] }
}
limitations: {
maxUsage: 1000
maxUsagePerCustomer: 2
cumulativeDiscount: false
repeatable: true
}
mechanism: {
type: Percentage
value: 20.0
}
}
]
) {
identifier
name
description
# Add other fields you want to return after creation
}
}The setPromotions mutation takes a list of promotions, saves the full list, and returns it. It is an override: any existing promotion that is not included in the input is removed.
Required field to specify the identifier of the promotion in Crystallize.
This is an optional field for providing the name of the promotion.
Another optional field to add a description for your promotion.
An array of periods a promotion should be valid for. Each period has two fields:
If no periods are specified, the promotion takes effect immediately and is permanent until modified or deleted.
A mechanism specifies the type of discount you would like to apply. It takes two values: type and value. Value is always a floating point number, and type can be one of the following:
For an XforY promotion, set the mechanism value to 0 and provide the X and Y quantities in the triggers.xForY field (see Triggers).
This is a list of conditions that will trigger the promotion to be applied to a cart. It can be one or more of the following:
You have triggers to specify the conditions for when a promotion is applied. Targets define which item(s) the promotion is applied to. Targets share some fields with triggers:
Targets are not used with the XforY mechanism, which discounts the cheapest qualifying item(s) automatically.
You might want to add some limitations to your promotions, and that is exactly what the limitations field does. You can set the following limitations:
Once created, promotions can be retrieved and inspected with the following admin queries:
query Promotions {
promotions {
identifier
name
mechanism { type value }
}
promotion(identifier: "summer_sale_2024") {
identifier
name
periods { start end }
}
promotionUsage {
count(identifier: "summer_sale_2024")
perCustomer(identifier: "summer_sale_2024")
}
}query Promotions {
promotions {
identifier
name
mechanism { type value }
}
promotion(identifier: "summer_sale_2024") {
identifier
name
periods { start end }
}
promotionUsage {
count(identifier: "summer_sale_2024")
perCustomer(identifier: "summer_sale_2024")
}
}The Promotion Price Interface (PPI) lets you delegate promotion pricing to an external endpoint. Crystallize calls the configured URL to resolve promotion prices, optionally refreshing them on a schedule.
Configure it with the setPPI mutation:
Retrieve the current configuration with the ppi query.
mutation ConfigurePPI {
setPPI(url: "https://example.com/ppi", refreshFrequency: 24) {
url
refreshFrequency
}
}
query GetPPI {
ppi {
url
refreshFrequency
}
}mutation ConfigurePPI {
setPPI(url: "https://example.com/ppi", refreshFrequency: 24) {
url
refreshFrequency
}
}
query GetPPI {
ppi {
url
refreshFrequency
}
}The cart admin endpoint also lets you list and filter carts across your tenant — useful for analytics and for finding abandoned carts. The carts query returns the matching carts together with a summary that includes totals and aggregates.
You can filter and paginate carts with the following arguments:
Abandoned carts are not a separate query — abandonment is a cart state. To list them, filter by state with states: [abandoned].
query AbandonedCarts {
carts(
states: [abandoned]
limit: 50
sortBy: { updatedAt: DESC }
) {
summary {
totalHits
totalGross
}
hits {
id
state
updatedAt
}
}
}query AbandonedCarts {
carts(
states: [abandoned]
limit: 50
sortBy: { updatedAt: DESC }
) {
summary {
totalHits
totalGross
}
hits {
id
state
updatedAt
}
}
}Watch our deep dive on building and configuring promotions with the Shop API.