# Shopify Flow Integration

Appstle Loyalty provides native Shopify Flow integration with 8 triggers and 6 actions. These extensions are defined in the app configuration and processed through `ShopifyFlowService`.

## Triggers

Triggers fire when loyalty events occur, sending customer and loyalty data to Shopify Flow.

| Trigger | Handle | Description |
|  --- | --- | --- |
| **Loyalty Sign Up** | `loyalty-sign-up` | Customer signs up for the loyalty program |
| **Loyalty Points Earned** | `loyalty-points-earned` | Customer earns loyalty points |
| **Loyalty Points Redeemed** | `loyalty-points-redeemed` | Customer redeems loyalty points |
| **Loyalty Credits Earned** | `loyalty-credits-earned` | Customer earns store credits |
| **Loyalty VIP Tier Achieved** | `loyalty-vip-tier-achieved` | Customer achieves a VIP tier |
| **Customer's Birthday** | `birthday-trigger` | Triggered on customer's birth date |
| **Referral Reward Created** | `referral-reward-created` | Reward created for the referring customer |
| **Referred Reward Created** | `referred-reward-created` | Reward created for the referred customer |


### Trigger Properties

All triggers include these fields:

**Top-level fields:**

- `customer_reference` — Shopify customer reference
- `Note` (String) — Activity description


**Points/Credits triggers only** (`loyalty-points-earned`, `loyalty-points-redeemed`, `loyalty-credits-earned`):

- `Points` / `Credits` (Decimal) — Amount earned/redeemed
- `Earn Rule ID` / `Redeem Rule ID` (Decimal) — Activity rule ID


**Customer Loyalty Details** (nested object):

| Field | Type | Description |
|  --- | --- | --- |
| `availablePoints` | Float | Current available points balance |
| `pendingPoints` | Float | Points pending approval |
| `creditedPoints` | Float | Total lifetime credited points |
| `spentAmount` | Float | Total amount spent by customer |
| `currentVipTier` | String | Current VIP tier name (if applicable) |
| `rewardedForFacebook` | Boolean | Completed Facebook like |
| `rewardedForTwitter` | Boolean | Completed Twitter follow |
| `rewardedForInstagram` | Boolean | Completed Instagram follow |
| `rewardedForYoutube` | Boolean | Completed YouTube subscribe |
| `rewardedForTiktok` | Boolean | Completed TikTok follow |
| `rewardedForNewsLetter` | Boolean | Subscribed to newsletter |
| `rewardedForSms` | Boolean | Subscribed to SMS |
| `rewardedForCreatingAccount` | Boolean | Created store account |
| `referredCompleted` | Int | Number of successful referrals |
| `referralLink` | String | Customer's referral URL |
| `dob` | String | Date of birth (ISO format) |
| `rewards` | Array | List of `CustomerReward` objects |


**CustomerReward object:**

| Field | Type | Description |
|  --- | --- | --- |
| `description` | String | Reward description |
| `pointTransactionId` | Int | Internal transaction ID |
| `pointRedeemRuleId` | Int | Redemption rule ID |
| `discountCode` | String | Generated discount code |
| `usedAt` | String | When reward was used |
| `orderId` | ID | Shopify order ID (if used) |
| `orderName` | String | Shopify order name |
| `status` | RewardStatus | `USED`, `UNUSED`, or `REFUNDED` |
| `createAt` | String | Creation timestamp |
| `expireDate` | String | Expiration timestamp |
| `variantId` | ID | Product variant ID (if applicable) |


:::note
The GraphQL schema for Flow exposes a subset of fields from `CustomerLoyaltyResponseVM`. Fields like `storeCreditBalance`, `rewardedForPinterest`, `rewardedForSharingOnFacebook`, `rewardedForSharingOnX`, `customerStatus`, `achievableTierId`, and `vipTierExpiredAt` exist in the Java DTO but are not currently exposed to Shopify Flow.
:::

## Actions

Actions allow Shopify Flow workflows to modify loyalty data.

| Action | Handle | Endpoint | Description |
|  --- | --- | --- | --- |
| **Add Points** | `add-points` | `POST /shopify-flow/add-points` | Award loyalty points |
| **Remove Points** | `remove-points` | `POST /shopify-flow/remove-points` | Deduct loyalty points |
| **Add Store Credits** | `add-store-credits` | `POST /shopify-flow/add-store-credits` | Award store credits |
| **Remove Store Credits** | `remove-store-credits` | `POST /shopify-flow/remove-store-credits` | Deduct store credits |
| **Reward Points for Reviews** | `reward-points-for-reviews` | `POST /shopify-flow/reward-points-for-reviews` | Award points for product reviews |
| **Assign VIP Tier** | `assign-vip-tier` | `POST /shopify-flow/assign-vip-tier` | Assign or change a customer's VIP tier |


### Add Points

**Fields:**

| Field | Key | Type | Required | Description |
|  --- | --- | --- | --- | --- |
| Customer Identifier | `customer-identifier` | String | ✅ | Customer ID or email (use `{{customer.email}}`) |
| Points To Add | `pointsToAdd` | String | ❌ | Points to award (e.g., 10) |
| Rule Id | `rule-id` | Integer | ❌ | Static rule ID from Appstle Loyalty |
| Reason | `reason` | String | ✅ | Visible to customer |


