{
  "name": "Style each customer's purchase with your newest arrival (post-purchase email)",
  "nodes": [
    {
      "parameters": {
        "authentication": "accessToken",
        "topic": "orders/create"
      },
      "id": "3a64be09-86db-438f-b9d6-fbfa684a22e8",
      "name": "New Order",
      "type": "n8n-nodes-base.shopifyTrigger",
      "typeVersion": 1,
      "position": [
        0,
        0
      ]
    },
    {
      "parameters": {
        "authentication": "accessToken",
        "resource": "product",
        "operation": "get",
        "productId": "={{ $json.line_items[0].product_id }}",
        "additionalFields": {}
      },
      "id": "e33c20a0-c3fa-4f12-8797-a365094fb098",
      "name": "Get Purchased Item",
      "type": "n8n-nodes-base.shopify",
      "typeVersion": 1,
      "position": [
        224,
        0
      ]
    },
    {
      "parameters": {
        "authentication": "accessToken",
        "resource": "product",
        "operation": "getAll",
        "returnAll": true,
        "additionalFields": {}
      },
      "id": "f9a91881-cd79-479a-9399-020de2caae91",
      "name": "Get Catalog",
      "type": "n8n-nodes-base.shopify",
      "typeVersion": 1,
      "position": [
        448,
        0
      ],
      "executeOnce": true
    },
    {
      "parameters": {
        "jsCode": "const purchased = $('Get Purchased Item').first().json;\nconst order = $('New Order').first().json;\nconst arrival = $('Get Catalog').all()\n  .map(i => i.json)\n  .filter(p => p.id !== purchased.id && (p.images || []).length > 0)\n  .sort((a, b) => new Date(b.created_at) - new Date(a.created_at))[0];\nif (!arrival) return [];\nreturn [{ json: {\n  customerEmail: order.email || 'unknown',\n  purchasedTitle: purchased.title,\n  purchasedImage: (purchased.images || [])[0]?.src,\n  arrivalTitle: arrival.title,\n  arrivalImage: arrival.images[0].src\n} }];"
      },
      "id": "77152326-7199-457c-ad28-076e1bbd7c1f",
      "name": "Pick Newest Arrival",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        672,
        0
      ]
    },
    {
      "parameters": {
        "resume": "webhook",
        "httpMethod": "POST",
        "limitWaitTime": true,
        "resumeAmount": 20,
        "resumeUnit": "minutes",
        "options": {}
      },
      "id": "d4eb15d1-0bc9-4292-8feb-b490017a5b88",
      "name": "Wait for Render",
      "type": "n8n-nodes-base.wait",
      "typeVersion": 1.1,
      "position": [
        1104,
        0
      ]
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "loose",
            "version": 1
          },
          "conditions": [
            {
              "leftValue": "={{ $json.body?.status ?? $json.status }}",
              "operator": {
                "type": "string",
                "operation": "equals"
              },
              "rightValue": "completed"
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "id": "a0d14db8-99c2-4292-a583-ab4beeba6374",
      "name": "Is Complete?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.2,
      "position": [
        1328,
        0
      ]
    },
    {
      "parameters": {
        "authentication": "oAuth2",
        "select": "channel",
        "channelId": {
          "__rl": true,
          "mode": "list",
          "value": ""
        },
        "text": "=:dress: *Your Closet pairing* for {{ $('Pick Newest Arrival').item.json.customerEmail }}\nOwns: {{ $('Pick Newest Arrival').item.json.purchasedTitle }} · New arrival: {{ $('Pick Newest Arrival').item.json.arrivalTitle }}\nStyled together: {{ $('Wait for Render').item.json.body.outputs[0].files[0].url }}\nDrop it in the post-purchase email.",
        "otherOptions": {}
      },
      "id": "bd4107b7-cf0c-4fbf-81b6-8d24fa4c3612",
      "name": "Deliver Pairing",
      "type": "n8n-nodes-base.slack",
      "typeVersion": 2.5,
      "position": [
        1552,
        -96
      ]
    },
    {
      "parameters": {
        "authentication": "oAuth2",
        "select": "channel",
        "channelId": {
          "__rl": true,
          "mode": "list",
          "value": ""
        },
        "text": "Your Closet pairing render timed out (or the paired-look request was rejected).",
        "otherOptions": {}
      },
      "id": "8d25106d-8097-4c01-a5a0-dd327bef89de",
      "name": "Timed Out",
      "type": "n8n-nodes-base.slack",
      "typeVersion": 2.5,
      "position": [
        1552,
        144
      ]
    },
    {
      "parameters": {
        "content": "## 🟢 Your Closet pairing\nOn every **new order**: takes the item the customer just bought + the store's **newest arrival**, and asks Dreem to render them **styled together on one model**. The new arrival is the main garment; the purchased item goes in as a **Styling Image** (outfit piece) on the Dreem node. The look lands in Slack for the post-purchase email.\n\n**Do:** create + pick your credentials (Settings → Credentials) — **Dreem API** (your `dreem_pk_...` key) on *Dreem: Generate Paired Look* · **Shopify Access Token API** (shop domain + Admin token, `read_products` + `read_orders`) on *New Order*, *Get Purchased Item* + *Get Catalog* · **Slack OAuth2** (on n8n Cloud just press *Connect*; or a *Slack API* bot token — then switch each Slack node's **Authentication** to *Access Token*) on *Deliver Pairing* + *Timed Out*. Pick **your channel** on both Slack nodes, set your model (talentId), then toggle **Active**.",
        "height": 304,
        "width": 864,
        "color": 7
      },
      "id": "734d4a4e-5ab8-4575-bb88-8bbb7aaf0850",
      "name": "Setup — Start Here",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        -48,
        -496
      ]
    },
    {
      "parameters": {
        "authentication": "apiKey",
        "talentId": "b2c80190-4a00-4718-9d3f-c531472d5984",
        "shotCodes": [
          "model_front_women"
        ],
        "frontImageUrl": "={{ $json.arrivalImage }}",
        "stylingImageUrls": {
          "imageValues": [
            {
              "url": "={{ $json.purchasedImage }}"
            }
          ]
        },
        "outputFormat": "jpeg",
        "outputAspectRatio": 5,
        "callbackUrl": "={{ $execution.resumeUrl }}",
        "operation": "generateModelShots"
      },
      "id": "6eb780eb-6a9e-41e9-87bc-15692367f77e",
      "name": "Dreem: Generate Paired Look",
      "type": "n8n-nodes-dreem.dreem",
      "typeVersion": 1,
      "position": [
        880,
        0
      ]
    },
    {
      "parameters": {
        "content": "### 📬 Ship it in your ESP\nThis template stops in Slack so a human approves the pairing. To send it to the customer, wire the approved render into your favourite email service provider (e.g. **Klaviyo**, **Drip**):\n- Fire a custom event (e.g. `Closet Pairing Ready`) on the customer's profile with the image URL + both product titles, and let an ESP flow send the post-purchase email.\n- Klaviyo: Klaviyo node or HTTP → Events API. Drip / Mailchimp / Omnisend: same event-or-field pattern.\n\nThe render URL is `{{ $('Wait for Render').item.json.body.outputs[0].files[0].url }}`; the customer email comes from **Pick Newest Arrival**.",
        "height": 564,
        "width": 616,
        "color": 4
      },
      "id": "cac2e763-c24b-481e-ac61-016e46fbfbf2",
      "name": "Note — Ship to your ESP",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        1920,
        -128
      ]
    },
    {
      "parameters": {
        "content": "### ⚙️ Generate Paired Look — set these\n- **Talent** (talentId) — select the model to generate on. Browse all talents inside [Dreem.ai](https://dreem.ai).\n- **Poses** (shotCodes) — pick the pose(s) you want; match them to the talent's gender (e.g. `model_front_women`).\n- **Output dimensions** (Output Aspect Ratio) — e.g. **4:5** for PDP, **9:16** for Story, **1:1** for feed.\n- **Styling items** (Styling Image URLs, *optional*) — add garment images to style the look with. Here it's the customer's **past purchase**. Leave it empty and **Dreem styles the outfit itself**.",
        "height": 320,
        "width": 476,
        "color": 5
      },
      "id": "5409ad95-af08-4fc9-98ea-b91865c95997",
      "name": "Note — Paired Look Settings",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        800,
        240
      ]
    }
  ],
  "connections": {
    "New Order": {
      "main": [
        [
          {
            "node": "Get Purchased Item",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Purchased Item": {
      "main": [
        [
          {
            "node": "Get Catalog",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Catalog": {
      "main": [
        [
          {
            "node": "Pick Newest Arrival",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Wait for Render": {
      "main": [
        [
          {
            "node": "Is Complete?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Is Complete?": {
      "main": [
        [
          {
            "node": "Deliver Pairing",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Timed Out",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Pick Newest Arrival": {
      "main": [
        [
          {
            "node": "Dreem: Generate Paired Look",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Dreem: Generate Paired Look": {
      "main": [
        [
          {
            "node": "Wait for Render",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "settings": {
    "executionOrder": "v1",
    "binaryMode": "separate"
  }
}