This endpoint requires a valid JWT Bearer token. Accessible via the API gateway at /v1/commerce/*.
Discounts API
Create and manage discounts, promotions, coupons, and dynamic pricing rules.
All endpoints require a valid Bearer token with pricing:manage scope for write operations. See Authentication for details.
Overview
| Attribute | Value |
|---|---|
| Base Path | /api/v1/discounts |
| Authentication | Bearer Token |
| Required Roles | pos_staff, server, restaurant_staff, restaurant_manager, kitchen, host, tenant_admin, platform_admin, system_admin, super_admin |
Discounts
List Discounts
GET /api/v1/discounts
Query Parameters
| Parameter | Type | Description |
|---|---|---|
location_id | uuid | Filter by location |
type | string | percentage, fixed, bogo, bundle |
status | string | active, scheduled, expired, disabled |
category | string | promotion, employee, loyalty, coupon |
Response
{
"discounts": [
{
"id": "disc_001",
"name": "Happy Hour",
"code": null,
"type": "percentage",
"value": 25,
"category": "promotion",
"status": "active",
"schedule": {
"days": ["monday", "tuesday", "wednesday", "thursday", "friday"],
"start_time": "15:00",
"end_time": "18:00"
},
"applies_to": {
"type": "category",
"categories": ["appetizers", "drinks"]
},
"usage": {
"today": 45,
"total": 2850
}
},
{
"id": "disc_002",
"name": "SAVE10",
"code": "SAVE10",
"type": "percentage",
"value": 10,
"category": "coupon",
"status": "active",
"restrictions": {
"minimum_order": 50.00,
"max_uses_total": 1000,
"max_uses_per_customer": 1
},
"usage": {
"remaining": 856,
"total_used": 144
},
"expires_at": "2026-02-28"
}
],
"total": 15
}
Create Discount
POST /api/v1/discounts
Request Body
{
"name": "Weekend Brunch Special",
"code": null,
"type": "percentage",
"value": 15,
"category": "promotion",
"description": "15% off all brunch items on weekends",
"applies_to": {
"type": "category",
"categories": ["brunch"]
},
"schedule": {
"days": ["saturday", "sunday"],
"start_time": "09:00",
"end_time": "14:00"
},
"restrictions": {
"minimum_order": null,
"maximum_discount": 25.00,
"exclude_items": ["mimosa_bottomless"],
"dine_in_only": true
},
"stacking": {
"allowed": false,
"priority": 1
},
"locations": ["loc_123", "loc_456"],
"starts_at": "2026-02-01",
"expires_at": "2026-03-31"
}
Response
{
"id": "disc_003",
"name": "Weekend Brunch Special",
"status": "scheduled",
"starts_at": "2026-02-01",
"created_at": "2026-01-24T19:30:00Z"
}
Get Discount
GET /api/v1/discounts/{discount_id}
Response
{
"id": "disc_001",
"name": "Happy Hour",
"code": null,
"type": "percentage",
"value": 25,
"category": "promotion",
"description": "25% off appetizers and drinks during happy hour",
"status": "active",
"applies_to": {
"type": "category",
"categories": ["appetizers", "drinks"],
"excluded_items": ["premium_whiskey", "wagyu_sliders"]
},
"schedule": {
"days": ["monday", "tuesday", "wednesday", "thursday", "friday"],
"start_time": "15:00",
"end_time": "18:00",
"timezone": "America/New_York"
},
"restrictions": {
"minimum_order": null,
"maximum_discount": 50.00,
"dine_in_only": true,
"bar_area_only": false
},
"stacking": {
"allowed": false,
"priority": 1,
"exclusive_with": ["disc_002", "disc_003"]
},
"locations": ["loc_123"],
"analytics": {
"usage_today": 45,
"usage_this_week": 285,
"usage_total": 2850,
"revenue_impact": -12500.00,
"avg_check_with_discount": 42.50,
"avg_check_without_discount": 38.00
},
"created_at": "2025-06-01T00:00:00Z",
"updated_at": "2026-01-15T00:00:00Z"
}
Update Discount
PUT /api/v1/discounts/{discount_id}
Delete Discount
DELETE /api/v1/discounts/{discount_id}
Coupon Codes
By default, discounts cannot be stacked (combined on the same order). Set stacking.allowed: true and configure stacking.priority to control which discounts can combine. Discounts listed in stacking.exclusive_with will never stack together regardless of the allowed setting.
Generate Coupon Code
POST /api/v1/discounts/coupons
Request Body
{
"prefix": "VIP",
"type": "percentage",
"value": 20,
"quantity": 100,
"restrictions": {
"minimum_order": 75.00,
"max_uses_per_code": 1,
"first_order_only": false
},
"valid_from": "2026-02-01",
"valid_until": "2026-02-28",
"campaign": "february_vip_promotion"
}
Response
{
"batch_id": "batch_001",
"codes_generated": 100,
"prefix": "VIP",
"sample_codes": ["VIP-A1B2C3", "VIP-D4E5F6", "VIP-G7H8I9"],
"download_url": "https://...",
"valid_from": "2026-02-01",
"valid_until": "2026-02-28"
}
Validate Coupon Code
POST /api/v1/discounts/coupons/validate
Request Body
{
"code": "SAVE10",
"order": {
"subtotal": 85.00,
"items": [
{"id": "item_001", "category": "entrees", "price": 45.00},
{"id": "item_002", "category": "appetizers", "price": 15.00},
{"id": "item_003", "category": "drinks", "price": 25.00}
]
},
"customer_id": "cust_abc",
"location_id": "loc_123"
}
Response
{
"valid": true,
"code": "SAVE10",
"discount": {
"type": "percentage",
"value": 10,
"calculated_amount": 8.50,
"applies_to_subtotal": 85.00
},
"restrictions_met": {
"minimum_order": true,
"customer_eligible": true,
"within_validity": true,
"uses_remaining": true
},
"message": "Coupon applied: 10% off your order"
}
Redeem Coupon Code
POST /api/v1/discounts/coupons/redeem
Request Body
{
"code": "SAVE10",
"order_id": "ord_12345",
"customer_id": "cust_abc"
}
Promotions
List Active Promotions
GET /api/v1/discounts/promotions/active
Query Parameters
| Parameter | Type | Description |
|---|---|---|
location_id | uuid | Filter by location |
order_type | string | dine_in, takeout, delivery |
time | datetime | Check for specific time |
Response
{
"promotions": [
{
"id": "disc_001",
"name": "Happy Hour",
"description": "25% off appetizers and drinks",
"type": "percentage",
"value": 25,
"applies_to": ["appetizers", "drinks"],
"active_now": true,
"ends_at": "2026-01-24T18:00:00Z"
},
{
"id": "disc_005",
"name": "Free Delivery Friday",
"description": "Free delivery on orders over $30",
"type": "free_delivery",
"minimum_order": 30.00,
"active_now": true,
"order_types": ["delivery"]
}
]
}
Create BOGO Promotion
POST /api/v1/discounts/promotions/bogo
Request Body
{
"name": "BOGO Appetizers",
"description": "Buy one appetizer, get one free",
"buy": {
"quantity": 1,
"category": "appetizers"
},
"get": {
"quantity": 1,
"category": "appetizers",
"discount": 100,
"max_value": 15.00
},
"schedule": {
"days": ["tuesday"],
"all_day": true
},
"restrictions": {
"dine_in_only": true,
"limit_per_table": 2
},
"locations": ["loc_123"],
"starts_at": "2026-02-01"
}
Create Bundle Deal
POST /api/v1/discounts/promotions/bundles
Request Body
{
"name": "Date Night Special",
"description": "2 entrees, 1 appetizer, 1 dessert for $75",
"bundle_price": 75.00,
"items": [
{"category": "entrees", "quantity": 2, "max_price": 35.00},
{"category": "appetizers", "quantity": 1, "max_price": 15.00},
{"category": "desserts", "quantity": 1, "max_price": 12.00}
],
"savings_display": "Save up to $22",
"schedule": {
"days": ["friday", "saturday"],
"start_time": "17:00",
"end_time": "22:00"
},
"dine_in_only": true
}
Employee Discounts
Get Employee Discount
GET /api/v1/discounts/employee/{employee_id}
Response
{
"employee_id": "emp_001",
"discount": {
"type": "percentage",
"value": 50,
"applies_to": "all_items",
"exclusions": ["alcohol", "gift_cards"]
},
"restrictions": {
"max_per_day": 1,
"max_value_per_use": 30.00,
"on_shift_only": false,
"requires_manager_approval": false
},
"family_discount": {
"enabled": true,
"percentage": 25,
"requires_employee_present": true
},
"usage_this_month": {
"count": 8,
"total_value": 125.00
}
}
Apply Employee Discount
POST /api/v1/discounts/employee/apply
Request Body
{
"employee_id": "emp_001",
"order_id": "ord_12345",
"discount_type": "employee",
"pin": "1234"
}
Dynamic Pricing
Get Dynamic Pricing Rules
GET /api/v1/discounts/dynamic-pricing
Response
{
"rules": [
{
"id": "dyn_001",
"name": "Surge Pricing - Peak Hours",
"type": "surge",
"adjustment": {
"type": "percentage",
"value": 15
},
"triggers": {
"occupancy_above": 90,
"wait_time_above_minutes": 30
},
"applies_to": ["delivery"],
"cap": {
"max_adjustment": 25
},
"status": "active"
},
{
"id": "dyn_002",
"name": "Slow Period Discount",
"type": "demand",
"adjustment": {
"type": "percentage",
"value": -10
},
"triggers": {
"occupancy_below": 30,
"time_range": ["14:00", "16:00"]
},
"applies_to": ["dine_in"],
"status": "active"
}
]
}
Create Dynamic Pricing Rule
POST /api/v1/discounts/dynamic-pricing
Analytics
Get Discount Performance
GET /api/v1/discounts/{discount_id}/analytics
Query Parameters
| Parameter | Type | Description |
|---|---|---|
period | string | day, week, month |
Response
{
"discount_id": "disc_001",
"name": "Happy Hour",
"period": "month",
"usage": {
"total_uses": 2850,
"unique_customers": 1420,
"repeat_rate": 0.35
},
"financial": {
"gross_discount_value": 12500.00,
"incremental_revenue": 8500.00,
"net_impact": -4000.00,
"avg_discount_per_use": 4.39
},
"orders": {
"total_orders_with_discount": 2850,
"avg_order_value_with": 42.50,
"avg_order_value_without": 38.00,
"items_per_order_with": 4.2,
"items_per_order_without": 3.1
},
"conversion": {
"viewed": 5200,
"applied": 2850,
"conversion_rate": 0.55
},
"top_items": [
{"item": "Loaded Nachos", "count": 450},
{"item": "Wings", "count": 380},
{"item": "Margarita", "count": 520}
]
}
Webhooks
| Event | Description |
|---|---|
discount.created | New discount created |
discount.activated | Discount became active |
discount.expired | Discount expired |
discount.applied | Discount applied to order |
coupon.redeemed | Coupon code redeemed |
coupon.exhausted | Coupon uses exhausted |
Error Responses
| Status | Code | Description |
|---|---|---|
| 400 | invalid_code | Coupon code invalid |
| 400 | minimum_not_met | Minimum order not met |
| 404 | discount_not_found | Discount ID not found |
| 409 | already_redeemed | Coupon already used by customer |
| 410 | discount_expired | Discount has expired |
| 422 | not_stackable | Cannot stack with other discounts |
Related Documentation
- Orders API - Order management
- Marketing API - Marketing campaigns
- Loyalty API - Loyalty rewards