# MerchPilot Plan Payload v1

This document defines the supported plan payload format for MerchPilot VNext.

## Versioned payload shape

```json
{
  "version": 1,
  "generated_at": "2026-02-23 12:00:00",
  "changes": [
    {
      "type": "category_position",
      "sku": "SKU-RED-TSHIRT",
      "category_id": 12,
      "position": 2
    },
    {
      "type": "category_assignment",
      "sku": "SKU-BLUE-JACKET",
      "category_id": 15,
      "assigned": true
    },
    {
      "type": "price",
      "sku": "SKU-BLACK-SHOE",
      "price": 84.99,
      "special_price": 79.99,
      "special_from": "2026-02-23",
      "special_to": "2026-03-01"
    }
  ]
}
```

## Backward compatibility

MerchPilot accepts payloads where the root is just an array of changes:

```json
[
  {"type": "category_position", "sku": "SKU-1", "category_id": 12, "position": 3}
]
```

It will be normalized internally to version `1`.

## Supported change types

## `category_position`

Required fields:
- `type` = `category_position`
- `sku` (string)
- `category_id` (int)
- `position` (int)

Effect:
- Updates target category position inside `extension_attributes.category_links` via product update endpoint.

## `category_assignment`

Required fields:
- `type` = `category_assignment`
- `sku` (string)
- `category_id` (int)
- `assigned` (bool, default `true`)

Effect:
- Adds/removes category link for SKU while preserving unrelated links.

## `price`

Required fields:
- `type` = `price`
- `sku` (string)
- `price` (number)

Optional fields:
- `special_price` (number|null)
- `special_from` (date string)
- `special_to` (date string)

Effect:
- Updates base price and optional special price/date attributes.

## Validation and block reasons

During preview generation, changes may be blocked by safety rules:

- `missing_type_or_sku`
- `rules_disabled`
- `invalid_category_position_payload`
- `invalid_category_assignment_payload`
- `invalid_price_payload`
- `price_delta_exceeds_threshold`
- `max_changes_per_run_exceeded`
- `unsupported_change_type`

## Safety rules used

Store-level rules:
- `max_price_delta_percent`
- `max_changes_per_run`
- `allow_out_of_stock_switch`
- `is_enabled`

## Notes

- Preview must be generated before approve/apply.
- Apply skips already-matching state (idempotent behavior).
- Snapshots are created before write calls to support rollback.
