← Files WixARCHIVED FILE

skills/wix-manage/references/ecommerce/api-recommendation-tracking.md

12.3 KB · Oct 3, 2026 · 06:02 UTC

↓ Download file

---
name: "API: Recommendation Tracking"
description: CRUD API for persisting and managing the lifecycle of AI-generated recommendations. Tracks state transitions from PROPOSED through APPROVED, EXECUTING, to DONE/FAILED/REJECTED. Every recommendation must be persisted here before presenting to the merchant.
layer: config
---
# API: Recommendation Tracking Service

This service persists recommendations in a database and tracks their lifecycle state. Every recommendation generated by any domain (discounts, shipping, etc.) **MUST** be persisted here before being presented to the merchant.

**Service**: `wix.ecom.agentic.recommendations.v1.AgenticRecommendationsService`

**Base URL**: `https://manage.wix.com/_api/agentic-recommendations/v1/agentic-recommendations`

## How to call these APIs

Use `CallWixSiteAPI` to invoke each endpoint:

```
CallWixSiteAPI(
  url: "https://manage.wix.com/_api/agentic-recommendations/v1/agentic-recommendations/<endpoint>",
  method: "POST",
  body: { ... }
)
```

---

## Data Model

### Recommendation

| Field | Type | Access | Description |
|---|---|---|---|
| `id` | string (GUID) | readOnly | Auto-generated unique identifier |
| `revision` | int64 | readOnly | Optimistic lock — must match current value for state transitions |
| `createdDate` | Timestamp | readOnly | When the recommendation was created |
| `updatedDate` | Timestamp | readOnly | When the recommendation was last updated |
| `title` | string | writable | Short human-readable title (max 200 chars) |
| `reasoning` | string | writable | Why this recommendation is being made, citing specific data (max 2000 chars) |
| `domain` | string | writable | Business domain: `"shipping"`, `"discounts"`, etc. (max 50 chars) |
| `urgency` | enum | writable | `CRITICAL`, `HIGH`, `MEDIUM`, or `LOW` |
| `advice` | Advice | writable | The action to take (see below) |
| `state` | enum | readOnly | Current lifecycle state (see State Machine) |
| `expiresAt` | Timestamp | writable | When the recommendation auto-expires |
| `conversationId` | string (GUID) | writable | Links all recommendations in a session |
| `decidedBy` | string | readOnly | Who made the last state transition |
| `rejectionReason` | string | readOnly | Reason for rejection (max 1000 chars) |
| `rejectionPermanent` | boolean | readOnly | If true, this action type is permanently blocked for this site |
| `executionResult` | ExecutionResult | readOnly | Result of execution (set on DONE/FAILED) |

### Advice

| Field | Type | Description |
|---|---|---|
| `action` | string | Action identifier (e.g., `"apply_discount"`, `"create_shipping_option"`) (max 100 chars) |
| `params` | object | Domain-specific parameters (discount config, shipping option details, etc.) |
| `successCriteria` | string | How to verify the action succeeded (max 500 chars) |

### ExecutionResult

| Field | Type | Description |
|---|---|---|
| `status` | enum | `SUCCESS`, `PARTIAL_FAILURE`, or `FAILURE` |
| `summary` | string | Description of what happened (max 2000 chars) |
| `error` | string | Error message if failed (max 2000 chars) |
| `resultPayload` | object | Additional result data |

---

## State Machine

```
PROPOSED → APPROVED → EXECUTING → DONE
    │                      └──→ FAILED
    ├──→ REJECTED
    └──→ EXPIRED (scheduler)
```

| From | To | Trigger | API Method |
|---|---|---|---|
| — | PROPOSED | Recommendation created | BatchCreate / Create |
| PROPOSED | APPROVED | Merchant approves | Approve |
| PROPOSED | REJECTED | Merchant rejects | Reject |
| PROPOSED | EXPIRED | `expiresAt` passed | Automatic (scheduler) |
| PROPOSED | PROPOSED | Merchant modifies parameters | Update (state unchanged) |
| APPROVED | EXECUTING | Execution begins | MarkExecuting |
| EXECUTING | DONE | Execution succeeded | MarkDone |
| EXECUTING | FAILED | Execution failed | MarkFailed |

---

## BatchCreate

Creates multiple recommendations (1-100) atomically. All start in PROPOSED state. **Use this as the default** — always batch recommendations together.

**Endpoint**: `POST https://manage.wix.com/_api/agentic-recommendations/v1/agentic-recommendations/batch-create`

