> ## Documentation Index
> Fetch the complete documentation index at: https://docs.omneo.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Reaction Actions

> Reference for the actions a Trigger or Automation can run, with their arguments.

Actions are the steps a [Reaction](/concepts/automation/reactions) executes. The same set is available to a [Trigger](/concepts/automation/triggers) and to an [Automation](/concepts/automation/automations), and they behave identically in both.

## Argument format

Each action takes an array of argument objects:

```json theme={null}
[
  { "name": "profile_id", "value": { "var": "id" }, "is_dynamic": true },
  { "name": "definition", "value": "welcome-reward" }
]
```

| Key          | Description                                                                                                                                            |
| ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `name`       | The argument name, from the tables below.                                                                                                              |
| `value`      | A literal value, or a JSON Logic expression when `is_dynamic` is true.                                                                                 |
| `is_dynamic` | Set true to evaluate `value` as JSON Logic against the [event context](/dev-guides/webhooks/event-contexts) before the action runs. Omit for literals. |

`profile_id` is almost always dynamic, reading the subject from context: `{ "var": "id" }` on a profile context, or `{ "var": "profile_id" }` on a transaction or transaction item context.

Every action validates its own arguments and rejects the whole reaction on a bad payload, so a `422` on save usually means a missing required argument or a handle that does not exist.

## How the chain runs

Actions run in `sort_order` order. **An action that returns false stops the chain**, and nothing after it runs. That is the mechanism behind every gate below, and it is why `filter` is conventionally `sort_order` 0 or 1.

## Filtering

### `filter`

Evaluates a condition and stops the chain when it is false. The most common action in any reaction.

| Argument    | Required | Description                                     |
| ----------- | -------- | ----------------------------------------------- |
| `condition` | Yes      | JSON Logic evaluated against the event context. |

```json theme={null}
[{ "name": "condition", "value": { "in": ["vip", { "var": "flattened_statuses" }] } }]
```

See [Reaction Filters](/dev-guides/reactions/filters) and [JSON Conditions](/dev-guides/reactions/json-conditions).

## Gates

These return a boolean rather than changing anything. They exist to stop a chain, so put them before the actions they protect.

### `incentive.not-received`

Passes only when the profile has **not** received the given incentives inside a window. Counts expired and redeemed ones, so it is a "not issued at all recently" check rather than "does not currently hold".

| Argument            | Required | Description                                                                                                      |
| ------------------- | -------- | ---------------------------------------------------------------------------------------------------------------- |
| `profile_id`        | Yes      | The profile to check.                                                                                            |
| `incentive_handles` | Yes      | Array of definition handles.                                                                                     |
| `incentive_days`    | Yes      | Window in days, minimum 1.                                                                                       |
| `incentive_type`    | No       | `benefit`, `reward`, or `credit`.                                                                                |
| `incentive_match`   | No       | `any` (default) passes only if none of the handles were received. `all` passes unless every handle was received. |

```json theme={null}
[
  { "name": "profile_id", "value": { "var": "id" }, "is_dynamic": true },
  { "name": "incentive_type", "value": "benefit" },
  { "name": "incentive_handles", "value": ["birthday-gift"] },
  { "name": "incentive_days", "value": 365 }
]
```

### `transaction.count`

Passes when the profile's transaction count in a period equals `count` exactly.

| Argument                         | Required | Description                                         |
| -------------------------------- | -------- | --------------------------------------------------- |
| `count`                          | Yes      | The exact count to match.                           |
| `period_count` and `period_unit` | Together | Window size, unit is `days`, `weeks`, or `months`.  |
| `profile_id`                     | No       | Defaults to the context profile.                    |
| `start_date`                     | No       | Count from this point instead of a rolling window.  |
| `location_type`                  | No       | Restrict to transactions at locations of this type. |
| `timezone`, `start_of_day`       | No       | Control day boundaries.                             |

Matching is an equality check, not a threshold, which makes it suited to "this is their Nth transaction" rules.

### `transaction.total`

As `transaction.count`, but matches the profile's spend total against `total`.

### `profile.done`

Passes when every named context path holds a value. Used for profile-completion rules.

