# Update promotion

`PUT /api/configuration/promotions/{id}`

- **Success:** `204 No content`
- **Products:** Operator (`FOH`)
- **Features:** Front of House Generic Access (`FOHGENERIC`)

HTML version: [Update promotion](https://support.gladstonesoftware.cloud/reference/configuration/update-promotion)

## Overview

Updates an existing promotion — its name, the window it is valid for, whether it is active, and which promo codes are attached to it.

**This is a full replacement, not a partial update.** `name`, `validFrom`, `validTo` and `isActive` are overwritten from your body on every call, so a field you leave out of the JSON is written with a default rather than left alone: `validFrom` and `validTo` become `0001-01-01` and `isActive` becomes `false`. `promoCodes` is worse than that — leaving it out or sending an empty array deletes every promo code mapping the promotion currently holds, because the existing mappings are removed and nothing replaces them. Fetch the current state with [Get promotion by ID](get-promotion-by-id.md) first, apply your change to that object, and send the whole thing back.

One field is silently ignored: `promotionType` is accepted but ignored on update. A promotion's type is fixed at creation and cannot be changed here.

A successful update returns `204 No Content` with an empty body, and the change is visible on the next call to [List promotions](get-all-promotions.md).

## Endpoint

`PUT {base_url}/api/configuration/promotions/{id}`

`{base_url}` is the environment URL issued to your integration. See [Authenticating with Gladstone APIs](../../guides/authenticating-with-gladstone-apis.md).

## Path parameters

| Name | Type | Required | Description |
|------|------|----------|-------------|
| `id` | integer (int32) | Yes | The promotion's identifier, as returned in `[].id` by [List promotions](get-all-promotions.md). Must be greater than zero — `0` or a negative value is rejected with `400` rather than `404`. |

## Request body

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `promotionName` | string | Yes | The promotion's display name, replacing the stored one. Maximum 50 characters; a longer value is rejected with `The field 'PromotionName' can not exceed 50 characters`, and omitting it or sending null is rejected with `PromotionName is required`. Names are not checked for uniqueness. |
| `validFrom` | string (date-time) | Yes | The first day the promotion applies. Only the date part is kept — any time component you send is discarded. Must not be later than `validTo`, or the call fails with `The validFrom date must be before the validTo date`. It is not marked as required, but omitting it writes `0001-01-01` over whatever was stored, so treat it as required. |
| `validTo` | string (date-time) | Yes | The last day the promotion applies. Only the date part is kept. Must be today or later whenever `isActive` is `true` — a past `validTo` is only accepted on an inactive promotion, otherwise the call fails with `The validTo date must be from today onwards for active promotions`. As with `validFrom`, omitting it writes `0001-01-01`, which then fails that same check if `isActive` is `true`. |
| `isActive` | boolean | No | Whether the promotion is in force. Defaults to `false` when omitted, which deactivates a currently active promotion — send `true` explicitly to keep it live. Setting it `true` tightens validation on `validTo`, which must then be today or later; set it `false` to archive a promotion whose window has already passed. |
| `promoCodes` | array of string | No | The complete set of promo codes to attach to the promotion, given as code strings rather than ids. Every code must already exist — an unrecognised one fails the whole call with `One or more of the promoCodes provided are not valid`, and repeating a code fails with `Duplicate promo codes can not be provided`. The list is absolute, not additive: whatever you send becomes the promotion's mappings, and omitting the field or sending `[]` unlinks all existing codes. Take the current values from `promoCodes` on [Get promotion by ID](get-promotion-by-id.md). |
| `promotionType` | string | No | Ignored on update. On [Create promotion](create-promotion.md) it must be `Membership` or `Booking`, but this endpoint ignores it, so the stored type is unchanged whatever you send, including an invalid value. |

## Sample request

```bash title="cURL"
curl -X PUT '{base_url}/api/configuration/promotions/{id}' \
  -H 'Authorization: Bearer {accessToken}' \
  -H 'X-Use-SSO: true' \
  -H 'Content-Type: application/json' \
  -d '{
  "promotionName": "string",
  "validFrom": "2026-08-14T19:00:00Z",
  "validTo": "2026-08-14T19:00:00Z",
  "isActive": true,
  "promoCodes": [
    "string"
  ],
  "promotionType": "string"
}'
```

## Sample response

```json title="204 No content"
1
```

## Errors

| Status | Meaning | When |
|--------|---------|------|
| `400` | Bad request | `id` is zero or negative — `Promotion ID {id} is not valid`. |
| `400` | Bad request | `promotionName` is missing (`PromotionName is required`) or longer than 50 characters (`The field 'PromotionName' can not exceed 50 characters`). |
| `400` | Bad request | `validFrom` is later than `validTo` — `The validFrom date must be before the validTo date`. |
| `400` | Bad request | `isActive` is `true` and `validTo` is before today — `The validTo date must be from today onwards for active promotions`. This also fires when `validTo` is omitted, because it then defaults to `0001-01-01`. |
| `400` | Bad request | `promoCodes` repeats the same code — `Duplicate promo codes can not be provided`. |
| `400` | Bad request | One or more entries in `promoCodes` do not match an existing promo code — `One or more of the promoCodes provided are not valid`. No part of the update is applied. |
| `403` | Forbidden | Your token lacks the `FOH` product or the `FOHGENERIC` feature claim this endpoint requires. Rejected before the request is processed. |
| `404` | Not found | No promotion exists with that `id` — `Unable to locate promotion with ID {id}`. |
| `500` | Internal server error | An unexpected server-side failure. Nothing is written; the request is safe to retry. |
