Alertes concernant la suppression des codes de réduction
Quand utiliser ce workflow ?
Utilisez ce workflow lorsqu’un code de réduction est supprimé et que vous avez besoin d’un relevé immédiat des éléments concernés. Il vous permet de recenser le code supprimé, son nombre d’utilisations, les détails de la campagne, ainsi qu’un récapitulatif que vous pouvez envoyer au service financier ou aux opérations via Slack.
Cette fonctionnalité s'avère particulièrement utile en cas de fuite de codes d'influenceurs, de divulgation imprévue de bons de réduction ou dans toute situation où vous avez besoin d'un rapport d'impact documenté après la suppression d'un code.
Ce que vous allez créer
- Un workflow Slack qui reçoit une variable «
text» via un webhook et publie ce même texte dans un canal Slack. - Un workflow Shopify Flow déclenché par la suppression d'un code de réduction.
- Une étape de traitement « Obtenir la remise » qui recherche la remise à l'aide de l'identifiant fourni par le déclencheur.
- Une étape « Exécuter le code » qui génère un rapport de suppression.
- Une étape « Requête HTTP » qui envoie le rapport vers Slack.
Avant de commencer
- Assurez-vous que l'application est bien installée et qu'elle apparaît dans l'interface d'administration d'Shopify, sous « **Apps » > « Workflow Trigger Extensions **».
- Assurez-vous d'avoir accès à Shopify Flow.
- Créez ou sélectionnez un canal Slack dans lequel les alertes doivent s'afficher.
- Créez un workflow Slack qui se déclenche par un webhook et accepte un champ «
text».
Guide vidéo
Vous pouvez également visionner la vidéo complète de la mise en œuvre.
Commencez par créer le workflow Slack
Dans Slack, créez un workflow dont le déclencheur est « Commence par un webhook ». Ajoutez une variable de texte nommée « text », puis ajoutez l'étape « Message » qui envoie un message au canal de votre choix en utilisant cette même variable « text ».

Accorder un accès à tarif réduit dans l'Workflow Trigger Extensions
Avant que le déclencheur n'apparaisse dans « Shopify Flow », activez l'accès aux remises dans l'application.
Dans l'interface d'administration d'Shopify, accédez à Applications > Workflow Trigger Extensions.
Dans le tableau de bord, recherchez la liste de contrôle de configuration.
Activez l'option « Accorder l'accès aux remises ». Cela active les déclencheurs liés aux remises, tels que « Remise créée », « Remise mise à jour », « Remise supprimée », « Code de remise ajouté », « Code de remise supprimé » et « Remise expirée ».

Construire l’Shopify Flow
Ouvrez Shopify Flow et créez un nouveau workflow.
Recherchez les déclencheurs liés aux réductions et sélectionnez « Code de réduction supprimé » dans «Workflow Trigger Extensions ».