| Argument     | Required | Description                                                                |
| ------------ | -------- | -------------------------------------------------------------------------- |
| `attributes` | Yes      | Array of context paths that must all be set.                               |
| `reward`     | No       | Reward definition handle to check the profile has not already been issued. |

## Rewards

### `reward.create`

Issues a reward.

| Argument                                                        | Required | Description                                                                |
| --------------------------------------------------------------- | -------- | -------------------------------------------------------------------------- |
| `profile_id`                                                    | Yes      | Recipient.                                                                 |
| `definition`                                                    | Yes      | Reward definition handle.                                                  |
| `value`                                                         | No       | Overrides the definition's value.                                          |
| `expires_at`                                                    | No       | Explicit expiry, `Y-m-d H:i:s`. Otherwise the definition's period applies. |
| `delay_days`                                                    | No       | Issue this many days in the future.                                        |
| `timezone`                                                      | No       | Timezone the dates are expressed in.                                       |
| `rounding`                                                      | No       | `DOWN`, `UP`, `HALF-DOWN`, or `HALF-UP`.                                   |
| `max_issue`                                                     | No       | Cap on the value issued.                                                   |
| `max_issue_count` and `max_issue_unit`                          | No       | Limit how many can be issued per window (`days`, `weeks`, `months`).       |
| `max_issue_period`, `max_issue_custom`, `max_issue_skip_period` | No       | Further issue-cap controls.                                                |
| `external_code`                                                 | No       | Store an external identifier on the reward.                                |
| `source_id`, `source_type`                                      | No       | Provenance, for tracing what caused the issue.                             |
| `meta`                                                          | No       | Arbitrary JSON stored on the reward.                                       |

```json theme={null}
[
  { "name": "profile_id", "value": { "var": "id" }, "is_dynamic": true },
  { "name": "definition", "value": "spend-100-get-10" },
  { "name": "value", "value": { "var": "reward_value" }, "is_dynamic": true },
  { "name": "delay_days", "value": 1 }
]
```

### `reward.expiry.extend`

Extends the expiry of the reward in context by `days`.

| Argument | Required | Description  |
| -------- | -------- | ------------ |
| `days`   | Yes      | Days to add. |

### `reverse.reward.value`

Reverses reward value for a profile, for example after a return.

| Argument              | Required | Description                          |
| --------------------- | -------- | ------------------------------------ |
| `profile_id`          | Yes      | The profile.                         |
| `value`               | Yes      | Amount to reverse.                   |
| `meta`                | Yes      | Context stored against the reversal. |
| `reward_exclude_type` | No       | Reward types to leave alone.         |

### `invite.reward.create` and `join.from.invite.reward`

The two halves of a referral: reward the inviter, and reward the joiner who used an invite. Both take the reward arguments above plus `invite_handle` (the custom attribute holding the invite code) and `tier_reward_handles` for tier-specific reward definitions.

## Points

### `point.create`

Issues a single point record.

| Argument                                   | Required | Description                                                   |
| ------------------------------------------ | -------- | ------------------------------------------------------------- |
| `profile_id`                               | Yes      | Recipient.                                                    |
| `amount`                                   | Yes      | Points to issue.                                              |
| `definition`                               | Yes      | Point definition handle.                                      |
| `expires_at`                               | No       | Explicit expiry.                                              |
| `accrued_at`                               | No       | When the points count as accrued, for backdating.             |
| `expiry`                                   | No       | Structured expiry configuration.                              |
| `rate`                                     | No       | Rate to apply to the amount.                                  |
| `check_rate`                               | No       | Resolve a configured Rate instead of using `amount` directly. |
| `rate_priority`                            | No       | Which rate wins when several match.                           |
| `location_id`, `region`, `country`, `code` | No       | Where the points came from, used by rate resolution.          |
| `profile_context`, `product_context`       | No       | Extra context for rate conditions.                            |
| `source_id`, `source_type`                 | No       | Provenance.                                                   |

### `points.create`

Issues points for every qualifying item on a transaction, rather than one flat amount.

| Argument                             | Required | Description                                               |
| ------------------------------------ | -------- | --------------------------------------------------------- |
| `profile_id`                         | Yes      | Recipient.                                                |
| `definition`                         | Yes      | Point definition handle.                                  |
| `transaction_id`                     | No       | Transaction to walk. Defaults to the context transaction. |
| `filter`                             | No       | Item filter deciding which lines qualify.                 |
| `bonus`                              | No       | Bonus configuration.                                      |
| `multiplier`                         | No       | Multiplier configuration.                                 |
| `expires_at`, `accrued_at`, `expiry` | No       | Expiry controls, as `point.create`.                       |
| `description`                        | No       | Description stored on the points.                         |