**Request**:
```json
{
  "agenticRecommendations": [
    {
      "title": "15% Off Electronics — Orders Over $200",
      "reasoning": "AOV is $165. Electronics avg margin 42%. $200 threshold incentivizes adding one more item.",
      "domain": "discounts",
      "urgency": "HIGH",
      "advice": {
        "action": "apply_discount",
        "params": {
          "mechanism": "AUTOMATIC",
          "scope": "CATEGORY",
          "discountType": "PERCENTAGE",
          "discount": 15,
          "conditions": { "minSubTotal": 200 }
        },
        "successCriteria": "15% discount applied to Electronics for orders above $200"
      }
    }
  ],
  "conversationId": "<conversationId>"
}
```

**Response**:
```json
{
  "results": [
    {
      "agenticRecommendation": {
        "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
        "revision": "1",
        "state": "PROPOSED",
        "title": "15% Off Electronics — Orders Over $200",
        "reasoning": "...",
        "domain": "discounts",
        "urgency": "HIGH",
        "advice": { "..." : "..." },
        "conversationId": "...",
        "createdDate": "2026-05-10T10:00:00Z",
        "updatedDate": "2026-05-10T10:00:00Z"
      }
    }
  ]
}
```

**Important**: Save the `id` and `revision` from each result — you need them for all subsequent state transitions.

---

## Create

Creates a single recommendation in PROPOSED state. Prefer BatchCreate for multiple recommendations.

**Endpoint**: `POST https://manage.wix.com/_api/agentic-recommendations/v1/agentic-recommendations`

**Request**:
```json
{
  "agenticRecommendation": {
    "title": "Add Free Shipping for Orders Over $75",
    "reasoning": "No free shipping option. delivery_step_cvr is 48%, below 65% benchmark. AOV is $62.",
    "domain": "shipping",
    "urgency": "HIGH",
    "advice": {
      "action": "create_shipping_option",
      "params": {
        "category": "free_shipping",
        "ids": ["region-guid"],
        "rates": [{ "amount": "0", "conditions": [{ "type": "BY_TOTAL_PRICE", "operator": "GTE", "value": "75" }] }]
      },
      "successCriteria": "Free shipping option created for orders above $75"
    },
    "conversationId": "<conversationId>"
  }
}
```

**Response**: Single `agenticRecommendation` object with `id`, `revision`, and `state: "PROPOSED"`.

---

## Query

Query recommendations by conversation ID, state, or domain. Returns paginated results.

**Endpoint**: `POST https://manage.wix.com/_api/agentic-recommendations/v1/agentic-recommendations/query`

**Request**:
```json
{
  "query": {
    "filter": {
      "conversationId": "<conversationId>",
      "state": { "$in": ["PROPOSED", "APPROVED"] }
    },
    "cursorPaging": { "limit": 50 }
  }
}
```

**Filterable fields**: `conversationId`, `state`, `domain`, `urgency`, `id`

**Response**:
```json
{
  "agenticRecommendations": [
    {
      "id": "a1b2c3d4-...",
      "revision": "1",
      "state": "PROPOSED",
      "title": "...",
      "domain": "discounts",
      "urgency": "HIGH",
      "advice": { "..." : "..." }
    }
  ],
  "pagingMetadata": { "count": 1, "cursors": {}, "hasNext": false }
}
```

---

## Get

Fetch a single recommendation by ID.

**Endpoint**: `GET https://manage.wix.com/_api/agentic-recommendations/v1/agentic-recommendations/{agenticRecommendationId}`

**Response**: Single `agenticRecommendation` object with all fields.

---

## Count

Get recommendation counts grouped by state for the current site.

**Endpoint**: `POST https://manage.wix.com/_api/agentic-recommendations/v1/agentic-recommendations/count`

**Request**: `{}`

**Response**:
```json
{
  "counts": {
    "PROPOSED": 3,
    "APPROVED": 1,
    "EXECUTING": 0,
    "DONE": 5,
    "FAILED": 1,
    "REJECTED": 2
  }
}
```

---

## Update

Update a PROPOSED recommendation. Only `title`, `reasoning`, `urgency`, `advice`, and `expiresAt` can be updated. State stays PROPOSED.

**Endpoint**: `PATCH https://manage.wix.com/_api/agentic-recommendations/v1/agentic-recommendations/{agenticRecommendationId}`

**Request**:
```json
{
  "agenticRecommendation": {
    "id": "a1b2c3d4-...",
    "revision": "1",
    "advice": {
      "action": "apply_discount",
      "params": { "discount": 20 },
      "successCriteria": "20% discount applied"
    }
  },
  "fieldMask": { "paths": ["advice"] }
}
```