Ajoutez l'action « Obtenir les données de réduction » de l'Shopify.
Dans « Sélectionner une requête pour filtrer les données », choisissez « Avancé ». Dans « Modifier la requête », utilisez l'ID de remise issu du déclencheur et supprimez le préfixe GID « Shopify » :
id:"{{discount.id|remove:'gid://shopify/DiscountCodeNode/'}}"
Ajoutez une étape « Exécuter le code » après « Récupérer les données sur les réductions ».
Utilisez la requête GraphQL suivante dans les champs de saisie « Sélectionner » des étapes précédentes :
query {
redeemCode {
code
usageCount
bulkRemoval
}
getDiscountData {
discount {
... on DiscountCodeBasic {
title
startsAt
endsAt
asyncUsageCount
codes {
code
asyncUsageCount
}
customerGets {
value {
... on DiscountAmount {
amount { amount currencyCode }
}
... on DiscountPercentage {
percentage
}
}
}
}
}
}
}Définissez les sorties comme suit :
"Output of discount code removal report"
type Output {
"Discount code that was removed"
code: String!
"Discount campaign title"
title: String!
"Campaign start date"
startsAt: String!
"Campaign end date"
endsAt: String!
"Number of times it was redeemed"
usageCount: Int!
"Total discount impact as a formatted string"
totalImpact: String!
"When it was removed"
removedAt: String!
"Full summary for alerts"
summary: String!
}Ensuite, collez ce code dans le champ « Écrire le code » :
export default function main(input) {
// ── Trigger: removed code ──
const removedCode = input?.redeemCode?.code ?? "Unknown";
const usageCount = input?.redeemCode?.usageCount ?? null; // null = bulk removal or unknown
const bulkRemoval = input?.redeemCode?.bulkRemoval ?? false;
const currency = input?.shop?.currencyCode ?? "";
const shopName = input?.shop?.name ?? "(not bound)";
const removedAt = new Date().toISOString();
// ── Find matching discount ──
const dataItems = input?.getDiscountData ?? [];
let matchedDiscount = null;
for (const item of dataItems) {
const d = item?.discount?.DiscountCodeBasic ?? item?.discount;
if (!d) continue;
const codesArray = Array.isArray(d.codes)
? d.codes.map(c => (typeof c === "string" ? c : c?.code ?? ""))
: [];
if (codesArray.includes(removedCode)) {
matchedDiscount = d;
break;
}
}
// fallback to first entry if removed code already gone from list
if (!matchedDiscount && dataItems.length > 0) {
matchedDiscount =
dataItems[0]?.discount?.DiscountCodeBasic ?? dataItems[0]?.discount ?? null;
}
const title = matchedDiscount?.title ?? "Untitled";
const startsAt = matchedDiscount?.startsAt ?? "N/A";
const endsAt = matchedDiscount?.endsAt ?? "None (no expiry)";
const totalUsageCount = matchedDiscount?.asyncUsageCount ?? null;
// ── Discount value ──
const valueWrapper = matchedDiscount?.customerGets?.value ?? {};
const pctObj = valueWrapper?.DiscountPercentage ?? null;
const amtObj = valueWrapper?.DiscountAmount ?? valueWrapper?.amount ?? null;
let discountLabel = "Unknown";
if (pctObj?.percentage != null) {
discountLabel = `${(pctObj.percentage * 100).toFixed(0)}% off`;
} else if (amtObj?.amount != null) {
const cur = amtObj.currencyCode || currency;
discountLabel = `${cur} ${parseFloat(amtObj.amount).toFixed(2)} off`;
}
// ── This code's impact ──
let thisCodeImpact;
if (bulkRemoval) {
thisCodeImpact = `Not available - removed as part of bulk deletion`;
} else if (usageCount === null) {
thisCodeImpact = `Could not be determined`;
} else if (pctObj?.percentage != null) {
thisCodeImpact = `${discountLabel} × ${usageCount} use(s)`;
} else if (amtObj?.amount != null) {
const perUse = parseFloat(amtObj.amount);
const cur = amtObj.currencyCode || currency;
thisCodeImpact = `${cur} ${(perUse * usageCount).toFixed(2)} (${cur} ${perUse.toFixed(2)} × ${usageCount} uses)`;
} else {
thisCodeImpact = "Unknown";
}
// ── Campaign-wide impact using asyncUsageCount on the discount ──
let campaignImpact;
if (totalUsageCount === null) {
campaignImpact = "Not available";
} else if (pctObj?.percentage != null) {
campaignImpact = `${discountLabel} × ${totalUsageCount} total use(s) across all codes`;
} else if (amtObj?.amount != null) {
const perUse = parseFloat(amtObj.amount);
const cur = amtObj.currencyCode || currency;
campaignImpact = `${cur} ${(perUse * totalUsageCount).toFixed(2)} total (${cur} ${perUse.toFixed(2)} × ${totalUsageCount} uses)`;
} else {
campaignImpact = "Unknown";
}
// ── Per-code breakdown ──
const codesNodes = Array.isArray(matchedDiscount?.codes)
? matchedDiscount.codes
: [];
const allCodes = codesNodes
.map(c => (typeof c === "string" ? c : c?.code ?? ""))
.filter(Boolean);
const codesRoster = codesNodes.length
? codesNodes.map(c => {
const code = typeof c === "string" ? c : c?.code ?? "";
const used = c?.asyncUsageCount ?? "?";
const flag = code === removedCode ? " ← REMOVED" : "";
return `${code} (used ${used}×)${flag}`;
}).join("\n ")
: "N/A";
const usageCountLabel = bulkRemoval
? "N/A (bulk removal)"
: usageCount !== null
? String(usageCount)
: "Unknown";
const summary =
`DISCOUNT CODE REMOVED\n` +
`─────────────────────\n` +
`Store: ${shopName}\n` +
`Code removed: ${removedCode}\n` +
`Bulk removal: ${bulkRemoval ? "Yes" : "No"}\n` +
`Campaign: ${title}\n` +
`Discount: ${discountLabel}\n` +
`Valid from: ${startsAt}\n` +
`Valid until: ${endsAt}\n` +
`\n` +
`THIS CODE\n` +
` Times used: ${usageCountLabel}\n` +
` Impact: ${thisCodeImpact}\n` +
`\n` +
`CAMPAIGN TOTAL\n` +
` Total uses: ${totalUsageCount ?? "Unknown"}\n` +
` Total impact: ${campaignImpact}\n` +
`\n` +
`CODE BREAKDOWN\n` +
` ${codesRoster}\n` +
`\n` +
`Removed at: ${removedAt}`;
const safeSummary = summary
.replace(/\\/g, "\\\\") // escape backslashes first
.replace(/"/g, '\\"') // escape quotes
.replace(/\n/g, "\\n") // escape newlines
.replace(/\r/g, "\\r");
return {
code: removedCode,
bulkRemoval,
title,
discountLabel,
startsAt,
endsAt,
usageCount: usageCount ?? 0,
usageCountLabel,
thisCodeImpact,
totalUsageCount: totalUsageCount ?? 0,
campaignImpact,
allCodes,
codesRoster,
removedAt,
summary: safeSummary,
};
}
Ajoutez l'action « Requête HTTP ». Dans le champ « Variables » ou « Remplacement du corps (JSON) », envoyez le résumé renvoyé par l'étape « Exécuter le code » :
{
"body": {
"text": "{{runCode.summary}}"
}
}Cette opération publie le résumé généré dans le workflow du webhook Slack à l'aide de la variable « text ».

Enregistrez le flux de travail une fois que toutes les étapes sont en place.
Pour tester, supprimez un code de réduction de votre campagne promotionnelle et vérifiez que le canal Slack reçoit bien le message récapitulatif.

Contenu de l'alerte Slack
- Le code de réduction supprimé
- Qu'il s'agisse d'un enlèvement en gros
- Le titre de la campagne
- La valeur actualisée
- Dates de début et de fin
- Fréquence d'utilisation du code supprimé
- Impact estimé de la suppression du code
- Utilisation et impact à l'échelle de la campagne
- Une ventilation par code, lorsqu'elle est disponible
- L'horodatage correspondant au moment où la suppression a été effectuée
Dépannage
Retournez dans « Applications » > « Workflow Trigger Extensions » et vérifiez que l'option « Accorder l'accès à la remise » est activée. Les déclencheurs de remise n'apparaissent qu'une fois l'accès accordé.
Vérifiez bien la requête avancée dans « Modifier la requête ». L'ID doit être saisi exactement comme indiqué :
id:"{{discount.id|remove:'gid://shopify/DiscountCodeNode/'}}"Assurez-vous que le workflow Slack démarre par un webhook, que l'URL du webhook correspond à celle utilisée par votre requête HTTP et que le corps de la requête inclut le champ « text ».
Certaines suppressions peuvent s'inscrire dans le cadre d'une action groupée ou ne pas fournir toutes les informations relatives à l'utilisation. Dans ces cas-là, le récapitulatif fournira tout de même les meilleures informations disponibles et indiquera clairement les valeurs manquantes.
Bonnes pratiques
- Envoyez ces alertes vers un canal dédié aux finances ou aux opérations plutôt que vers un canal d'assistance général.
- Choisissez un nom précis pour le flux de travail, par exemple « Code de réduction supprimé », afin qu'il soit facile à identifier dans Shopify Flow et sur Slack.
- Commencez par effectuer un test avec un code de réduction hors environnement de production afin de vérifier le format du message et le fonctionnement du webhook.