### `create.points.with.rate`

Issues points through a Rate with spend and issue limits. Requires `profile_id`, `definition`, `value`, `type`, `timezone`, `limit`, and `spend_limit`. Optional `transaction_item_id`, `date_range`, `is_return`, `linked_identity`, `location_type`, `name`, `code`, `expires_at`, `accrued_at`.

### `redeem.point`

Redeems points against an external endpoint.

| Argument                  | Required | Description                         |
| ------------------------- | -------- | ----------------------------------- |
| `profile_id`              | Yes      | The profile.                        |
| `point_definition_handle` | Yes      | Which point balance to spend.       |
| `url`                     | Yes      | Endpoint to call.                   |
| `authorization`           | No       | Authorization header value.         |
| `exchange_rate`           | No       | Points to currency conversion.      |
| `max_value`               | No       | Cap per redemption.                 |
| `day_of_week`, `timezone` | No       | Restrict which day redemption runs. |

### `finish.transaction.point`

Finalises a pending point record, optionally limited to given SKUs. Requires `point_id`; optional `sku` array.

## Tier points

### `tier-point.create`

Issues tier points, which count toward tier progression rather than a spendable balance. Same shape as `point.create`: requires `profile_id`, `amount`, `definition`, with `issued_at`, `accrued_at`, `transaction_id`, `check_rate`, `rate_priority`, `is_return`, `delay_return_points`, and the location and context arguments optional.

### `tier-points.create`

Per-item tier points for a transaction. Requires `profile_id` and `definition`; optional `transaction_id`, `filter`, `issued_at`, `accrued_at`, `delay_return_points`.

### `tier-point.create.with.region`

Tier points with currency conversion across regions. Additionally requires `currency`, `transaction_region`, and `profile_region`, with optional `convert_currency`.

## Benefits

### `benefit.create`

Issues a benefit.

| Argument                   | Required | Description                                    |
| -------------------------- | -------- | ---------------------------------------------- |
| `profile_id`               | Yes      | Recipient.                                     |
| `definition`               | Yes      | Benefit definition handle.                     |
| `check_existing`           | No       | Skip issuing when the profile already has one. |
| `check_days`               | No       | With `check_existing`, how far back to look.   |
| `issued_at_days`           | No       | Offset the issue date by this many days.       |
| `issued_at_hour`           | No       | Hour of day to issue at.                       |
| `timezone`                 | No       | Timezone the issue date is expressed in.       |
| `source_id`, `source_type` | No       | Provenance.                                    |
| `meta`                     | No       | Arbitrary JSON stored on the benefit.          |

### `benefit.replace`

Keeps a profile holding exactly one benefit from a mutually exclusive family, which is what tiered gifts need. Instead of a single definition it takes `mappings`, one entry per tier.

| Argument                | Required | Description                                    |
| ----------------------- | -------- | ---------------------------------------------- |
| `profile_id`            | Yes      | The profile.                                   |
| `mappings`              | Yes      | One entry per tier, at least one.              |
| `mappings.*.handle`     | Yes      | Benefit definition to issue for this tier.     |
| `mappings.*.condition`  | Yes      | JSON Logic deciding whether this tier applies. |
| `mappings.*.sort_order` | No       | Rank, where **lower is the higher tier**.      |

```json theme={null}
[
  {
    "name": "profile_id",
    "value": { "var": "id" },
    "is_dynamic": true
  },
  {
    "name": "mappings",
    "value": [
      { "handle": "gift-platinum", "sort_order": 10, "condition": { "in": ["platinum", { "var": "flattened_statuses" }] } },
      { "handle": "gift-gold",     "sort_order": 20, "condition": { "in": ["gold",     { "var": "flattened_statuses" }] } },
      { "handle": "gift-member",   "sort_order": 30, "condition": { "in": ["member",   { "var": "flattened_statuses" }] } }
    ]
  }
]
```