:::caution
The field `points` (Decimal) is deprecated. Use `pointsToAdd` instead. If unavailable, remove and re-add the action.
:::

### Remove Points

**Fields:**

| Field | Key | Type | Required | Description |
|  --- | --- | --- | --- | --- |
| Customer | — | `customer_reference` | ✅ | Shopify customer reference |
| Points To Remove | `pointsToRemove` | String | ✅ | Points to deduct |
| Reason | `reason` | String | ✅ | Visible to customer |


### Add Store Credits

**Fields:**

| Field | Key | Type | Required | Description |
|  --- | --- | --- | --- | --- |
| Customer | — | `customer_reference` | ✅ | Shopify customer reference |
| Store Credits To Add | `storeCreditsToAdd` | String | ✅ | Amount in store currency |
| Reason | `reason` | String | ✅ | Visible to customer |


### Remove Store Credits

**Fields:**

| Field | Key | Type | Required | Description |
|  --- | --- | --- | --- | --- |
| Customer | — | `customer_reference` | ✅ | Shopify customer reference |
| Store Credits To Remove | `storeCreditsToRemove` | String | ✅ | Amount to remove |
| Reason | `reason` | String | ✅ | Visible to customer |


### Reward Points for Reviews

**Fields:**

| Field | Key | Type | Required | Description |
|  --- | --- | --- | --- | --- |
| Customer Email | `customer-email` | String | ✅ | Customer email variable |
| Product ID | `product-id` | String | ✅ | Product ID variable |
| Rating | `review-rating` | String | ✅ | Review rating value |
| Review Type | `review-type` | String | ✅ | One of: `REVIEWS_IO`, `AIR_REVIEWS`, `OKENDO`, `LEAVE_REVIEW_LOOX_IO`, `LEAVE_REVIEW_STAMPED_IO`, `LEAVE_REVIEW_PRODUCT_REVIEWS` |
| Images Count | `image-count` | String | ❌ | Number of images (if applicable) |
| Videos Count | `video-count` | String | ❌ | Number of videos (if applicable) |


### Assign VIP Tier

**Fields:**

| Field | Key | Type | Required | Description |
|  --- | --- | --- | --- | --- |
| Customer Identifier | `customer-identifier` | String | ✅ | Customer ID or email (use `{{customer.email}}`) |
| VIP Tier Name | `vip-tier-name` | String | ✅ | Exact name of the VIP tier to assign (must match a tier configured in your loyalty program) |
| Lock Tier | `lock-tier` | String | ❌ | Set to `"true"` to lock the customer at this tier and prevent automatic recalculation from changing it. Default is `"false"`. |
| Reason | `reason` | String | ❌ | Reason for assigning this VIP tier |


:::note
The tier name must exactly match a VIP tier configured in your Appstle Loyalty settings (e.g., "Gold", "Silver"). If the customer is already on the specified tier, no changes are made.
:::

## Flow Templates

Appstle provides pre-built Flow templates for common review integrations:

| Template | Handle | Description |
|  --- | --- | --- |
| Points for Reviews.io | `points-for-reviews-io` | Auto-reward Reviews.io submissions |
| Points for Okendo Reviews | `points-for-okendo-reviews` | Auto-reward Okendo reviews |
| Points for Loox Reviews | `points-for-loox-reviews` | Auto-reward Loox reviews |
| Points for Air Reviews | `points-for-air-reviews` | Auto-reward Air reviews |
| Points for Rivyo Reviews | `appstle-review-collected-loyalty-points` | Auto-reward Rivyo Reviews submissions |


## Example Workflows

### Welcome Email on Sign Up

```
Trigger: Loyalty Sign Up
  ↓
Action: Send email marketing
  Customer Email: {{customer.email}}
  Subject: "Welcome to our Loyalty Program!"
```

### Tag VIP Customers

```
Trigger: Loyalty VIP Tier Achieved
  Condition: currentVipTier equals "Gold"
  ↓
Action: Add customer tags
  Tags: "vip-gold"
```

### Birthday Reward

```
Trigger: Customer's Birthday
  ↓
Action: Add Points
  Customer Identifier: {{customer.email}}
  Points To Add: 100
  Reason: "Happy Birthday! 🎉"
```

### Assign VIP Tier via Flow

```
Trigger: Any Flow trigger or condition
  ↓
Action: Assign VIP Tier
  Customer Identifier: {{customer.email}}
  VIP Tier Name: "Gold"
  Reason: "Upgraded to Gold tier via Shopify Flow"
```

### Review Points (Okendo)

```
Trigger: Okendo review submitted (via Okendo integration)
  ↓
Action: Reward Points for Reviews
  Customer Email: {{customer.email}}
  Product ID: {{product.id}}
  Rating: {{review.rating}}
  Review Type: "OKENDO"
```

## Technical Reference

**Extension Location:** `theme-app-extension-v4/extensions/`

**Source Files:**

- `ShopifyFlowService.java` — Trigger dispatch logic
- `ShopifyFlowResource.java` — Action REST endpoints
- `ShopifyFlowActionRequest.java` — Action request DTO


**Authentication:** Actions use Shopify's JWT validation via `ShopifyFlowJwtFilter`.