**Response**: Updated `agenticRecommendation` with incremented `revision`.

**Constraint**: Only works on PROPOSED recommendations. Non-PROPOSED returns INVALID_STATE_TRANSITION.

---

## Approve

PROPOSED → APPROVED. Call when the merchant agrees with a recommendation.

**Endpoint**: `POST https://manage.wix.com/_api/agentic-recommendations/v1/agentic-recommendations/{agenticRecommendationId}/approve`

**Request**:
```json
{
  "revision": "1"
}
```

**Response**: Updated `agenticRecommendation` with `state: "APPROVED"` and incremented `revision`.

---

## Reject

PROPOSED → REJECTED. Call when the merchant declines.

**Endpoint**: `POST https://manage.wix.com/_api/agentic-recommendations/v1/agentic-recommendations/{agenticRecommendationId}/reject`

**Request**:
```json
{
  "revision": "1",
  "reason": "Merchant prefers flat-rate discount instead",
  "permanent": false
}
```

| Field | Type | Required | Description |
|---|---|---|---|
| `revision` | int64 | Yes | Current revision for optimistic locking |
| `reason` | string | No | Why the merchant rejected (max 1000 chars) |
| `permanent` | boolean | No | If true, permanently blocks this action type for this site |

**Response**: Updated `agenticRecommendation` with `state: "REJECTED"`.

---

## MarkExecuting

APPROVED → EXECUTING. Call immediately before applying the recommendation.

**Endpoint**: `POST https://manage.wix.com/_api/agentic-recommendations/v1/agentic-recommendations/{agenticRecommendationId}/mark-executing`

**Request**:
```json
{
  "revision": "2"
}
```

**Response**: Updated `agenticRecommendation` with `state: "EXECUTING"` and incremented `revision`.

---

## MarkDone

EXECUTING → DONE. Call after the recommendation was successfully applied.

**Endpoint**: `POST https://manage.wix.com/_api/agentic-recommendations/v1/agentic-recommendations/{agenticRecommendationId}/mark-done`

**Request**:
```json
{
  "revision": "3",
  "executionResult": {
    "status": "SUCCESS",
    "summary": "Created 15% discount rule for Electronics with $200 minimum"
  }
}
```

**Response**: Updated `agenticRecommendation` with `state: "DONE"` and `executionResult`.

---

## MarkFailed

EXECUTING → FAILED. Call when execution fails.

**Endpoint**: `POST https://manage.wix.com/_api/agentic-recommendations/v1/agentic-recommendations/{agenticRecommendationId}/mark-failed`

**Request**:
```json
{
  "revision": "3",
  "executionResult": {
    "status": "FAILURE",
    "summary": "Failed to create discount rule",
    "error": "REVISION_MISMATCH: discount rule was modified by another process"
  }
}
```

**Response**: Updated `agenticRecommendation` with `state: "FAILED"` and `executionResult`.

---

## Error Codes

| Error | HTTP | Meaning |
|---|---|---|
| RECOMMENDATION_SUPPRESSED | 400 | Action + site blocked by permanent rejection |
| INVALID_STATE_TRANSITION | 400 | Wrong source state for the requested transition |
| VERSION_MISMATCH | 400 | Optimistic lock conflict — re-fetch and retry |
| RECOMMENDATION_NOT_FOUND | 404 | ID doesn't exist |

On VERSION_MISMATCH: Query to get the latest revision, then retry the operation with the updated revision.

---

## Integration pattern for all recommendation flows

Every recommendation flow MUST follow this lifecycle:

```
Step 1: Generate recommendations (domain-specific analysis)
Step 2: BatchCreate → persist ALL recommendations as PROPOSED (save id + revision for each)
Step 3: Present recommendations to merchant (include id + revision)
Step 4: On each merchant action:
  - APPROVE: Approve → MarkExecuting → apply the change → MarkDone or MarkFailed
  - REJECT:  Reject (with reason, permanent flag)
  - MODIFY:  Update (field mask) → stays PROPOSED → re-present
```

**Rules**:
- Every recommendation MUST be persisted before presenting to the merchant
- Always use the latest `revision` from the previous API response
- Track ALL outcomes — both success (MarkDone) and failure (MarkFailed)
- On VERSION_MISMATCH: re-fetch with Query, get latest revision, retry
- Never skip the EXECUTING state — always call MarkExecuting before applying

SHA-256: 15286069e163bf12aeec4d74a468d7d2243a71500792fc23dea7be0b2124016e