# Updates an existing credit bundle or plan for an environment

`PUT /api/configuration/credits`

- **Products:** Gladstone Admin Areas (`ADMIN`)
- **Features:** not restricted by feature

HTML version: [Updates an existing credit bundle or plan for an environment](https://support.gladstonesoftware.cloud/reference/configuration/updates-an-existing-credit-bundle-or-plan-for-an-environment)

## Overview

Changes a recurring communication credit plan — the allowance of email and SMS credits an environment gets for sending member communications.

**Despite the title, one-off credit bundles cannot be edited.** A `planId` of `0` marks a one-off bundle, and sending it is rejected with `PlanId of 0 indicates a one-off credit bundle. One-off bundles cannot be edited.` Only recurring plans, which carry a `planId` above zero, can be changed here.

**This is a full replacement, not a partial update.** Every field in the table below overwrites the stored plan, so a value you leave out is set to its default rather than left alone. Validation catches most of the omissions — `startDate` is rejected if absent, and a plan with no credits at all is rejected — but not all of them, so read the plan first with [List credit plans for an environment](retrieves-all-communication-credit-plans-for-a-specific-environment.md) and send the whole object back.

`environmentId` and `tenantId` are not editable in practice: both must match the values already on the plan, so they act as a safety check rather than a way to move a plan between environments. Only `planId`, `isActive`, the two credit counts and the two dates can actually change.

**The update is not atomic.** The plan is saved first, and the change is then applied to the environment's communications as a separate step. If that second step fails, you get a `500` even though the plan has already been changed. Re-send the same request to reconcile.

**Note the response.** A successful call returns `200 OK` with an empty body — not `204`, and not the updated plan. Fetch the plan again if you need to confirm the stored result, particularly since `expiryDate` may have been defaulted for you.

## Endpoint

`PUT {base_url}/api/configuration/credits`

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

## Request body

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `id` | integer (int32) | Yes | The identifier of the credit plan record you are updating, as returned by [List credit plans for an environment](retrieves-all-communication-credit-plans-for-a-specific-environment.md). This is the plan record's own id, not `planId`. It is looked up before anything else, and an id matching no plan fails with `400` and `Credit plan with Id {id} not found.` Although marked optional, omitting it is treated as `0`, which will not match a plan. |
| `tenantId` | string | Yes | The tenant the plan belongs to, as a GUID string; surrounding whitespace is trimmed for you. Must be a parseable GUID (`TenantId '{value}' is not a valid GUID.`) and must equal the tenant already stored against the plan — a mismatch fails with `400` rather than moving the plan. Blank or null fails with `TenantId was unexpectedly blank or null.` |
| `environmentId` | integer (int32) | Yes | The environment the plan belongs to. Must be greater than zero (`EnvironmentId must be an integer above 0.`), must exist (`EnvironmentId {id} does not exist.`), must already have a credit plan, and must equal the environment stored against the plan — a mismatch fails with `400`. It cannot be used to move a plan to a different environment. |
| `planId` | integer (int32) | Yes | Which of the platform's defined credit plans this environment is on, from [List default credit plans](retrieves-all-default-communication-credit-plans-available-in-the-system.md). Must be a positive integer that exists, or the call fails with `PlanId {planId} does not exist.` **`0` is not a plan** — it marks a one-off credit bundle, and sending it is rejected outright because one-off bundles cannot be edited. Omitting the field is treated as `0` and produces exactly that rejection. |
| `isActive` | boolean | Yes | Whether the plan is currently in force. Omitting it is treated as `false` and deactivates the plan. Reactivating is restricted: if the stored plan is inactive you may only set `isActive` to `true` when the plan has an expiry date and your `startDate` is on or after it — otherwise the call fails with either `Credit plan for this environment already exists with expiry date of {date}. New credit plan must begin after this date.` or `Cannot reactivate an inactive credit plan that has no defined expiry date.` |
| `includedEmails` | integer (int32) | No | The number of email credits the plan includes. Overwrites the stored value, so omitting it clears the email allowance. At least one of `includedEmails` or `includedSMS` must be present and greater than zero, or the call fails with `Plan cannot include 0 credits. At least 1 credit must be included for Email or SMS.` — so you may omit this only if you are supplying SMS credits. |
| `includedSMS` | integer (int32) | No | The number of SMS credits the plan includes. Same rules as `includedEmails`: overwrites the stored value, and at least one of the two must be present and above zero. Email and SMS credits are counted separately and are not interchangeable. |
| `startDate` | string (date-time) | Yes | The date the plan's allowance begins. Required — omitting it fails with `StartDate must be specified and cannot be blank.` Only the date part is stored; any time component is discarded. A start date in the past is accepted, so this can be backdated. It also gates reactivation of an inactive plan, which requires the start date to fall on or after the stored expiry date. |
| `expiryDate` | string (date-time) | No | The date the plan's allowance ends. **Defaults to twelve months after `startDate` when omitted** rather than meaning "no expiry", so leaving it out silently sets an expiry. Must not be in the past (`ExpiryDate cannot be in the past.`) and must not precede `startDate` (`ExpiryDate cannot be before the StartDate.`). Only the date part is stored. |

## Sample request

```bash title="cURL"
curl -X PUT '{base_url}/api/configuration/credits' \
  -H 'Authorization: Bearer {accessToken}' \
  -H 'X-Use-SSO: true' \
  -H 'Content-Type: application/json' \
  -d '{
  "id": 12345,
  "tenantId": "string",
  "environmentId": 12345,
  "planId": 12345,
  "isActive": true,
  "includedEmails": 1,
  "includedSMS": 1,
  "startDate": "2026-08-14T19:00:00Z",
  "expiryDate": "2026-08-14T19:00:00Z"
}'
```

## Errors

| Status | Meaning | When |
|--------|---------|------|
| `400` | Bad request | No plan has that `id` — `Credit plan with Id {id} not found.` Checked before any other validation. |
| `400` | Bad request | `planId` is `0` — `PlanId of 0 indicates a one-off credit bundle. One-off bundles cannot be edited.` This is also what you get if you omit `planId` entirely. |
| `400` | Bad request | `planId` is negative (`PlanId must be a positive integer.`) or does not exist (`PlanId {planId} does not exist.`). |
| `400` | Bad request | `tenantId` is blank (`TenantId was unexpectedly blank or null.`), not a GUID (`TenantId '{value}' is not a valid GUID.`), or does not match the plan's stored tenant. |
| `400` | Bad request | `environmentId` is zero or negative (`EnvironmentId must be an integer above 0.`), does not exist (`EnvironmentId {id} does not exist.`), has no existing credit plan, or does not match the plan's stored environment. |
| `400` | Bad request | `startDate` is absent — `StartDate must be specified and cannot be blank.` |
| `400` | Bad request | `expiryDate` is before today (`ExpiryDate cannot be in the past.`) or before `startDate` (`ExpiryDate cannot be before the StartDate.`). |
| `400` | Bad request | Neither `includedEmails` nor `includedSMS` is above zero — `Plan cannot include 0 credits. At least 1 credit must be included for Email or SMS.` |
| `400` | Bad request | You are reactivating an inactive plan too early, or one with no expiry date — `Credit plan for this environment already exists with expiry date of {date}. New credit plan must begin after this date.` or `Cannot reactivate an inactive credit plan that has no defined expiry date.` |
| `500` | Internal server error | Saving the plan failed, in which case nothing changes, **or** the plan was saved but applying the change to the environment's communications failed, in which case the plan is already updated and the environment is not. Re-sending the same request is safe and reconciles the two. |
