# Update details for a business rule

`PUT /api/business-rules/{id}`

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

HTML version: [Update details for a business rule](https://support.gladstonesoftware.cloud/reference/bookings/update-details-for-a-business-rule)

## Overview

Replaces the definition of an existing business rule: its description, its active flag, the
trigger it listens to and its complete set of actions.

A business rule links an event in the platform (the **trigger**) to a set of **actions**. When the
trigger fires for a member, the rule's non-default actions are evaluated in ascending `priority`
order and the first one whose conditions the member satisfies is chosen; if none match, the single
default action is chosen. The chosen action's **results** are then carried out — sending one or
more emails from named templates, or calling one or more configured webhooks.

**This is a full replacement, not a merge.** The rule's existing actions, conditions and results
are deleted and rebuilt from the `actions` array you send, so anything you leave out is lost. Fetch
the current definition with
[Retrieve details for a business rule](retrieve-details-for-a-business-rule.md),
change the part you care about, and send the whole thing back. The only field that means "leave
unchanged" is `isActive`.

A successful call returns `204 No Content` with an empty body.

Use this endpoint to:

- change which emails or webhooks a rule sends, by rewriting its `actions`
- switch a rule on or off without touching its logic, by sending only `isActive`
- repoint an existing rule at a different trigger

Rules are not mutually exclusive. If several active rules share the same trigger, every one of them
runs, so overlapping rules produce duplicate emails and webhook calls. Keep the triggers of your
rules distinct, or accept the duplication.

## Endpoint

`PUT {base_url}/api/business-rules/{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 rule you are updating, as returned in `id` by [List all business rules](list-all-business-rules.md). Must be a positive integer — `0` or a negative value is rejected with `Id must be a positive integer`, and an id that matches no rule with `404` and `Business Rule Id {id} does not exist.` |

## Request body

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `description` | string | Yes | Free-text label for the rule, returned as `description` by [List all business rules](list-all-business-rules.md). It has no effect on behaviour but is the only human-readable identification a rule has. Missing or empty is rejected with `Description must be specified`. |
| `isActive` | boolean | No | Whether the rule runs when its trigger fires. Omit it, or send `null`, to leave the rule's current setting unchanged — this is the only field on this endpoint that behaves that way; everything else is overwritten by what you send. |
| `trigger` | object | No | The platform event that causes the rule to be evaluated. Marked optional, but omitting it returns `500` rather than `400`. Always send it, and note that only `trigger.id` is used. |
| `trigger.id` | integer (int32) | No | The id of the trigger this rule listens to. Must be a positive integer (`Trigger '{id}' must be a positive number.`) and must match a trigger configured in the platform, or the request is rejected with `Trigger '{id}' doesn't exist.` The available triggers, with their ids, are returned by `GET /api/business-rules/actions/triggers`. |
| `trigger.name` | string | No | Ignored. The name belongs to the trigger definition, not to the rule, and cannot be changed here — only `trigger.id` is read. Send it, omit it or send nonsense; the stored rule is identical either way. |
| `trigger.description` | string | No | Ignored, for the same reason as `trigger.name`. |
| `actions` | array of object | No | The rule's complete set of actions, replacing whatever it had before. Marked optional, but omitting it returns `500`. Exactly one entry must be the default action (`isDefault: true`), so an empty array is rejected with `Exactly one default action must be specified.` |
| `actions[].description` | string | Yes | Free-text label for this action. Required on every action; missing or empty is rejected with `Description must be specified`. |
| `actions[].priority` | integer (int32) | No | The order in which non-default actions are tried when the trigger fires — lowest first, and the first action whose conditions match wins. Must be greater than zero on a non-default action (`Non-default actions must have a priority greater than zero.`), must be exactly `0` on the default action (`Default actions must have a priority of zero.`), can never be negative (`Priorities must be a positive integer.`), and must be unique across the rule's actions (`Actions can not have duplicate priorities.`). |
| `actions[].isDefault` | boolean | No | Marks this action as the fallback that runs when no conditional action matches the member. Exactly one action in the rule must set it, or the request is rejected with `Exactly one default action must be specified.` The default action must have no conditions (`Default actions cannot have conditions.`) and `priority` `0`; every other action must have at least one condition and a priority above zero. The simplest valid rule is therefore a single default action with no conditions. |
| `actions[].conditions` | array of object | No | The tests that decide whether this action applies to the member the trigger fired for. All conditions in one action are combined with `AND`; there is no way to express `OR` within an action, so use a separate action for each alternative. Must be absent or empty on the default action, and must contain at least one entry on every other action (`Non-default actions must have at least one condition.`). |
| `actions[].conditions[].fieldName` | string | No | The member field this condition tests, for example a field of the member record. Paired with `tableName`: the combination must match one of the platform's allowed condition fields, or the request is rejected with `Condition fieldName '{tableName}.{fieldName}' doesn't exist.` Arbitrary field names are not accepted; only fields on the allowed list can be used. |
| `actions[].conditions[].tableName` | string | No | The table the field belongs to, resolved together with `fieldName` against the same allowed list and rejected with the same message when the pair is unknown. Conditions are evaluated against the member record, so in practice this is the member record. |
| `actions[].conditions[].conditional` | string | No | The comparison operator. Must be one of `=`, `<>`, `>`, `<`, `>=`, `<=`, `IN`, `NOT IN`, `LIKE`, `NOT LIKE`, matched case-insensitively; anything else is rejected with `Condition '{value}' is not valid, must be one of =,<>,>,<,>=,<=,IN,NOT IN,LIKE,NOT LIKE`. Omitting it returns `500` rather than `400`. |
| `actions[].conditions[].value` | string | Yes | The value the field is compared against. Always a string, even for numeric, date or boolean fields — it is converted according to the field's type. Missing or empty is rejected with `Value must be specified`. |
| `actions[].results` | array of object | No | What the action does when it is chosen. Marked optional, but every action must have at least one result, or the request is rejected with `All actions must have one or more results.` All results on the chosen action are carried out. |
| `actions[].results[].type` | string | No | The kind of result, matched case-insensitively against the name of an action type configured in the platform; `GET /api/business-rules/actions/types` lists the available names. An unrecognised value is rejected with `Action Result Type '{type}' doesn't exist.` The types named `email` and `webhook` are treated specially and govern whether `templates` and `webhooks` are required or forbidden. |
| `actions[].results[].templates` | array of integer | No | The ids of the email templates to send. Required and non-empty when `type` is the `email` action type (`Action Result Type '{type}' requires one or more email templates to be specified.`), and every id must exist (`One or more email template IDs do not exist`). For any other type it must be omitted or empty, or the request is rejected with `Email templates cannot be specified for Action Result Type '{type}'.` |
| `actions[].results[].webhooks` | array of integer | No | The ids of the webhooks to call. Required and non-empty when `type` is the `webhook` action type (`Action Result Type '{type}' requires one or more webhooks to be specified.`), and every id must exist (`One or more webhook IDs do not exist`). For any other type it must be omitted or empty, or the request is rejected with `Webhooks cannot be specified for Action Result Type '{type}'.` |
| `actions[].results[].description` | string | No | Accepted by the request but not saved, so this value is discarded and does not come back from [Retrieve details for a business rule](retrieve-details-for-a-business-rule.md). Put any label you need on `actions[].description` instead. |

## Sample request

```bash title="cURL"
curl -X PUT '{base_url}/api/business-rules/{id}' \
  -H 'Authorization: Bearer {accessToken}' \
  -H 'X-Use-SSO: true' \
  -H 'Content-Type: application/json' \
  -d '{
  "description": "string",
  "isActive": true,
  "trigger": {
    "id": 12345,
    "name": "string",
    "description": "string"
  },
  "actions": [
    {
      "description": "string",
      "priority": 1,
      "isDefault": true,
      "conditions": [
        {
          "fieldName": "string",
          "tableName": "string",
          "conditional": "string",
          "value": "string"
        }
      ],
      "results": [
        {
          "type": "string",
          "templates": [
            1
          ],
          "webhooks": [
            1
          ],
          "description": "string"
        }
      ]
    }
  ]
}'
```

## Errors

| Status | Meaning | When |
|--------|---------|------|
| `400` | Bad request | The path `id` is not a positive integer: `Id must be a positive integer` |
| `400` | Bad request | A required string is missing or empty: `Description must be specified` (on the rule or on an action) or `Value must be specified` (on a condition) |
| `400` | Bad request | The action set breaks a structural rule: `Exactly one default action must be specified.`, `Default actions cannot have conditions.`, `Default actions must have a priority of zero.`, `Non-default actions must have at least one condition.`, `Non-default actions must have a priority greater than zero.`, `Priorities must be a positive integer.`, `Actions can not have duplicate priorities.` or `All actions must have one or more results.` |
| `400` | Bad request | The trigger is unusable: `Trigger '{id}' must be a positive number.` or `Trigger '{id}' doesn't exist.` |
| `400` | Bad request | A condition does not resolve: `Condition fieldName '{tableName}.{fieldName}' doesn't exist.` or `Condition '{conditional}' is not valid, must be one of =,<>,>,<,>=,<=,IN,NOT IN,LIKE,NOT LIKE` |
| `400` | Bad request | A result does not resolve: `Action Result Type '{type}' doesn't exist.`, `Action Result Type '{type}' requires one or more email templates to be specified.`, `Action Result Type '{type}' requires one or more webhooks to be specified.`, `Email templates cannot be specified for Action Result Type '{type}'.`, `Webhooks cannot be specified for Action Result Type '{type}'.`, `One or more email template IDs do not exist` or `One or more webhook IDs do not exist` |
| `404` | Not found | No rule exists with the given path `id`: `Business Rule Id {id} does not exist.` Validation of the body runs first, so a malformed body against a non-existent rule returns `400`, not `404`. |
| `500` | Internal server error | An unexpected server-side failure. In practice this is what you get for omitting `trigger`, `actions` or a condition's `conditional`. Check that you sent all three before assuming the fault is on our side. |