`sort_order` does two jobs. It picks the target, since the first matching mapping in `sort_order` sequence wins, and it establishes rank, which governs what happens on the way down:

* **Moving up** always issues the higher-tier benefit. An unredeemed lower-tier benefit is retired and the new one inherits its expiry rather than extending it. A redeemed one is left alone.
* **Moving down** does not hand back a lower-tier benefit the profile has already used at a higher tier, as long as that redemption still falls inside the benefit definition's own period. Once that period has elapsed the entitlement reopens.

Omitting `sort_order` falls back to the order the mappings are declared in, so an existing configuration keeps working, but set it explicitly: it makes the tier order visible to whoever edits the reaction next.

### `update.benefit`

Expires or extends a benefit the profile holds.

| Argument                         | Required               | Description                                            |
| -------------------------------- | ---------------------- | ------------------------------------------------------ |
| `profile_id`                     | Yes                    | The profile.                                           |
| `definition`                     | Yes                    | Which benefit to change.                               |
| `type`                           | Yes                    | `expire` or `extend`.                                  |
| `extend_count` and `extend_unit` | Together, for `extend` | How much to add, unit is `days`, `weeks`, or `months`. |

### `redeem.benefit`

Records a redemption against a benefit the profile holds.

| Argument     | Required | Description                                      |
| ------------ | -------- | ------------------------------------------------ |
| `profile_id` | Yes      | The profile.                                     |
| `definition` | Yes      | Which benefit to redeem.                         |
| `count`      | No       | Redemptions to record, minimum 1. Defaults to 1. |

## Credits

### `credit.create`

Issues a credit. Requires `profile_id` and `definition`. Shares the reward issue-cap and expiry arguments (`value`, `expires_at`, `delay_days`, `timezone`, `rounding`, `max_issue*`), and adds gifting arguments: `creator_profile_id`, `recipient_email`, `recipient_first_name`, `message`, plus `quantity`, `issue_per_item_quantity`, `external_id`, `external_namespace`, and `location_id`.

## Tiers

### `update.profile.tier`

Assigns a tier or recalculates the profile's tier.

| Argument                       | Required       | Description                                           |
| ------------------------------ | -------------- | ----------------------------------------------------- |
| `profile_id`                   | Yes            | The profile.                                          |
| `recalculate` or `tier_handle` | One of the two | Recalculate from spend, or assign this tier directly. |
| `achieved_at`                  | No             | When the tier was reached.                            |
| `anniversary_at`               | No             | When the tier period resets.                          |
| `is_assigned`                  | No             | Mark the tier as manually assigned.                   |
| `trigger_event`                | No             | Whether to dispatch the tier event.                   |
| `timezone`                     | No             | Timezone for the dates.                               |

### `tier.update`

Sets the profile onto a specific tier definition. Requires `profile_id` and `definition`; optional `trigger_event`.

### `tier.calculate`

Recalculates the profile's tier from current data. Requires `profile_id`; optional `disable_event` to suppress the resulting event.

### `tier.maintain.instant`

Applies instant tier maintenance. Takes `count` and `unit` together (`days`, `weeks`, `months`).

## Achievements

### `achievement.increment`

Increments an achievement.

| Argument                   | Required | Description                    |
| -------------------------- | -------- | ------------------------------ |
| `profile_id`               | Yes      | The profile.                   |
| `definition`               | Yes      | Achievement definition handle. |
| `count`                    | Yes      | Amount to add.                 |
| `source_id`, `source_type` | No       | Provenance.                    |

### `achievement.increment.transaction`

Increments based on the transaction in context, counting qualifying items. Requires `profile_id` and `definition`; optional `item_filter` and `source_id`.

### `achievement.increment.product-update`

Increments from a product update. Requires `id` (the product) and `definition`.

### `achievement.progress.reset`

Resets progress on an achievement. Requires `profile_id` and `handle`.

## Profile updates

### `update.profile.status`

Adds or removes profile statuses.

| Argument     | Required | Description                                             |
| ------------ | -------- | ------------------------------------------------------- |
| `add`        | Yes      | Boolean. True adds, false removes.                      |
| `status`     | No       | A single status handle.                                 |
| `statuses`   | No       | Array of status handles, as an alternative to `status`. |
| `removes`    | No       | Statuses to remove in the same call.                    |
| `profile_id` | No       | Defaults to the context profile.                        |

