Create promo code
POST /api/configuration/promotion-codes
Success: 201 Created
- Products: Operator (
FOH) - Features: Front of House Generic Access (
FOHGENERIC)
Overview
Creates a promo code — the short string a customer types at checkout to pull in one or more discounts you have already configured. The promo code itself carries no money off; it is a validity window plus a list of discount ids, and the discounts decide what is actually reduced. You therefore have to create the discounts first.
The identifier is chosen by the platform, not by you: it is an automatically allocated integer, and
that integer is the entire body of the 201 Created response — a bare JSON number such as 12345, not
an object. Keep it, because it is what
Update promo code takes in its path. The Location header
points back at this same collection URL rather than at the new promo code, so it is of no use to you.
If you lose the id and only have the code string, Get promo code by ID or code will find it — but that
endpoint treats a bare path segment as a numeric id, so the code has to be wrapped in curly braces:
/api/configuration/promotion-codes/{SUMMER24}.
Retrying is safe in the ordinary case: code must be unique, compared case-insensitively, and a repeat
returns 409 Conflict with a message that includes the id of the existing promo code — so you can recover
the id from the error. Be aware, though, that uniqueness is not guaranteed under concurrency: two identical requests arriving at
the same moment can both succeed and leave you
with two promo codes sharing a code. Nothing else on the request is checked for uniqueness.
The remaining validation is about time and is stricter than it first looks. validFrom must be today or
later and validTo must be strictly later than today, both compared as dates in UTC with the time of day
discarded, so a code cannot be created that starts and finishes on the same day, and no code can be
back-dated.
Endpoint
POST {base_url}/api/configuration/promotion-codes
{base_url} is the environment URL issued to your integration. See Authenticating with Gladstone APIs.
Request body
| Field | Type | Required | Description |
|---|---|---|---|
code | string | Yes | The string customers actually type, up to 25 characters. It must contain no whitespace anywhere — not even in the middle — and must not match an existing promo code, compared case-insensitively, so SUMMER24 clashes with summer24. It is stored exactly as you send it, but every lookup ignores case. |
validFrom | string (date-time) | Yes | The first day the code can be redeemed. Only the date part is kept; any time of day you send is discarded, so the code is live from the very start of that day. It must be today or later in UTC — back-dating is rejected — and must not be after validTo. |
validTo | string (date-time) | Yes | The expiry day. Only the date part is kept. It is exclusive where validFrom is inclusive: redemption fails as soon as the current UTC date reaches validTo, so set it to the day after the last day you want the code to work. It must be strictly later than today, which makes tomorrow the earliest value this endpoint accepts. |
maxUsages | integer (int32) | Yes | How many times the code may be redeemed in total. Must be 0 or greater. 0 means unlimited, and the retrieval endpoints report maxUsage as 0 for an unlimited code. The separate remaining-usages count starts at the number you send, so an unlimited code starts life reporting usagesRemaining of 0. |
active | boolean | Yes | Whether the code is currently enabled. This is enforced at redemption: membership pricing rejects an inactive code with Promotion code {code} is not currently active, regardless of its dates. It also controls visibility on Get all promo codes, which omits inactive codes unless you pass includeInactive=true. Send false to stage a campaign in advance and switch it on later with Update promo code. |
autoApply | boolean | Yes | Stored against the promo code and returned by the retrieval endpoints. The field is mandatory — omitting it fails request validation — so send false unless you have been told otherwise. |
discountIds | array of integer | Yes | The discounts this code switches on, by id. At least one is required, and every id must already exist or the whole request fails with 400 listing the ids it could not find. The ids come from the response of Create a discount — which returns an array of ids, one per membership type, when the discount covers several — or from Get all discounts. The discounts are what determine the actual reduction and which membership types it applies to; a code whose discounts match none of the memberships being bought is rejected at pricing time, not here. |
Sample request
curl -X POST '{base_url}/api/configuration/promotion-codes' \
-H 'Authorization: Bearer {accessToken}' \
-H 'X-Use-SSO: true' \
-H 'Content-Type: application/json' \
-d '{
"code": "string",
"validFrom": "2026-08-14T19:00:00Z",
"validTo": "2026-08-14T19:00:00Z",
"maxUsages": 1,
"active": true,
"autoApply": true,
"discountIds": [
12345
]
}'Sample response
1Errors
Every failure below returns the message as a plain string body, except request validation, which returns a
validation problem document (application/problem+json) with an errors map keyed by field name.
| Status | Meaning | When |
|---|---|---|
400 | Bad request | Request validation failed before any other check: code missing or longer than 25 characters, validFrom or validTo missing, maxUsages missing or negative, active or autoApply missing, or discountIds missing or empty. The messages are The field 'Code' is a mandatory field, The field 'Code' can not exceed 25 characters, ValidFrom date must be provided, ValidTo date must be provided, MaxUsages must be supplied, Max Usages must be 0 or greater, Active must be supplied, AutoApply must be supplied and At least one discount ID must be supplied. |
400 | Bad request | Malformed JSON, or a value of the wrong type for its field. |
400 | Bad request | code contains a space, tab or other whitespace character. Body: Promo-code code can not have any blank spaces.. |
400 | Bad request | validTo is today or earlier in UTC. Body: The Valid To date must be a future date. |
400 | Bad request | validFrom is before today in UTC. Body: The Valid From date must be from today onwards. |
400 | Bad request | validTo is earlier than validFrom. Body: The Valid To date must be a future date and after the Valid From date. |
400 | Bad request | One or more entries in discountIds do not exist. Body: The following discount IDs do not exist and can not be mapped to a promo-code: {ids}, listing exactly which ids were not found. Nothing is created. |
409 | Conflict | A promo code with the same code already exists, ignoring case. Body: A promo-code with the code '{code}' already exists with an ID of {id} — the existing id is in the message, so you can carry on without a second call. |
500 | Internal server error | Any unexpected server-side failure. The promo code and its discount links are saved in two separate steps, so a failure between them can leave a promo code with no discounts attached; check with Get promo code by ID or code before retrying. |