Update a discount
PUT /api/configuration/discounts/{id}
Success: 204 No Content
- Products: Operator (
FOH) - Features: Front of House Generic Access (
FOHGENERIC)
Overview
Rewrites a membership discount: which membership type it applies to, the three percentage rates it offers, how long the regular rate runs for, the marketing text shown with it, whether it is live, and whether it is restricted to one site.
This is a full replacement, not a patch. Every field in the body is written, and there is no "leave this alone" — a field you omit takes its JSON default. Two of those defaults are destructive: omitting isActive sets it to false and switches the discount off, and omitting siteId sets it to null, which widens a site-specific discount to every site. Read the discount first, change what you mean to change, and send the whole object back.
The percentage amounts also drive a derived flag you cannot set directly. Each amount you send as zero records that discount kind as "none" rather than as a nil percentage, and the discount is marked perpetual only when discountRegularAmount is above zero and discountUntilUnit is zero. You cannot make a discount perpetual without a regular rate.
Successful updates return 204 No Content with an empty body — no representation of the updated discount comes back, so re-read it if you need to confirm what was stored. A discount id that does not exist returns 404, and every business-rule failure returns 400 with a plain-text sentence naming the rule.
One wrinkle in the validation messages: several are worded "Discount could not be created…" even though this is an update, because the request body is shared with Create a discount. Match on the rule, not the verb.
Changes are visible immediately, so Retrieves all active discounts reflects the change on its next call.
Endpoint
PUT {base_url}/api/configuration/discounts/{id}
{base_url} is the environment URL issued to your integration. See Authenticating with Gladstone APIs.
Path parameters
| Name | Type | Required | Description |
|---|---|---|---|
id | integer (int32) | Yes | The id of the discount to update, as returned in the discountId field of Retrieves all active discounts. An id that does not exist returns 404 with the body Discount with ID {id} does not exist. Note that a single discount id can cover several discounted products, so updating one id can affect more than one discounted product. |
Request body
| Field | Type | Required | Description |
|---|---|---|---|
membershipTypeId | string | Yes | The single membership type the discount applies to, taken from the id of Get all membership types with optional filtering. Note the difference from Create a discount, which takes a membershipTypeIds array — on update you set exactly one, and changing it moves the discount rather than adding a second. Stored upper-cased whatever you send, so compare case-insensitively. Must be present and non-empty, and must exist: an unknown value fails with 400 and Discount could not be updated as {membershipTypeId} does not exist. |
discountRegularAmount | number (double) | Yes | The percentage off the recurring subscription price, as a number between 0 and 100 inclusive — 10.5 means 10.5% off, not £10.50. Required: omit it and the request is rejected with 400. 0 is accepted and means "no regular discount", which also records the discount kind as none rather than as a zero percentage. Outside the range, 400. This is the only amount that can be paired with discountUntilUnit, and the only one that can make a discount perpetual. |
discountIntroductoryAmount | number (double) | Yes | The percentage off the introductory (joining) price, between 0 and 100 inclusive. Required — omitting it is a 400. 0 means no introductory discount and records that kind as none. Independent of the regular and one-off amounts: a discount may offer any combination, but not all three as zero. |
discountOneOffAmount | number (double) | Yes | The percentage off one-off product prices, between 0 and 100 inclusive. Required — omitting it is a 400. 0 means no one-off discount. At least one of the three amounts must be above zero, or the request fails with Discount could not be updated as no discount amounts have been provided. |
discountUntilUnit | integer (int32) | Yes | How many billing units the regular discount runs for before the full price resumes. Required and must be 0 or greater; a negative value is a 400. 0 combined with a discountRegularAmount above zero marks the discount perpetual — it never expires. 0 with no regular discount simply means the setting is unused. Only set this above zero when you are also setting a regular discount, since nothing else consumes it. |
discountBanner | string | No | Short marketing text displayed alongside the discount in consumer-facing journeys. Maximum 510 characters, rejected with 400 beyond that. An omitted, null, empty or whitespace-only value is stored as null, clearing whatever banner the discount had — whitespace is trimmed before the check, so " " clears it just as an omission does. |
discountText | string | No | Longer descriptive text for the discount, shown to members alongside the banner. Maximum 510 characters, rejected with 400 beyond that. Same clearing behaviour as discountBanner: omitted, null, empty or whitespace-only is stored as null. |
isActive | boolean | No | Whether the discount is live and applied to qualifying prices. Because a missing boolean defaults to false, omitting this field switches the discount off — the single most common way to break a working discount with this endpoint. Always send it explicitly. Setting it false is how you retire a discount; there is no separate delete step required for that. |
siteId | string | No | Restricts the discount to one site, using the site code from Get all sites with optional filtering. Null or omitted means the discount applies at every site — so leaving it out of the body silently removes a site restriction rather than preserving it. An empty string is not the same as null: "" is rejected with 400 and Discount could not be updated as siteId does not exist, and so is any code that does not match an existing site. |
Sample request
curl -X PUT '{base_url}/api/configuration/discounts/{id}' \
-H 'Authorization: Bearer {accessToken}' \
-H 'X-Use-SSO: true' \
-H 'Content-Type: application/json' \
-d '{
"membershipTypeId": "string",
"discountRegularAmount": 10.5,
"discountIntroductoryAmount": 10.5,
"discountOneOffAmount": 10.5,
"discountUntilUnit": 1,
"discountBanner": "string",
"discountText": "string",
"isActive": true,
"siteId": "string"
}'Sample response
(empty body)Errors
| Status | Meaning | When |
|---|---|---|
204 | No content | Success. The body is empty; nothing about the updated discount is returned. |
400 | Bad request | A required field is missing — membershipTypeId, discountRegularAmount, discountIntroductoryAmount, discountOneOffAmount or discountUntilUnit — returned as a validation problem document (application/problem+json) with an errors map keyed by field name. The messages are worded "could not be created" because the request body is shared with the create endpoint. |
400 | Bad request | membershipTypeId is empty, or names a membership type that does not exist. Body: Discount could not be updated as no membership type ID has been provided or Discount could not be updated as {membershipTypeId} does not exist. |
400 | Bad request | Any of the three amounts is negative or above 100. Body names the offending field, for example Discount could not be updated as the DiscountRegularAmount cannot be greater than 100. |
400 | Bad request | All three amounts are zero. Body: Discount could not be updated as no discount amounts have been provided. A discount must actually discount something. |
400 | Bad request | discountUntilUnit is negative. Body: Discount could not be updated as the DiscountUntilUnit is not greater than or equal to 0. |
400 | Bad request | siteId is an empty string, or a site code that does not exist. Body: Discount could not be updated as siteId does not exist. Send null, or omit the field, if you want the discount to apply to all sites. |
400 | Bad request | discountBanner or discountText exceeds 510 characters. Body: The field 'DiscountBanner' can not exceed 510 characters. |
404 | Not found | No discount exists with the supplied id. Body: Discount with ID {id} does not exist. The id is looked up before the business rules are checked, so a 404 tells you nothing about whether your amounts, membership type or site id were valid — fix the id and expect to see those errors next. |
500 | Internal server error | An unexpected server-side failure, including a failure while saving. The body is empty. Validation runs before anything is written, so a 500 here means the write itself failed — re-read the discount to see whether it landed. |