Promotions API
This guide covers how external integrators read promotion data from DRE.
/pos-integration/Promotions endpointOlder notes referenced a read-only GET /pos-integration/Promotions OData
collection. That path does not exist. The POS Service (/pos) is
action-based (POST /pos/v2/evaluate, /confirm, …) and exposes no
readable Promotions entity. To read promotion master data, use the Admin
Service OData V4 collection GET /admin/Promotions, documented here. See the
Admin Service reference for the full entity
catalogue.
Fetching Active Promotions
Retrieve all currently active promotions via the Admin Service (OData V4):
GET /admin/Promotions?$filter=status eq 'ACTIVE'
status is a code-list value (DRAFT, ACTIVE, INACTIVE, EXPIRED,
ARCHIVED, PENDING_APPROVAL, APPROVED, REJECTED, PILOT). The
temporarily-disabled status is INACTIVE. Filter on the literal you need.
Selecting Fields
Trim the payload to the fields your POS needs with $select:
GET /admin/Promotions?$filter=status eq 'ACTIVE'&$select=ID,name,status,type,validFrom,validTo
Expanding Related Data
Promotions expose these navigation properties (see the Admin Service reference for the full list):
| Navigation property | Target | Description |
|---|---|---|
posGroups | PromotionPosGroups | POS group assignments (empty = all POS groups) |
calculationConfig | CalculationConfigs | Calculation mode, rounding, precision |
campaign | Campaigns | Optional campaign grouping |
Expand them in a single request:
GET /admin/Promotions?$filter=status eq 'ACTIVE'&$expand=posGroups,calculationConfig,campaign
$expand on PromotionsDRE uses a flat, per-promotion condition model — there is no
conditionGroups navigation and no nested condition tree.
Conditions (and DiscountActions) point back to their promotion via a
promotion association, so you query them with a filter, not an expand:
GET /admin/Conditions?$filter=promotion_ID eq <promotionID>
GET /admin/DiscountActions?$filter=promotion_ID eq <promotionID>
Pagination
For large result sets, use server-driven pagination with $top / $skip:
GET /admin/Promotions?$top=50&$skip=0
GET /admin/Promotions?$top=50&$skip=50
Add $count=true to obtain the total row count alongside the page.
Polling Strategy
Recommended Approach
- Poll interval: Every 5–15 minutes depending on business requirements.
- Filter on
modifiedAt: Only fetch promotions changed since your last sync.modifiedAtis the managed timestamp on every entity (CAPmanagedaspect,@sap/cds/common); there is nochangedAtfield.
GET /admin/Promotions?$filter=modifiedAt gt 2026-05-01T00:00:00Z&$orderby=modifiedAt
- Cache locally: Store promotion data in your POS system and sync incrementally on each poll.
Error Handling
| HTTP Status | Meaning | Action |
|---|---|---|
200 | Success | Process response |
401 | Unauthorized | Refresh auth token |
403 | Forbidden | Check role/scope assignment |
429 | Rate limited | Back off and retry |
500 | Server error | Retry with exponential backoff |
Response Format
All responses follow OData v4 JSON format:
{
"@odata.context": "$metadata#Promotions",
"value": [
{
"ID": "b1a2c3d4-0001-4000-8000-000000000001",
"name": "Summer Sale 2025",
"status": "ACTIVE",
"type": "RECEIPT",
"validFrom": "2025-06-01T00:00:00Z",
"validTo": "2025-08-31T23:59:59Z",
"modifiedAt": "2025-05-28T09:14:02Z"
}
]
}
Writing promotion data via the Public API
This page covers reading promotion master data. To write from an
external system (create promotions, campaigns, templates, pilot configs, or
bulk-import master data), use the stable Public API
at /api/v1 — not the Admin Service (which is Fiori/draft-coupled).
Relevant writable paths for promotion-centric integrations:
| Goal | Path | Scope |
|---|---|---|
| Create / update a promotion | POST / PATCH /api/v1/Promotions | PublicApi.Write |
| Bulk-import promotions | POST /api/v1/Promotions:bulk-import (track via ImportJobs) | PublicApi.Write |
| Create a campaign and attach promotions | POST /api/v1/Campaigns + Campaigns(<ID>)/addPromotions | PublicApi.Write |
| Instantiate a promotion from a template | POST /api/v1/createPromotionFromTemplate | PublicApi.Write |
| Configure an A/B pilot for a promotion | POST /api/v1/PromotionPilotConfigs | PublicApi.Write |
Every writable /api/v1 collection is tenant-scoped — a caller only sees and
writes its own tenant's rows (cross-tenant access returns 404). See the
Public API reference for the full
collection catalogue, read/write matrix, action contracts, and tenant-isolation
rules, and Article Import API for the
bulk-import body schema.
See Also
- Public API reference —
/api/v1collection catalogue, read/write matrix, OAuth scopes, tenant isolation, and the Campaigns / PromotionTemplates / PromotionPilotConfigs write paths. - Admin Service reference — full entity catalogue, navigation properties, draft lifecycle, and custom actions.
- API Reference overview — all six DRE services and their base paths.