### `update.profile.status.with.expiry`

As above, plus `expires_at` and `timezone` so the status lapses on its own.

### `update.profile.tags`

Adds or removes tags. Requires `profile_id`; optional `add` and `remove` arrays, and `trigger_event`.

### `update.profile`

Writes attributes onto the profile. Requires `id` and `attributes`; optional `trigger_event`.

### `update.profile.attributes.comms`

Writes comms preferences. Requires `id` and `attributes`.

### `create.custom.attribute`

Writes a custom attribute.

| Argument     | Required | Description                                                  |
| ------------ | -------- | ------------------------------------------------------------ |
| `profile_id` | Yes      | The profile.                                                 |
| `namespace`  | Yes      | Attribute namespace.                                         |
| `handle`     | Yes      | Attribute handle.                                            |
| `type`       | Yes      | `string`, `integer`, `float`, `boolean`, `json`, or `array`. |
| `value`      | No       | The value to write.                                          |
| `is_date`    | No       | Treat the value as a date.                                   |

### `update.highest.status.attribute`

Resolves the profile's highest status in a group and writes that handle to a custom attribute. Only writes when the resolved status is higher than the stored one.

| Argument                      | Required | Description                                               |
| ----------------------------- | -------- | --------------------------------------------------------- |
| `group_handle`                | Yes      | Status group to read.                                     |
| `namespace`, `handle`, `type` | Yes      | Where to write, as `create.custom.attribute`.             |
| `direction`                   | No       | `asc` or `desc`, controlling which end counts as highest. |

### `delete.profile`

Deletes the profile. Requires `id`.

## Outbound

### `target.send`

Sends a Twig-templated payload to a configured Target.

| Argument | Required | Description          |
| -------- | -------- | -------------------- |
| `target` | Yes      | Target handle or id. |

See [Targets](/dev-guides/reactions/targets).

### `webhook.send`

Posts to a URL directly, without a Target.

| Argument | Required | Description                             |
| -------- | -------- | --------------------------------------- |
| `url`    | Yes      | Destination.                            |
| `event`  | Yes      | Event name to send.                     |
| `data`   | No       | Payload. Defaults to the event context. |

## Lists

### `lists.create`

Creates one or more lists for a profile. Requires `profile_id` and `lists`, where each entry needs `definition_handle` and may carry `tags` and `custom_fields` (each field needing `namespace`, `handle`, and `type`).

### `update.list.item`

Updates a list item's status or position. Requires `profile_id`, `definition_handle`, `sku`, and `attributes`, where attributes may hold `status` and `position`.

### `delete.list.reservation`

Deletes a reservation. Requires `id`.

## Transactions and products

### `transaction.update`

Writes attributes onto a transaction. Requires `id` and `attributes`; optional `event` to dispatch afterwards.

### `profile.transaction.event.dispatch`

Re-dispatches an event for a profile's transactions in a window. Requires `profile_id` and `event`; optional `start_time`, `end_time`, and `filter`.

### `update.variant.sellingfast`

Flags a variant as selling fast. Requires `selling_fast_threshold`, `transacted_at`, and one of `product_variant_id` or `sku`; optional `timezone`.

## Connections

### `connection.create`

Connects two profiles. Requires `connected_id`, `connector_id`, and `connection_definition`. Optional `status` (`draft`, `pending`, `accepted`, `rejected`, `hold`, `archived`, `break`), `external_id` with `external_id_type`, `name`, `meta`, `is_published`, and `organisation_id`.

<Note>
  CX Manager may list an action that is not on this page. A few are deliberately left out: some exist to serve one instance's specific arrangement and do not carry over to others, and some are registered but unused in practice. If you are considering one that is not documented here, ask before building on it.
</Note>

## Related

* [Reactions](/concepts/automation/reactions) for the concept and where actions sit
* [Reactions Overview](/dev-guides/reactions/overview) for building a reaction
* [Reaction Filters](/dev-guides/reactions/filters) and [JSON Conditions](/dev-guides/reactions/json-conditions)
* [Automation Queries](/dev-guides/reactions/automation-queries) for the query side
* [Event Contexts](/dev-guides/webhooks/event-contexts) for what `is_dynamic` can read
