Skip to content

None of these have been run against live Gmail, Slack, Google Sheets or Shopify accounts yet.

Exercise 1

Replace the No Operation node with IF {{ $json.drafts.length > 0 }} → Gmail Send (or Slack):

Subject: {{ $json.drafts.length }} product descriptions to review ({{ $json.needs_review }} need attention)

{{ $json.drafts.map(d => (d.status === 'ready' ? '✓ ' : '⚠ ') + d.title + (d.issues.length ? ' (' + d.issues.join('; ') + ')' : '')).join('\n') }}

{{ $json.remaining }} more products are waiting for the next run.
Review: http://localhost:5678/webhook/pd/review

Exercise 2

  1. After Queue for Review, add Split Out on drafts → Google Sheets Append or Update Row (match on product_id) with columns product_id, title, status, issues, description, bullets, seo_title, meta_description, decision, note. Use {{ $json.issues.join('; ') }} and {{ $json.copy.bullets.join('\n') }}.
  2. New workflow: Schedule every 15 min → Google Sheets Get Row(s) filtered on decision not empty → HTTP Request POST http://localhost:5678/webhook/pd/review/decide:
    { "product_id": "{{ $json.product_id }}", "decision": "{{ $json.decision.toLowerCase() }}",
      "note": "{{ $json.note }}", "edits": { "description": "{{ $json.description }}" } }
    
    Sending the sheet's description as an edit means reviewers can fix the wording right in the sheet.
  3. → Google Sheets Update Row: set decision to done: {{ $json.published ? 'published' : $json.state }} so the row isn't processed twice.

Exercise 3

In decide.js, include seo_title: final.seo_title, meta_description: final.meta_description in the output. After Update Shopify Product, add two HTTP Request nodes (Shopify credential, On Error → Continue):

POST https://{{ shop }}.myshopify.com/admin/api/2024-10/products/{{ $('Apply Decision').first().json.product_id }}/metafields.json
{ "metafield": { "namespace": "global", "key": "title_tag", "type": "single_line_text_field",
                 "value": "{{ $('Apply Decision').first().json.seo_title }}" } }

and the same with key: "description_tag" and the meta description. In published.js, report both results.

Stretch

In select.js keep image: p.image?.src. Replace the Basic LLM Chain with OpenAI Message a Model (a vision-capable model) with the image URL as an input image and the same prompt, asking for structured JSON. In check.js, add the issue visual details: confirm against the photo whenever the source is AI and an image was used, so a person always checks them.