Promotions - Marketing & Discounts

Domain: Promotions Location: api/nextango/apps/promotions/ Last Updated: 2026-07-08

Overview

The Promotions app provides two discount mechanisms: customer-entered PromoCodes (standalone or grouped under a PromotionalCampaign) and codeless Promotions evaluated automatically against every cart. Both share the same discount vocabulary (percentage, fixed amount, free shipping, BOGO) and both flow through a shared clamp engine that bounds combined discounts to the cart subtotal. The domain also includes A/B testing (ABTest) and four immutable ledger models backing usage counters and analytics.

Core Documentation

FileDescription
models.mdPromotionalCampaign, PromoCode, ABTest, Promotion, and the ledger models
serializers.mdPromotion API serializers and validation
views.mdCampaign, promo code, and auto-apply promotion API endpoints
services.mdPromotionService, discount calculation, the clamp engine
signals.mdPromotion lifecycle signals and Sanity sync
examples.mdUsage examples and patterns
analytics_service.mdDashboard analytics: promo performance, campaign lift, auto-promotion performance

Key Features

Promotional Campaigns

  • Campaign management - Create, schedule, and manage marketing campaigns
  • Date range control - Start and end dates for campaigns
  • Active status - Enable/disable campaigns dynamically
  • Target products - Specific products eligible for promotion
  • Target stores - Store-specific promotional campaigns
  • Campaign metrics - Track usage, revenue impact, conversion rates

Promo Codes

  • Discount codes - Alphanumeric promo codes for customers
  • Multiple discount types - Percentage, fixed amount, free shipping, BOGO
  • Stackable codes - Apply multiple promo codes per transaction
  • Usage limits - Per-customer and total usage restrictions
  • Date restrictions - Valid date ranges for codes
  • Minimum purchase - Minimum order value requirements

Discount Types

  • Percentage discount - Percentage off (e.g., 20% off)
  • Fixed amount discount - Dollar amount off (e.g., $10 off)
  • Free shipping - Waives the transaction's actual shipping cost
  • BOGO - Buy X get Y free, calculated per-unit from real cart line items
  • BOGO discounted - Buy X get Y at a percentage discount

Targeting & Restrictions

  • Product and collection targeting - Scope a discount to specific inventory or collections via applies_to
  • Store targeting - Limit to specific store locations via applicable_stores / target_stores
  • Date restrictions - Valid date windows for both codes and campaigns
  • Usage restrictions - Total and per-customer usage caps via usage_restrictions
  • Inventory gate - Auto-apply promotions only fire when the store has sufficient stock

A/B Testing

  • Test variants - Compare two variants' conversion rates
  • Thread-safe counters - Impressions and conversions increment via atomic F() expressions
  • Statistical significance - True once combined impressions reach 1000

Performance Tracking

  • Usage analytics - Promo code performance, campaign summary, campaign lift, auto-promotion performance via PromotionAnalyticsService
  • Immutable ledgers - Usage and conversion counters backed by unique-constrained ledger rows

API Endpoints

Every endpoint below is root-level, there is no /promotions/ or /api/ prefix on campaigns, promo-codes, or ab-tests. See Views for the URL grounding.

Campaign Management

EndpointMethodDescription
/campaigns/GET, POSTList, create campaigns
/campaigns/{id}/GET, PUT, PATCH, DELETEIndividual campaign operations
/campaigns/analytics/summary/GETCampaign performance summary

Promo Code Management

EndpointMethodDescription
/promo-codes/GET, POSTList, create promo codes
/promo-codes/{code}/GET, PUT, PATCH, DELETEIndividual code operations, looked up by code string
/promo-codes/validate/POSTValidate a code for checkout, read-only, does not increment usage
/promo-codes/analytics/performance/GETPromo code performance summary

The /promo-codes/validate/ endpoint is public (AllowAny) and does not increment current_usage_count, that increment happens only when the transaction using the code reaches COMPLETED status.

Auto-Apply Promotions

