External Promotions API
Introduction
The External Promotions API allows Distributors and Sellers to check which vendor promotions are eligible or already applied for a given Offer configuration or an existing Subscription. This endpoint returns the vendor's promotion data as received, giving full visibility into what the vendor makes available.
This gives Distributors and Sellers full visibility into vendor-side promotion eligibility, discount details, validity periods, and promotion identifiers, independent of the Platform's own calculated pricing result.
This API supports Microsoft Growth Margin scenarios. The Category field in the response indicates whether a promotion record is a standard promotion ("Default") or a growth margin ("GrowthMargin"). For more details on how growth margins work within the Platform, see What is Growth Margin and how does it work?
In this article:
- Response structure
- Error handling
- Organization scope and filtering
- Scenarios
- Example request and response
- Summary
Overview of the endpoint
The POST {base_url}/api/v1/price/externalPromotions endpoint retrieves vendor promotion information for either:
A new purchase configuration (
ConfigurationType: "Offer") — checks which promotions the vendor considers eligible for a specific offer configuration, orAn existing Subscription (
ConfigurationType: "Subscription") — checks promotions already applied to the subscription, in addition to any newly eligible promotions the vendor returns for that scope.
Key capabilities of this endpoint:
Retrieve vendor promotion eligibility for a new offer configuration before purchase
Retrieve promotions already applied to an existing subscription, alongside any newly eligible ones
Access Organization-specific promotion benefits, filtered by the caller's role
Receive the vendor's response with no fields dropped, including any vendor-specific AdditionalInformation
Request details
Method and route:
POST {base_url}/api/v1/price/externalPromotions
Headers
-
Authorization
- Bearer token (existing authentication)
-
X-Referer
- The URL of the requesting Platform
-
X-On-Behalf-Of
- The identifier of the user on whose behalf the request is made
-
Content-Type
- application/json
Request body
| Field | Type | Required | Description |
|---|---|---|---|
| ConfigurationType | String ("Offer" | "Subscription") | Yes | Determines whether the request checks a new offer configuration or an existing subscription |
| OfferId | String (GUID) or null | Conditional | Required when ConfigurationType is "Offer". Must not be provided together with a mismatching SubscriptionId |
| SubscriptionId | String (GUID) or null | Conditional | Required when ConfigurationType is "Subscription". If provided alone, the Platform resolves the offer, customer, and tenant from the subscription |
| FormData | String (JSON) | Conditional | Required when ConfigurationType is "Offer". Optional for "Subscription" — if omitted, the last active FormData on the subscription is used |
Validation rules:
At least one of OfferId or SubscriptionId must be present, otherwise the request fails
For ConfigurationType: "Offer", OfferId is required; providing SubscriptionId at the same time results in an error
For ConfigurationType: "Subscription", SubscriptionId is required
If both OfferId and SubscriptionId are provided but do not correspond to the same offer, an error is returned
Response structure
A successful request returns a 200 OK response with the following top-level structure:
| Field | Type | Description |
|---|---|---|
| IsSuccess | Boolean | Whether the request was processed successfully |
| ErrorMessage | String or null | Top-level error message, if the request as a whole could not be processed |
| Items | Array | Promotion records. Can contain zero, one, or multiple entries |
Item fields
| Field | Type | Description |
|---|---|---|
| LineItemNumber | Integer | Line item this promotion record applies to |
| Category | String | The promotion category (for example, "Default" for a standard promotion or "GrowthMargin" for a growth margin) |
| IsEligible | Boolean | Whether the promotion is eligible for this configuration |
| DiscountPercentage | Decimal or null | Discount as a percentage. Already expressed as a percentage value — do not multiply again |
| VendorPromotionId | String | The vendor's identifier for the promotion |
| EffectiveFrom | DateTime | Start date of the promotion, as returned by the vendor |
| EffectiveTill | DateTime | End date of the promotion, as returned by the vendor |
| ErrorMessage | String or null | Item-level, vendor-provided error message, if any. Distinct from the top-level ErrorMessage |
| PromotionDetails | Array | Organization-specific promotion details (see filtering below) |
| AdditionalInformation | Object or null | Free-form vendor data, passed through as received |
PromotionDetails fields
| Field | Type | Description |
|---|---|---|
| OrganizationId | String (GUID) | The Organization this promotion detail applies to |
| DiscountType | String | For example, "Percentage" |
| DiscountValue | Decimal | The discount value for this Organization |
When there is no eligible promotion for the vendor/offer, the endpoint returns 200 OK with an empty Items array. This is a normal response, not an error.
Error handling
Errors are returned using standard HTTP status codes with a ProblemDetails body that describes what went wrong.
| Status | When | Detail says |
|---|---|---|
| 400 | Neither OfferId nor SubscriptionId provided | One of OfferId or SubscriptionId is required |
| 400 | OfferId provided while ConfigurationType is "Subscription", or SubscriptionId provided while ConfigurationType is "Offer" | The identifier provided does not match the selected ConfigurationType |
| 400 | OfferId and SubscriptionId both provided but do not correspond to the same offer | OfferId and SubscriptionId do not match |
| 400 | FormData missing for ConfigurationType "Offer" | FormData is required when ConfigurationType is "Offer" |
| 401 | No or invalid token | — |
Organization scope and filtering
Access to this endpoint is restricted to the following roles: Super Admin, Distributor Admin, and Seller Admin.
All roles authenticate using the same mechanism: a Bearer token passed in the Authorization header, together with the X-Referer (identifying the Platform) and X-On-Behalf-Of (identifying the user). The token carries the caller's existing Platform permission scope, and the endpoint uses that scope to filter PromotionDetails.
The filtering rules are:
If the vendor returns a PromotionDetails entry with OrganizationId set to null, it is passed through to any authorized caller.
If the vendor returns a PromotionDetails entry with an OrganizationId, it is included only if the calling user has Seller Admin or Distributor Admin permission for that Organization.
Entries for Organizations outside the caller's scope are silently dropped from the array.
Scenarios
The following table describes expected results for common scenarios:
| # | Case | Result |
|---|---|---|
| 1 | ConfigurationType "Offer", valid OfferId and FormData | 200, Items with vendor promotion data |
| 2 | ConfigurationType "Subscription", valid SubscriptionId, no FormData | 200, FormData resolved from last active subscription state |
| 3 | Neither OfferId nor SubscriptionId provided | 400 |
| 4 | OfferId provided together with SubscriptionId for ConfigurationType "Offer" | 400 |
| 5 | Same promotion, called by Seller Admin vs Distributor Admin | 200 for both — Seller Admin sees PromotionDetails scoped to their own Organization(s) only; Distributor Admin sees those plus additional Organization-scoped entries the Distributor manages |
| 6 | No eligible promotion for vendor/offer | 200, empty Items array |
| 7 | No or invalid token | 401 |
Example request and response
Example request
POST {base_url}/api/v1/price/externalPromotions
Headers:
X-Referer: https://{your-platform-domain}/
X-On-Behalf-Of: {user-id}
Content-Type: application/json
Authorization: Bearer {your-access-token}
Body:
{
"ConfigurationType": "Offer",
"OfferId": "11111111-2222-3333-4444-555555555555",
"SubscriptionId": null,
"FormData": "{}"
}
Example response — Seller Admin role
Indirect Model (multi-tier) Seller — by default, a Seller Admin in the Indirect Model sees promotions but not Growth Margin. PromotionDetails include entries where OrganizationId is null and entries scoped to the Seller's own Organization(s):
{
"IsSuccess": true,
"ErrorMessage": null,
"Items": [
{
"LineItemNumber": 0,
"Category": "Default",
"IsEligible": true,
"DiscountPercentage": null,
"VendorPromotionId": "99999999-8888-7777-6666-555555555555",
"EffectiveFrom": "2026-06-01T03:00:00+03:00",
"EffectiveTill": "2028-08-31T03:00:00+03:00",
"ErrorMessage": "",
"PromotionDetails": [
{
"OrganizationId": "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee",
"DiscountType": "Percentage",
"DiscountValue": 20.0
},
{
"OrganizationId": null,
"DiscountType": "Percentage",
"DiscountValue": 20.0
}
],
"AdditionalInformation": {
"PromotionTitle": "Summer Cloud Savings",
"PromotionDescription": "Save on annual commitments made during the summer promotional period.",
"MinQuantity": 5,
"MaxQuantity": 500,
"TermDurationMonths": 36,
"BillingCycle": "Monthly",
"Countries": [
"US",
"CA",
"GB",
"DE",
"FR"
],
"Constraints": {
"RequiresExistingSubscription": false,
"MaxDiscountAmountPerMonth": 1000.0,
"EligibleProductFamilies": [
"Compute",
"Storage"
],
"ExcludedSkus": [
"SKU-1234",
"SKU-5678"
],
"CustomerSegment": {
"Type": "Enterprise",
"MinEmployeeCount": 100,
"AllowedIndustries": [
"Retail",
"Healthcare",
"Finance"
]
},
"UsageLimits": {
"MaxRedemptionsPerCustomer": 1,
"MaxTotalRedemptions": 10000,
"CooldownPeriodDays": 90
}
},
"Notes": "Discount applies only to new subscriptions, not renewals."
}
}
]
}
Direct Model (one-tier) Seller, or Indirect Model Seller upon request — a Seller Admin in the Direct Model also sees the "GrowthMargin" item with the Seller's Organization ID. For Indirect Model Sellers, this view becomes available after reaching out to the Support team and requesting it:
{
"IsSuccess": true,
"ErrorMessage": null,
"Items": [
{
"LineItemNumber": 0,
"Category": "Default",
"IsEligible": true,
"DiscountPercentage": null,
"VendorPromotionId": "99999999-8888-7777-6666-555555555555",
"EffectiveFrom": "2026-06-01T03:00:00+03:00",
"EffectiveTill": "2028-08-31T03:00:00+03:00",
"ErrorMessage": "",
"PromotionDetails": [
{
"OrganizationId": "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee",
"DiscountType": "Percentage",
"DiscountValue": 20.0
},
{
"OrganizationId": null,
"DiscountType": "Percentage",
"DiscountValue": 20.0
}
],
"AdditionalInformation": {
"PromotionTitle": "Summer Cloud Savings",
"PromotionDescription": "Save on annual commitments made during the summer promotional period.",
"MinQuantity": 5,
"MaxQuantity": 500,
"TermDurationMonths": 36,
"BillingCycle": "Monthly",
"Countries": [
"US",
"CA",
"GB",
"DE",
"FR"
],
"Constraints": {
"RequiresExistingSubscription": false,
"MaxDiscountAmountPerMonth": 1000.0,
"EligibleProductFamilies": [
"Compute",
"Storage"
],
"ExcludedSkus": [
"SKU-1234",
"SKU-5678"
],
"CustomerSegment": {
"Type": "Enterprise",
"MinEmployeeCount": 100,
"AllowedIndustries": [
"Retail",
"Healthcare",
"Finance"
]
},
"UsageLimits": {
"MaxRedemptionsPerCustomer": 1,
"MaxTotalRedemptions": 10000,
"CooldownPeriodDays": 90
}
},
"Notes": "Discount applies only to new subscriptions, not renewals."
}
},
{
"LineItemNumber": 0,
"Category": "GrowthMargin",
"IsEligible": true,
"DiscountPercentage": null,
"VendorPromotionId": "99999999-8888-7777-6666-555555555555",
"EffectiveFrom": "2026-06-01T03:00:00+03:00",
"EffectiveTill": "2028-08-31T03:00:00+03:00",
"ErrorMessage": "",
"PromotionDetails": [
{
"OrganizationId": "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee",
"DiscountType": "Percentage",
"DiscountValue": 20.0
},
{
"OrganizationId": null,
"DiscountType": "Percentage",
"DiscountValue": 20.0
}
],
"AdditionalInformation": null
}
]
}
Example response — Distributor Admin role
A Distributor Admin sees all promotion items, including "GrowthMargin", with an additional PromotionDetails entry scoped to an Organization the Seller Admin above does not have visibility into:
{
"IsSuccess": true,
"ErrorMessage": null,
"Items": [
{
"LineItemNumber": 0,
"Category": "Default",
"IsEligible": true,
"DiscountPercentage": null,
"VendorPromotionId": "99999999-8888-7777-6666-555555555555",
"EffectiveFrom": "2026-06-01T03:00:00+03:00",
"EffectiveTill": "2028-08-31T03:00:00+03:00",
"ErrorMessage": "",
"PromotionDetails": [
{
"OrganizationId": "bbbbbbbb-cccc-dddd-eeee-ffffffffffff",
"DiscountType": "Percentage",
"DiscountValue": 20.0
},
{
"OrganizationId": "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee",
"DiscountType": "Percentage",
"DiscountValue": 20.0
},
{
"OrganizationId": null,
"DiscountType": "Percentage",
"DiscountValue": 20.0
}
],
"AdditionalInformation": {
"PromotionTitle": "Summer Cloud Savings",
"PromotionDescription": "Save on annual commitments made during the summer promotional period.",
"MinQuantity": 5,
"MaxQuantity": 500,
"TermDurationMonths": 36,
"BillingCycle": "Monthly",
"Countries": [
"US",
"CA",
"GB",
"DE",
"FR"
],
"Constraints": {
"RequiresExistingSubscription": false,
"MaxDiscountAmountPerMonth": 1000.0,
"EligibleProductFamilies": [
"Compute",
"Storage"
],
"ExcludedSkus": [
"SKU-1234",
"SKU-5678"
],
"CustomerSegment": {
"Type": "Enterprise",
"MinEmployeeCount": 100,
"AllowedIndustries": [
"Retail",
"Healthcare",
"Finance"
]
},
"UsageLimits": {
"MaxRedemptionsPerCustomer": 1,
"MaxTotalRedemptions": 10000,
"CooldownPeriodDays": 90
}
},
"Notes": "Discount applies only to new subscriptions, not renewals."
}
},
{
"LineItemNumber": 0,
"Category": "GrowthMargin",
"IsEligible": true,
"DiscountPercentage": null,
"VendorPromotionId": "99999999-8888-7777-6666-555555555555",
"EffectiveFrom": "2026-06-01T03:00:00+03:00",
"EffectiveTill": "2028-08-31T03:00:00+03:00",
"ErrorMessage": "",
"PromotionDetails": [
{
"OrganizationId": "bbbbbbbb-cccc-dddd-eeee-ffffffffffff",
"DiscountType": "Percentage",
"DiscountValue": 20.0
},
{
"OrganizationId": "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee",
"DiscountType": "Percentage",
"DiscountValue": 20.0
},
{
"OrganizationId": null,
"DiscountType": "Percentage",
"DiscountValue": 20.0
}
],
"AdditionalInformation": null
}
]
}
Summary
The POST {base_url}/api/v1/price/externalPromotions endpoint gives Distributors and Sellers direct visibility into vendor promotion eligibility for both new offer configurations and existing subscriptions, without relying solely on calculated pricing results. It respects the caller's Organization permission scope, filtering PromotionDetails entries the caller is not authorized to view.
Add comment
Please sign in to leave a comment.