EndpointMethodDescription
/promotions/GETList, retrieve active promotions (read-only)
/promotions/active/GETActive promotions for a store, public, checkout-preview shape

A/B Testing

EndpointMethodDescription
/ab-tests/GET, POSTList, create A/B tests
/ab-tests/{id}/GET, PUT, PATCH, DELETEIndividual test operations
/ab-tests/{id}/record-impression/POSTRecord a variant impression
/ab-tests/{id}/record-conversion/POSTRecord a variant conversion

Full endpoint documentation: views.md

Architecture Highlights

Two discount mechanisms, one clamp engine

PromoCode (customer-entered) and Promotion (auto-apply, no code) share the same discount_effect and applies_to JSON shapes. Both flow through PromotionService's shared clamp engine, which bounds combined discounts so a code and an auto-apply promotion on the same line item cannot jointly discount that line below zero, and the transaction total cannot exceed the subtotal.

Read more: services.md

Stackable and competing discounts

allow_combination=True promotions stack, applying together against a shared per-unit headroom pool. allow_combination=False promotions compete against each other for a single winning slot, chosen by post-clamp contribution.

Read more: services.md

Service layer architecture

Promotion logic is centralized in PromotionService (checkout validation, discount calculation, the clamp engine) and PromotionAnalyticsService (dashboard aggregates). Neither model classes nor views contain discount calculation logic.

Read more: services.md

Signal-based Sanity sync

Signals sync PromotionalCampaign, PromoCode, ABTest, and Promotion to Sanity CMS in real time on save, with multi-layer loop prevention. No deletion propagates outbound: PromotionalCampaign, PromoCode, and ABTest carry delete handlers that are deliberately disabled (log only), and Promotion has no delete handler at all.

Read more: signals.md

Getting Started

1. Understand the models

Start with models.md to understand PromotionalCampaign, PromoCode, ABTest, Promotion, and the four ledger models that back usage counters and analytics.

2. Review the promotion service

Read services.md for checkout validation, the discount calculators, and the clamp engine that reconciles codes and auto-apply promotions against a cart.

3. Explore the API

Check views.md for the campaign, promo code, auto-apply promotion, and A/B test endpoints, including the correct root-level URL mounts.

4. Study the analytics surface

Review analytics_service.md for promo code performance, campaign summary, campaign lift, and auto-promotion performance.

  • Transactions - Discount application to transactions via recalculate_totals()
  • Products - Product-specific and collection-specific targeting
  • Stores - Store-specific campaigns
  • Web Commerce - Storefront checkout promo code flow
  • Integrations - Campaign synchronization

Key Architectural Decisions

  1. Two discount sources, one clamp engine - Codes and auto-apply promotions never jointly over-discount a line
  2. Stackable vs. competing discounts - Explicit allow_combination flag on every discount source
  3. Service layer - Business logic centralized, not in views or models
  4. Immutable ledgers - Usage and conversion counters are backed by unique-constrained ledger rows, safe to retry from Celery
  5. A/B testing support - Thread-safe atomic counters via F() expressions
  6. Store targeting - Localized promotional campaigns and codes
  7. Usage restrictions - Flexible per-code restriction rules

Common Use Cases

  • Percentage sale - Create "20% off" campaign for specific products
  • Dollar discount - "$10 off orders over $50" promotion
  • BOGO promotion - "Buy one get one free" for select items
  • Free shipping - "Free shipping on orders over $25"
  • Flash sale - Time-limited promotional campaign
  • Auto-apply discount - A codeless promotion that applies automatically at checkout
  • A/B testing - Compare 10% vs 15% discount effectiveness
  • Campaign analytics - Track promotion lift against a control period

Next Steps

  1. Start with models.md - Understand the data structure
  2. Then services.md - Learn discount calculation and the clamp engine
  3. Review views.md - Explore API endpoints
  4. Check signals.md - See Sanity sync
  5. Study serializers.md - Understand validation

Was this page helpful?