{
  "name": "Workflow B \u2014 AI Listing Engine",
  "nodes": [
    {
      "id": "wb000001-0001-0001-0001-000000000001",
      "name": "Listing Input Webhook",
      "type": "n8n-nodes-base.webhook",
      "typeVersion": 2,
      "position": [
        200,
        300
      ],
      "parameters": {
        "path": "realestate/listing-input",
        "httpMethod": "POST",
        "responseMode": "lastNode",
        "options": {}
      }
    },
    {
      "id": "wb000002-0002-0002-0002-000000000002",
      "name": "Validate Inputs",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        440,
        300
      ],
      "parameters": {
        "language": "javaScript",
        "jsCode": "const body = $input.first().json.body ?? $input.first().json;\nconst errors = [];\n\nconst required = ['propertyId','rawNotes','bedrooms','bathrooms','location','area','priceGhs','propertyType','listingType'];\nfor (const f of required) {\n  if (body[f] === undefined || body[f] === null || body[f] === '') {\n    errors.push('Missing required field: ' + f);\n  }\n}\n\nconst PROPERTY_TYPES = ['APARTMENT','HOUSE','LAND','COMMERCIAL','SELF_CONTAIN','CHAMBER_AND_HALL','BOYS_QUARTERS'];\nconst LISTING_TYPES  = ['SALE','RENT'];\n\nif (!errors.length) {\n  if (typeof body.bedrooms !== 'number' || body.bedrooms < 0) errors.push('bedrooms must be a non-negative number');\n  if (typeof body.bathrooms !== 'number' || body.bathrooms < 0) errors.push('bathrooms must be a non-negative number');\n  if (typeof body.priceGhs !== 'number' || body.priceGhs < 0) errors.push('priceGhs must be a non-negative number');\n  if (!PROPERTY_TYPES.includes(body.propertyType)) errors.push('propertyType must be one of: ' + PROPERTY_TYPES.join(', '));\n  if (!LISTING_TYPES.includes(body.listingType)) errors.push('listingType must be SALE or RENT');\n  if (typeof body.area !== 'string' || !body.area.trim()) errors.push('area must be a non-empty string');\n}\n\nif (errors.length) {\n  return [{ json: { _validationFailed: true, errors, propertyId: body.propertyId ?? null, body } }];\n}\n\nreturn [{ json: { _validationFailed: false, errors: [], ...body } }];"
      }
    },
    {
      "id": "wb000003-0003-0003-0003-000000000003",
      "name": "Inputs Valid?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2,
      "position": [
        680,
        300
      ],
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "loose"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "cond-valid",
              "leftValue": "={{ $json._validationFailed }}",
              "rightValue": false,
              "operator": {
                "type": "boolean",
                "operation": "equals"
              }
            }
          ]
        },
        "options": {}
      }
    },
    {
      "id": "wb000004-0004-0004-0004-000000000004",
      "name": "Create INPUTS_INVALID ReviewTask",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        920,
        480
      ],
      "onError": "continueRegularOutput",
      "parameters": {
        "method": "POST",
        "url": "={{ $env.TWENTY_API_URL + '/graphql' }}",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Authorization",
              "value": "={{ 'Bearer ' + $env.TWENTY_API_KEY }}"
            },
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        },
        "sendBody": true,
        "specifyBody": "json",
        "contentType": "json",
        "jsonBody": "={{ { query: 'mutation { createReviewTask(data: { kind: \"LISTING_APPROVAL\", subjectProperty: { connect: { where: { id: \"' + ($json.propertyId ?? 'unknown') + '\" } } } }) { id kind status } }' } }}",
        "options": {
          "timeout": 15000,
          "retry": {
            "enabled": true,
            "maxTries": 2,
            "waitBetweenTries": 2000
          }
        }
      }
    },
    {
      "id": "wb000005-0005-0005-0005-000000000005",
      "name": "Return Invalid Inputs Response",
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        1160,
        480
      ],
      "parameters": {
        "mode": "manual",
        "duplicateItem": false,
        "assignments": {
          "assignments": [
            {
              "id": "inv-ok",
              "name": "ok",
              "value": false,
              "type": "boolean"
            },
            {
              "id": "inv-errors",
              "name": "errors",
              "value": "={{ $('Validate Inputs').first()?.json?.errors ?? [] }}",
              "type": "array"
            }
          ]
        },
        "options": {}
      }
    },
    {
      "id": "wb000006-0006-0006-0006-000000000006",
      "name": "Voice Note Present?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2,
      "position": [
        920,
        260
      ],
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "loose"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "cond-voice",
              "leftValue": "={{ $json.voiceNoteUrl ?? '' }}",
              "rightValue": "",
              "operator": {
                "type": "string",
                "operation": "notEquals"
              }
            }
          ]
        },
        "options": {}
      }
    },
    {
      "id": "wb000007-0007-0007-0007-000000000007",
      "name": "Transcribe Voice Note \u2014 Groq Whisper",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        1160,
        160
      ],
      "onError": "continueErrorOutput",
      "parameters": {
        "method": "POST",
        "url": "https://api.groq.com/openai/v1/audio/transcriptions",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Authorization",
              "value": "={{ 'Bearer ' + $env.GROQ_API_KEY }}"
            }
          ]
        },
        "sendBody": true,
        "contentType": "multipart-form-data",
        "bodyParameters": {
          "parameters": [
            {
              "name": "url",
              "value": "={{ $json.voiceNoteUrl }}"
            },
            {
              "name": "model",
              "value": "whisper-large-v3-turbo"
            },
            {
              "name": "language",
              "value": "en"
            }
          ]
        },
        "options": {
          "timeout": 30000,
          "retry": {
            "enabled": true,
            "maxTries": 2,
            "waitBetweenTries": 2000
          }
        }
      }
    },
    {
      "id": "wb000008-0008-0008-0008-000000000008",
      "name": "Whisper Succeeded?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2,
      "position": [
        1400,
        160
      ],
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "loose"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "cond-whisper",
              "leftValue": "={{ $json.text ?? '' }}",
              "rightValue": "",
              "operator": {
                "type": "string",
                "operation": "notEquals"
              }
            }
          ]
        },
        "options": {}
      }
    },
    {
      "id": "wb000009-0009-0009-0009-000000000009",
      "name": "Create TRANSCRIPTION_FAILED ReviewTask",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        1640,
        260
      ],
      "onError": "continueRegularOutput",
      "parameters": {
        "method": "POST",
        "url": "={{ $env.TWENTY_API_URL + '/graphql' }}",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Authorization",
              "value": "={{ 'Bearer ' + $env.TWENTY_API_KEY }}"
            },
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        },
        "sendBody": true,
        "specifyBody": "json",
        "contentType": "json",
        "jsonBody": "={{ { query: 'mutation { createReviewTask(data: { kind: \"LISTING_APPROVAL\", subjectProperty: { connect: { where: { id: \"' + $('Validate Inputs').first().json.propertyId + '\" } } } }) { id kind status } }' } }}",
        "options": {
          "timeout": 15000,
          "retry": {
            "enabled": true,
            "maxTries": 2,
            "waitBetweenTries": 2000
          }
        }
      }
    },
    {
      "id": "wb000010-0010-0010-0010-000000000010",
      "name": "Set Transcribed Text",
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        1640,
        120
      ],
      "parameters": {
        "mode": "manual",
        "duplicateItem": false,
        "assignments": {
          "assignments": [
            {
              "id": "trans-text",
              "name": "transcribed",
              "value": "={{ $json.text ?? '' }}",
              "type": "string"
            }
          ]
        },
        "options": {}
      }
    },
    {
      "id": "wb000011-0011-0011-0011-000000000011",
      "name": "Merge Notes",
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        1880,
        300
      ],
      "parameters": {
        "mode": "manual",
        "duplicateItem": false,
        "assignments": {
          "assignments": [
            {
              "id": "merge-notes",
              "name": "mergedNotes",
              "value": "={{ (function() { const base = $('Validate Inputs').first().json.rawNotes || ''; const transcript = $('Set Transcribed Text').first()?.json?.transcribed ?? ''; return transcript ? base + '\\n\\nVOICE NOTE: ' + transcript : base; })() }}",
              "type": "string"
            },
            {
              "id": "merge-pid",
              "name": "propertyId",
              "value": "={{ $('Validate Inputs').first().json.propertyId }}",
              "type": "string"
            },
            {
              "id": "merge-beds",
              "name": "bedrooms",
              "value": "={{ $('Validate Inputs').first().json.bedrooms }}",
              "type": "number"
            },
            {
              "id": "merge-baths",
              "name": "bathrooms",
              "value": "={{ $('Validate Inputs').first().json.bathrooms }}",
              "type": "number"
            },
            {
              "id": "merge-loc",
              "name": "location",
              "value": "={{ $('Validate Inputs').first().json.location }}",
              "type": "string"
            },
            {
              "id": "merge-area",
              "name": "area",
              "value": "={{ $('Validate Inputs').first().json.area }}",
              "type": "string"
            },
            {
              "id": "merge-price",
              "name": "priceGhs",
              "value": "={{ $('Validate Inputs').first().json.priceGhs }}",
              "type": "number"
            },
            {
              "id": "merge-ptype",
              "name": "propertyType",
              "value": "={{ $('Validate Inputs').first().json.propertyType }}",
              "type": "string"
            },
            {
              "id": "merge-ltype",
              "name": "listingType",
              "value": "={{ $('Validate Inputs').first().json.listingType }}",
              "type": "string"
            }
          ]
        },
        "options": {}
      }
    },
    {
      "id": "wb000012-0012-0012-0012-000000000012",
      "name": "Build OpenAI Messages",
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        2120,
        300
      ],
      "parameters": {
        "mode": "manual",
        "duplicateItem": false,
        "assignments": {
          "assignments": [
            {
              "id": "msg-system",
              "name": "system",
              "value": "You are a Ghanaian property-portal copywriter. Your job is to produce five marketing outputs for a residential or commercial property listing in Ghana, using the structured inputs and raw agent notes provided.\n\nSTYLE GUIDE:\n- Write in plain, professional Ghanaian English. No US-style hyped language.\n- Never write 'DM us', 'WhatsApp us', 'Call now', 'Message us', or similar direct-contact calls to action. The property portal handles inbound enquiries.\n- Prices must be written in GHS with thousand separators, e.g. 'GHS 4,500/month' or 'GHS 850,000'.\n- Use neighbourhood context from this reference:\n  EAST_LEGON: upscale residential, gated estates, embassies nearby\n  AIRPORT_RES: Airport Residential Area, quiet, established, close to Kotoka\n  CANTONMENTS: diplomatic quarter, tree-lined streets, high-security\n  OSU: lively, central, restaurants and nightlife on Oxford Street\n  LABONE: mid-to-high end, near beach, popular with expats\n  SPINTEX: fast-growing, highway access, many new developments\n  TEMA: industrial port city, community estates, affordable\n  ADENTA: suburb on Adenta-Dodowa Road, growing middle-class\n  MADINA: north-east Accra, busy market town, affordable\n  ACHIMOTA: near Achimota Forest Reserve, educational institutions nearby\n  DZORWULU: quiet, leafy, near Airport Hills\n  AIRPORT_HILLS: elevated, panoramic views, premium\n  RIDGE: government district, historic, mature trees\n  ROMAN_RIDGE: upscale, near Ridge, embassies\n  KASOA: Western Region border, high growth, affordable\n  OTHER: describe based on provided location details\n\nOUTPUT REQUIREMENTS \u2014 return ONLY a JSON object with these five keys:\n\n1. listing_description (string, 200-300 words):\n   Full property-portal description. Include: property type, bedrooms/bathrooms, price with frequency (per month or outright), neighbourhood feel, key amenities, standout features. End with a benefit statement.\n\n2. whatsapp_broadcast (string, max 500 characters, NO emojis):\n   Plain-text broadcast message. Short. No direct-contact CTAs. Mention property type, location, price, 2-3 key features. End with 'Reply YES for details'.\n\n3. facebook_caption (string, 300-500 characters, light emojis OK):\n   Engaging Facebook post. Highlight 2-3 features. Neighbourhood feel. Price. End with 'Tap the link in bio to enquire.'.\n\n4. instagram_caption (string, max 2200 characters, hashtags required):\n   Visually descriptive IG caption. Lifestyle angle. Neighbourhood. Price. End with at least 10 relevant hashtags including #GhanaRealEstate #AccraHomes and area-specific tags.\n\n5. reel_script (string, no length limit):\n   30-second Instagram Reel voiceover with shot list. Format:\n   [SHOT 1: ...] Voiceover: \"...\"\n   [SHOT 2: ...] Voiceover: \"...\"\n   etc. Include 4-6 shots. End with a property-portal tagline.\n\nOutput ONLY the JSON object. No markdown. No preamble.",
              "type": "string"
            },
            {
              "id": "msg-messages",
              "name": "messages",
              "value": "={{ (function() { const d = $json; return [{ role: 'user', content: JSON.stringify({ propertyId: d.propertyId, propertyType: d.propertyType, listingType: d.listingType, bedrooms: d.bedrooms, bathrooms: d.bathrooms, location: d.location, area: d.area, priceGhs: d.priceGhs, agentNotes: d.mergedNotes }) }]; })() }}",
              "type": "array"
            }
          ]
        },
        "options": {}
      }
    },
    {
      "id": "wb000013-0013-0013-0013-000000000013",
      "name": "Compose Listing \u2014 gpt-4o",
      "type": "n8n-nodes-base.executeWorkflow",
      "typeVersion": 1.3,
      "position": [
        2360,
        300
      ],
      "parameters": {
        "workflowId": {
          "value": "SUBFLOW_OPENAI_CALL_ID",
          "mode": "id"
        },
        "workflowInputs": {
          "mappingMode": "defineBelow",
          "value": {
            "system": "={{ $json.system }}",
            "messages": "={{ $json.messages }}",
            "model": "gpt-4o",
            "max_tokens": 2500,
            "temperature": 0.4,
            "workflow_label": "workflow-b-listing-engine"
          },
          "schema": [
            {
              "id": "system",
              "displayName": "system",
              "type": "string",
              "required": false
            },
            {
              "id": "messages",
              "displayName": "messages",
              "type": "array",
              "required": true
            },
            {
              "id": "model",
              "displayName": "model",
              "type": "string",
              "required": true
            },
            {
              "id": "max_tokens",
              "displayName": "max_tokens",
              "type": "number",
              "required": false
            },
            {
              "id": "temperature",
              "displayName": "temperature",
              "type": "number",
              "required": false
            },
            {
              "id": "workflow_label",
              "displayName": "workflow_label",
              "type": "string",
              "required": false
            }
          ]
        }
      }
    },
    {
      "id": "wb000014-0014-0014-0014-000000000014",
      "name": "Parse Listing Output",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        2600,
        300
      ],
      "parameters": {
        "language": "javaScript",
        "jsCode": "const raw = $input.first().json.text ?? '';\nlet parsed;\ntry {\n  // Strip markdown code fences if present\n  const cleaned = raw.replace(/^```(?:json)?\\s*/i, '').replace(/\\s*```$/,'').trim();\n  parsed = JSON.parse(cleaned);\n} catch (e) {\n  return [{ json: { _parseFailed: true, parseError: e.message, rawText: raw } }];\n}\nreturn [{ json: { _parseFailed: false, ...parsed } }];"
      }
    },
    {
      "id": "wb000015-0015-0015-0015-000000000015",
      "name": "Parse Succeeded?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2,
      "position": [
        2840,
        300
      ],
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "loose"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "cond-parse",
              "leftValue": "={{ $json._parseFailed }}",
              "rightValue": false,
              "operator": {
                "type": "boolean",
                "operation": "equals"
              }
            }
          ]
        },
        "options": {}
      }
    },
    {
      "id": "wb000016-0016-0016-0016-000000000016",
      "name": "Log Parse Failure",
      "type": "n8n-nodes-base.postgres",
      "typeVersion": 2.6,
      "position": [
        3080,
        460
      ],
      "alwaysOutputData": true,
      "credentials": {
        "postgres": {
          "name": "<your credential>"
        }
      },
      "parameters": {
        "operation": "executeQuery",
        "query": "INSERT INTO workflow_errors (workflow_name, execution_id, node_name, error_message, error_stack, input_payload) VALUES ($1, $2, $3, $4, $5, $6::jsonb)",
        "options": {
          "queryReplacement": "={{ ['Workflow B \u2014 AI Listing Engine', $execution.id, 'Parse Listing Output', ($json.parseError ?? 'JSON parse failed'), ($json.parseError ?? ''), '{\"node\":\"Parse Listing Output\"}'] }}"
        }
      }
    },
    {
      "id": "wb000017-0017-0017-0017-000000000017",
      "name": "Create PARSE_FAILED ReviewTask",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        3320,
        460
      ],
      "onError": "continueRegularOutput",
      "parameters": {
        "method": "POST",
        "url": "={{ $env.TWENTY_API_URL + '/graphql' }}",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Authorization",
              "value": "={{ 'Bearer ' + $env.TWENTY_API_KEY }}"
            },
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        },
        "sendBody": true,
        "specifyBody": "json",
        "contentType": "json",
        "jsonBody": "={{ { query: 'mutation { createReviewTask(data: { kind: \"LISTING_APPROVAL\", subjectProperty: { connect: { where: { id: \"' + $('Merge Notes').first().json.propertyId + '\" } } } }) { id kind status } }' } }}",
        "options": {
          "timeout": 15000,
          "retry": {
            "enabled": true,
            "maxTries": 2,
            "waitBetweenTries": 2000
          }
        }
      }
    },
    {
      "id": "wb000018-0018-0018-0018-000000000018",
      "name": "Return Parse Failed Response",
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        3560,
        460
      ],
      "parameters": {
        "mode": "manual",
        "duplicateItem": false,
        "assignments": {
          "assignments": [
            {
              "id": "pf-ok",
              "name": "ok",
              "value": false,
              "type": "boolean"
            },
            {
              "id": "pf-err",
              "name": "error",
              "value": "LISTING_PARSE_FAILED",
              "type": "string"
            }
          ]
        },
        "options": {}
      }
    },
    {
      "id": "wb000019-0019-0019-0019-000000000019",
      "name": "Validate Outputs",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        3080,
        260
      ],
      "parameters": {
        "language": "javaScript",
        "jsCode": "const d = $input.first().json;\nconst validationErrors = [];\n\nconst BANNED = [/\\bDM\\s+us\\b/i, /\\bWhatsApp\\s+us\\b/i, /\\bCall\\s+now\\b/i, /\\bMessage\\s+us\\b/i];\n\nfunction checkBanned(field, text) {\n  for (const re of BANNED) {\n    if (re.test(text)) validationErrors.push(field + ': contains banned phrase matching ' + re.toString());\n  }\n}\n\n// listing_description\nif (!d.listing_description || !d.listing_description.trim()) {\n  validationErrors.push('listing_description: missing or empty');\n} else {\n  const len = d.listing_description.length;\n  if (len < 200 || len > 320) validationErrors.push('listing_description: length ' + len + ' outside 200-320');\n  checkBanned('listing_description', d.listing_description);\n  // Bedroom count check\n  const beds = $('Merge Notes').first()?.json?.bedrooms;\n  if (beds !== undefined) {\n    const digitPattern = /(\\d+)(?:\\s*-\\s*bed|\\s+bedroom)/i;\n    const match = d.listing_description.match(digitPattern);\n    if (match && parseInt(match[1], 10) !== beds) {\n      validationErrors.push('listing_description: mentions ' + match[1] + ' bed but inputs say ' + beds);\n    }\n  }\n}\n\n// whatsapp_broadcast\nif (!d.whatsapp_broadcast || !d.whatsapp_broadcast.trim()) {\n  validationErrors.push('whatsapp_broadcast: missing or empty');\n} else {\n  if (d.whatsapp_broadcast.length > 500) validationErrors.push('whatsapp_broadcast: length ' + d.whatsapp_broadcast.length + ' exceeds 500');\n  checkBanned('whatsapp_broadcast', d.whatsapp_broadcast);\n}\n\n// facebook_caption\nif (!d.facebook_caption || !d.facebook_caption.trim()) {\n  validationErrors.push('facebook_caption: missing or empty');\n} else {\n  const len = d.facebook_caption.length;\n  if (len < 280 || len > 520) validationErrors.push('facebook_caption: length ' + len + ' outside 280-520');\n  checkBanned('facebook_caption', d.facebook_caption);\n}\n\n// instagram_caption\nif (!d.instagram_caption || !d.instagram_caption.trim()) {\n  validationErrors.push('instagram_caption: missing or empty');\n} else {\n  if (d.instagram_caption.length > 2200) validationErrors.push('instagram_caption: length ' + d.instagram_caption.length + ' exceeds 2200');\n  checkBanned('instagram_caption', d.instagram_caption);\n}\n\n// reel_script\nif (!d.reel_script || !d.reel_script.trim()) {\n  validationErrors.push('reel_script: missing or empty');\n} else {\n  checkBanned('reel_script', d.reel_script);\n}\n\nreturn [{ json: { ...d, validationErrors } }];"
      }
    },
    {
      "id": "wb000020-0020-0020-0020-000000000020",
      "name": "Update Property Description",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        3320,
        260
      ],
      "onError": "continueRegularOutput",
      "parameters": {
        "method": "POST",
        "url": "={{ $env.TWENTY_API_URL + '/graphql' }}",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Authorization",
              "value": "={{ 'Bearer ' + $env.TWENTY_API_KEY }}"
            },
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        },
        "sendBody": true,
        "specifyBody": "json",
        "contentType": "json",
        "jsonBody": "={{ (function() { const pid = $('Merge Notes').first().json.propertyId; const desc = $json.listing_description; const safeDesc = JSON.stringify(desc); return { query: 'mutation { updateProperty(id: \"' + pid + '\", data: { description: ' + safeDesc + ' }) { id description } }' }; })() }}",
        "options": {
          "timeout": 15000,
          "retry": {
            "enabled": true,
            "maxTries": 2,
            "waitBetweenTries": 2000
          }
        }
      }
    },
    {
      "id": "wb000021-0021-0021-0021-000000000021",
      "name": "Property Update OK?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2,
      "position": [
        3560,
        260
      ],
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "loose"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "cond-prop-ok",
              "leftValue": "={{ ($json.errors?.length ?? 0) }}",
              "rightValue": 0,
              "operator": {
                "type": "number",
                "operation": "equals"
              }
            }
          ]
        },
        "options": {}
      }
    },
    {
      "id": "wb000022-0022-0022-0022-000000000022",
      "name": "Log Property Update Error",
      "type": "n8n-nodes-base.postgres",
      "typeVersion": 2.6,
      "position": [
        3800,
        380
      ],
      "alwaysOutputData": true,
      "credentials": {
        "postgres": {
          "name": "<your credential>"
        }
      },
      "parameters": {
        "operation": "executeQuery",
        "query": "INSERT INTO workflow_errors (workflow_name, execution_id, node_name, error_message, error_stack, input_payload) VALUES ($1, $2, $3, $4, $5, $6::jsonb)",
        "options": {
          "queryReplacement": "={{ ['Workflow B \u2014 AI Listing Engine', $execution.id, 'Update Property Description', ($json.errors?.[0]?.message ?? 'Twenty GQL error on updateProperty'), '', '{\"node\":\"Update Property Description\"}'] }}"
        }
      }
    },
    {
      "id": "wb000023-0023-0023-0023-000000000023",
      "name": "Send Telegram \u2014 Property Error",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        4040,
        380
      ],
      "onError": "continueRegularOutput",
      "parameters": {
        "method": "POST",
        "url": "={{ 'https://api.telegram.org/bot' + $env.TELEGRAM_BOT_TOKEN + '/sendMessage' }}",
        "sendBody": true,
        "specifyBody": "json",
        "contentType": "json",
        "jsonBody": "={{ { chat_id: $env.TELEGRAM_CHANNEL_ID, text: '[Workflow B] ERROR: updateProperty failed for ' + $('Merge Notes').first().json.propertyId + '. Check workflow_errors.' } }}",
        "options": {
          "timeout": 10000,
          "retry": {
            "enabled": true,
            "maxTries": 2,
            "waitBetweenTries": 1000
          }
        }
      }
    },
    {
      "id": "wb000024-0024-0024-0024-000000000024",
      "name": "Create SocialPost \u2014 Facebook",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        3800,
        200
      ],
      "onError": "continueRegularOutput",
      "parameters": {
        "method": "POST",
        "url": "={{ $env.TWENTY_API_URL + '/graphql' }}",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Authorization",
              "value": "={{ 'Bearer ' + $env.TWENTY_API_KEY }}"
            },
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        },
        "sendBody": true,
        "specifyBody": "json",
        "contentType": "json",
        "jsonBody": "={{ (function() { const pid = $('Merge Notes').first().json.propertyId; const body = $('Validate Outputs').first().json.facebook_caption; return { query: 'mutation { createSocialPost(data: { body: ' + JSON.stringify(body) + ', platform: \"FACEBOOK\", status: \"PENDING_APPROVAL\", property: { connect: { where: { id: \"' + pid + '\" } } } }) { id platform status } }' }; })() }}",
        "options": {
          "timeout": 15000,
          "retry": {
            "enabled": true,
            "maxTries": 2,
            "waitBetweenTries": 2000
          }
        }
      }
    },
    {
      "id": "wb000025-0025-0025-0025-000000000025",
      "name": "FB Post OK?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2,
      "position": [
        4040,
        200
      ],
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "loose"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "cond-fb-ok",
              "leftValue": "={{ ($json.errors?.length ?? 0) }}",
              "rightValue": 0,
              "operator": {
                "type": "number",
                "operation": "equals"
              }
            }
          ]
        },
        "options": {}
      }
    },
    {
      "id": "wb000026-0026-0026-0026-000000000026",
      "name": "Log FB Post Error",
      "type": "n8n-nodes-base.postgres",
      "typeVersion": 2.6,
      "position": [
        4280,
        300
      ],
      "alwaysOutputData": true,
      "credentials": {
        "postgres": {
          "name": "<your credential>"
        }
      },
      "parameters": {
        "operation": "executeQuery",
        "query": "INSERT INTO workflow_errors (workflow_name, execution_id, node_name, error_message, error_stack, input_payload) VALUES ($1, $2, $3, $4, $5, $6::jsonb)",
        "options": {
          "queryReplacement": "={{ ['Workflow B \u2014 AI Listing Engine', $execution.id, 'Create SocialPost \u2014 Facebook', ($json.errors?.[0]?.message ?? 'Twenty GQL error on createSocialPost FB'), '', '{\"node\":\"Create SocialPost FB\"}'] }}"
        }
      }
    },
    {
      "id": "wb000027-0027-0027-0027-000000000027",
      "name": "Capture FB Post ID",
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        4280,
        160
      ],
      "parameters": {
        "mode": "manual",
        "duplicateItem": false,
        "assignments": {
          "assignments": [
            {
              "id": "fb-id",
              "name": "fbPostId",
              "value": "={{ $json.data?.createSocialPost?.id ?? '' }}",
              "type": "string"
            }
          ]
        },
        "options": {}
      }
    },
    {
      "id": "wb000028-0028-0028-0028-000000000028",
      "name": "Create SocialPost \u2014 Instagram",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        4520,
        160
      ],
      "onError": "continueRegularOutput",
      "parameters": {
        "method": "POST",
        "url": "={{ $env.TWENTY_API_URL + '/graphql' }}",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Authorization",
              "value": "={{ 'Bearer ' + $env.TWENTY_API_KEY }}"
            },
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        },
        "sendBody": true,
        "specifyBody": "json",
        "contentType": "json",
        "jsonBody": "={{ (function() { const pid = $('Merge Notes').first().json.propertyId; const body = $('Validate Outputs').first().json.instagram_caption; return { query: 'mutation { createSocialPost(data: { body: ' + JSON.stringify(body) + ', platform: \"INSTAGRAM\", status: \"PENDING_APPROVAL\", property: { connect: { where: { id: \"' + pid + '\" } } } }) { id platform status } }' }; })() }}",
        "options": {
          "timeout": 15000,
          "retry": {
            "enabled": true,
            "maxTries": 2,
            "waitBetweenTries": 2000
          }
        }
      }
    },
    {
      "id": "wb000029-0029-0029-0029-000000000029",
      "name": "IG Post OK?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2,
      "position": [
        4760,
        160
      ],
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "loose"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "cond-ig-ok",
              "leftValue": "={{ ($json.errors?.length ?? 0) }}",
              "rightValue": 0,
              "operator": {
                "type": "number",
                "operation": "equals"
              }
            }
          ]
        },
        "options": {}
      }
    },
    {
      "id": "wb000030-0030-0030-0030-000000000030",
      "name": "Log IG Post Error",
      "type": "n8n-nodes-base.postgres",
      "typeVersion": 2.6,
      "position": [
        5000,
        260
      ],
      "alwaysOutputData": true,
      "credentials": {
        "postgres": {
          "name": "<your credential>"
        }
      },
      "parameters": {
        "operation": "executeQuery",
        "query": "INSERT INTO workflow_errors (workflow_name, execution_id, node_name, error_message, error_stack, input_payload) VALUES ($1, $2, $3, $4, $5, $6::jsonb)",
        "options": {
          "queryReplacement": "={{ ['Workflow B \u2014 AI Listing Engine', $execution.id, 'Create SocialPost \u2014 Instagram', ($json.errors?.[0]?.message ?? 'Twenty GQL error on createSocialPost IG'), '', '{\"node\":\"Create SocialPost IG\"}'] }}"
        }
      }
    },
    {
      "id": "wb000031-0031-0031-0031-000000000031",
      "name": "Capture IG Post ID",
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        5000,
        120
      ],
      "parameters": {
        "mode": "manual",
        "duplicateItem": false,
        "assignments": {
          "assignments": [
            {
              "id": "ig-id",
              "name": "igPostId",
              "value": "={{ $json.data?.createSocialPost?.id ?? '' }}",
              "type": "string"
            }
          ]
        },
        "options": {}
      }
    },
    {
      "id": "wb000032-0032-0032-0032-000000000032",
      "name": "Create SocialPost \u2014 IG Reel (as Instagram)",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        5240,
        120
      ],
      "onError": "continueRegularOutput",
      "parameters": {
        "method": "POST",
        "url": "={{ $env.TWENTY_API_URL + '/graphql' }}",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Authorization",
              "value": "={{ 'Bearer ' + $env.TWENTY_API_KEY }}"
            },
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        },
        "sendBody": true,
        "specifyBody": "json",
        "contentType": "json",
        "jsonBody": "={{ (function() { const pid = $('Merge Notes').first().json.propertyId; const body = '[REEL SCRIPT]\\n' + $('Validate Outputs').first().json.reel_script; return { query: 'mutation { createSocialPost(data: { body: ' + JSON.stringify(body) + ', platform: \"INSTAGRAM\", status: \"PENDING_APPROVAL\", property: { connect: { where: { id: \"' + pid + '\" } } } }) { id platform status } }' }; })() }}",
        "options": {
          "timeout": 15000,
          "retry": {
            "enabled": true,
            "maxTries": 2,
            "waitBetweenTries": 2000
          }
        }
      }
    },
    {
      "id": "wb000033-0033-0033-0033-000000000033",
      "name": "Reel Post OK?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2,
      "position": [
        5480,
        120
      ],
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "loose"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "cond-reel-ok",
              "leftValue": "={{ ($json.errors?.length ?? 0) }}",
              "rightValue": 0,
              "operator": {
                "type": "number",
                "operation": "equals"
              }
            }
          ]
        },
        "options": {}
      }
    },
    {
      "id": "wb000034-0034-0034-0034-000000000034",
      "name": "Log Reel Post Error",
      "type": "n8n-nodes-base.postgres",
      "typeVersion": 2.6,
      "position": [
        5720,
        220
      ],
      "alwaysOutputData": true,
      "credentials": {
        "postgres": {
          "name": "<your credential>"
        }
      },
      "parameters": {
        "operation": "executeQuery",
        "query": "INSERT INTO workflow_errors (workflow_name, execution_id, node_name, error_message, error_stack, input_payload) VALUES ($1, $2, $3, $4, $5, $6::jsonb)",
        "options": {
          "queryReplacement": "={{ ['Workflow B \u2014 AI Listing Engine', $execution.id, 'Create SocialPost \u2014 IG Reel', ($json.errors?.[0]?.message ?? 'Twenty GQL error on createSocialPost Reel'), '', '{\"node\":\"Create SocialPost Reel\"}'] }}"
        }
      }
    },
    {
      "id": "wb000035-0035-0035-0035-000000000035",
      "name": "Capture Reel Post ID",
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        5720,
        80
      ],
      "parameters": {
        "mode": "manual",
        "duplicateItem": false,
        "assignments": {
          "assignments": [
            {
              "id": "reel-id",
              "name": "reelPostId",
              "value": "={{ $json.data?.createSocialPost?.id ?? '' }}",
              "type": "string"
            }
          ]
        },
        "options": {}
      }
    },
    {
      "id": "wb000036-0036-0036-0036-000000000036",
      "name": "Create SocialPost \u2014 WhatsApp Broadcast (as Facebook)",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        5960,
        80
      ],
      "onError": "continueRegularOutput",
      "parameters": {
        "method": "POST",
        "url": "={{ $env.TWENTY_API_URL + '/graphql' }}",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Authorization",
              "value": "={{ 'Bearer ' + $env.TWENTY_API_KEY }}"
            },
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        },
        "sendBody": true,
        "specifyBody": "json",
        "contentType": "json",
        "jsonBody": "={{ (function() { const pid = $('Merge Notes').first().json.propertyId; const body = '[WHATSAPP BROADCAST]\\n' + $('Validate Outputs').first().json.whatsapp_broadcast; return { query: 'mutation { createSocialPost(data: { body: ' + JSON.stringify(body) + ', platform: \"FACEBOOK\", status: \"PENDING_APPROVAL\", property: { connect: { where: { id: \"' + pid + '\" } } } }) { id platform status } }' }; })() }}",
        "options": {
          "timeout": 15000,
          "retry": {
            "enabled": true,
            "maxTries": 2,
            "waitBetweenTries": 2000
          }
        }
      }
    },
    {
      "id": "wb000037-0037-0037-0037-000000000037",
      "name": "WA Broadcast Post OK?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2,
      "position": [
        6200,
        80
      ],
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "loose"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "cond-wa-ok",
              "leftValue": "={{ ($json.errors?.length ?? 0) }}",
              "rightValue": 0,
              "operator": {
                "type": "number",
                "operation": "equals"
              }
            }
          ]
        },
        "options": {}
      }
    },
    {
      "id": "wb000038-0038-0038-0038-000000000038",
      "name": "Log WA Broadcast Error",
      "type": "n8n-nodes-base.postgres",
      "typeVersion": 2.6,
      "position": [
        6440,
        180
      ],
      "alwaysOutputData": true,
      "credentials": {
        "postgres": {
          "name": "<your credential>"
        }
      },
      "parameters": {
        "operation": "executeQuery",
        "query": "INSERT INTO workflow_errors (workflow_name, execution_id, node_name, error_message, error_stack, input_payload) VALUES ($1, $2, $3, $4, $5, $6::jsonb)",
        "options": {
          "queryReplacement": "={{ ['Workflow B \u2014 AI Listing Engine', $execution.id, 'Create SocialPost \u2014 WhatsApp Broadcast', ($json.errors?.[0]?.message ?? 'Twenty GQL error on createSocialPost WA'), '', '{\"node\":\"Create SocialPost WA\"}'] }}"
        }
      }
    },
    {
      "id": "wb000039-0039-0039-0039-000000000039",
      "name": "Capture WA Post ID",
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        6440,
        40
      ],
      "parameters": {
        "mode": "manual",
        "duplicateItem": false,
        "assignments": {
          "assignments": [
            {
              "id": "wa-id",
              "name": "waBroadcastPostId",
              "value": "={{ $json.data?.createSocialPost?.id ?? '' }}",
              "type": "string"
            }
          ]
        },
        "options": {}
      }
    },
    {
      "id": "wb000040-0040-0040-0040-000000000040",
      "name": "Create LISTING_REVIEW ReviewTask",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        6680,
        40
      ],
      "onError": "continueRegularOutput",
      "parameters": {
        "method": "POST",
        "url": "={{ $env.TWENTY_API_URL + '/graphql' }}",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Authorization",
              "value": "={{ 'Bearer ' + $env.TWENTY_API_KEY }}"
            },
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        },
        "sendBody": true,
        "specifyBody": "json",
        "contentType": "json",
        "jsonBody": "={{ (function() { const pid = $('Merge Notes').first().json.propertyId; return { query: 'mutation { createReviewTask(data: { kind: \"LISTING_APPROVAL\", subjectProperty: { connect: { where: { id: \"' + pid + '\" } } } }) { id kind status } }' }; })() }}",
        "options": {
          "timeout": 15000,
          "retry": {
            "enabled": true,
            "maxTries": 2,
            "waitBetweenTries": 2000
          }
        }
      }
    },
    {
      "id": "wb000041-0041-0041-0041-000000000041",
      "name": "ReviewTask Created OK?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2,
      "position": [
        6920,
        40
      ],
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "loose"
          },
          "combinator": "and",
          "conditions": [
            {
              "id": "cond-rt-ok",
              "leftValue": "={{ ($json.errors?.length ?? 0) }}",
              "rightValue": 0,
              "operator": {
                "type": "number",
                "operation": "equals"
              }
            }
          ]
        },
        "options": {}
      }
    },
    {
      "id": "wb000042-0042-0042-0042-000000000042",
      "name": "Log ReviewTask Error",
      "type": "n8n-nodes-base.postgres",
      "typeVersion": 2.6,
      "position": [
        7160,
        140
      ],
      "alwaysOutputData": true,
      "credentials": {
        "postgres": {
          "name": "<your credential>"
        }
      },
      "parameters": {
        "operation": "executeQuery",
        "query": "INSERT INTO workflow_errors (workflow_name, execution_id, node_name, error_message, error_stack, input_payload) VALUES ($1, $2, $3, $4, $5, $6::jsonb)",
        "options": {
          "queryReplacement": "={{ ['Workflow B \u2014 AI Listing Engine', $execution.id, 'Create LISTING_REVIEW ReviewTask', ($json.errors?.[0]?.message ?? 'Twenty GQL error on createReviewTask'), '', '{\"node\":\"Create ReviewTask\"}'] }}"
        }
      }
    },
    {
      "id": "wb000043-0043-0043-0043-000000000043",
      "name": "Capture ReviewTask ID",
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        7160,
        0
      ],
      "parameters": {
        "mode": "manual",
        "duplicateItem": false,
        "assignments": {
          "assignments": [
            {
              "id": "rt-id",
              "name": "reviewTaskId",
              "value": "={{ $json.data?.createReviewTask?.id ?? '' }}",
              "type": "string"
            }
          ]
        },
        "options": {}
      }
    },
    {
      "id": "wb000044-0044-0044-0044-000000000044",
      "name": "Notify Agent \u2014 Telegram",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        7400,
        0
      ],
      "onError": "continueRegularOutput",
      "parameters": {
        "method": "POST",
        "url": "={{ 'https://api.telegram.org/bot' + $env.TELEGRAM_BOT_TOKEN + '/sendMessage' }}",
        "sendBody": true,
        "specifyBody": "json",
        "contentType": "json",
        "jsonBody": "={{ (function() { const pid = $('Merge Notes').first().json.propertyId; const errs = $('Validate Outputs').first().json.validationErrors ?? []; const errNote = errs.length ? ' (' + errs.length + ' validation warning(s))' : ''; return { chat_id: $env.TELEGRAM_CHANNEL_ID, text: 'New listing draft ready for review: Property ' + pid + errNote + '. Open Twenty and review the LISTING_APPROVAL task.' }; })() }}",
        "options": {
          "timeout": 10000,
          "retry": {
            "enabled": true,
            "maxTries": 2,
            "waitBetweenTries": 1000
          }
        }
      }
    },
    {
      "id": "wb000045-0045-0045-0045-000000000045",
      "name": "Webhook Success Response",
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        7640,
        0
      ],
      "parameters": {
        "mode": "manual",
        "duplicateItem": false,
        "assignments": {
          "assignments": [
            {
              "id": "resp-ok",
              "name": "ok",
              "value": true,
              "type": "boolean"
            },
            {
              "id": "resp-pid",
              "name": "propertyId",
              "value": "={{ $('Merge Notes').first().json.propertyId }}",
              "type": "string"
            },
            {
              "id": "resp-fb",
              "name": "fbPostId",
              "value": "={{ $('Capture FB Post ID').first()?.json?.fbPostId ?? '' }}",
              "type": "string"
            },
            {
              "id": "resp-ig",
              "name": "igPostId",
              "value": "={{ $('Capture IG Post ID').first()?.json?.igPostId ?? '' }}",
              "type": "string"
            },
            {
              "id": "resp-reel",
              "name": "reelPostId",
              "value": "={{ $('Capture Reel Post ID').first()?.json?.reelPostId ?? '' }}",
              "type": "string"
            },
            {
              "id": "resp-wa",
              "name": "waBroadcastPostId",
              "value": "={{ $('Capture WA Post ID').first()?.json?.waBroadcastPostId ?? '' }}",
              "type": "string"
            },
            {
              "id": "resp-rt",
              "name": "reviewTaskId",
              "value": "={{ $('Capture ReviewTask ID').first()?.json?.reviewTaskId ?? '' }}",
              "type": "string"
            },
            {
              "id": "resp-verr",
              "name": "validationErrors",
              "value": "={{ $('Validate Outputs').first().json.validationErrors ?? [] }}",
              "type": "array"
            }
          ]
        },
        "options": {}
      }
    },
    {
      "id": "wb000046-0046-0046-0046-000000000046",
      "name": "Error Trigger",
      "type": "n8n-nodes-base.errorTrigger",
      "typeVersion": 1,
      "position": [
        200,
        600
      ],
      "parameters": {}
    },
    {
      "id": "wb000047-0047-0047-0047-000000000047",
      "name": "Log B Error",
      "type": "n8n-nodes-base.postgres",
      "typeVersion": 2.6,
      "position": [
        440,
        600
      ],
      "alwaysOutputData": true,
      "credentials": {
        "postgres": {
          "name": "<your credential>"
        }
      },
      "parameters": {
        "operation": "executeQuery",
        "query": "INSERT INTO workflow_errors (workflow_name, execution_id, node_name, error_message, error_stack, input_payload) VALUES ($1, $2, $3, $4, $5, $6::jsonb)",
        "options": {
          "queryReplacement": "={{ ['Workflow B \u2014 AI Listing Engine', $json.execution.id, $json.execution.lastNodeExecuted ?? 'unknown', $json.execution.error.message ?? 'unknown error', ($json.execution.error.stack ? $json.execution.error.stack.split('\\n')[0] : ''), '{\"source\":\"error_trigger\"}'] }}"
        }
      }
    }
  ],
  "connections": {
    "Listing Input Webhook": {
      "main": [
        [
          {
            "node": "Validate Inputs",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Validate Inputs": {
      "main": [
        [
          {
            "node": "Inputs Valid?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Inputs Valid?": {
      "main": [
        [
          {
            "node": "Voice Note Present?",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Create INPUTS_INVALID ReviewTask",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Create INPUTS_INVALID ReviewTask": {
      "main": [
        [
          {
            "node": "Return Invalid Inputs Response",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Voice Note Present?": {
      "main": [
        [
          {
            "node": "Transcribe Voice Note \u2014 Groq Whisper",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Merge Notes",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Transcribe Voice Note \u2014 Groq Whisper": {
      "main": [
        [
          {
            "node": "Whisper Succeeded?",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Create TRANSCRIPTION_FAILED ReviewTask",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Whisper Succeeded?": {
      "main": [
        [
          {
            "node": "Set Transcribed Text",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Create TRANSCRIPTION_FAILED ReviewTask",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Set Transcribed Text": {
      "main": [
        [
          {
            "node": "Merge Notes",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Create TRANSCRIPTION_FAILED ReviewTask": {
      "main": [
        [
          {
            "node": "Merge Notes",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Merge Notes": {
      "main": [
        [
          {
            "node": "Build OpenAI Messages",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Build OpenAI Messages": {
      "main": [
        [
          {
            "node": "Compose Listing \u2014 gpt-4o",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Compose Listing \u2014 gpt-4o": {
      "main": [
        [
          {
            "node": "Parse Listing Output",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Parse Listing Output": {
      "main": [
        [
          {
            "node": "Parse Succeeded?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Parse Succeeded?": {
      "main": [
        [
          {
            "node": "Validate Outputs",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Log Parse Failure",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Log Parse Failure": {
      "main": [
        [
          {
            "node": "Create PARSE_FAILED ReviewTask",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Create PARSE_FAILED ReviewTask": {
      "main": [
        [
          {
            "node": "Return Parse Failed Response",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Validate Outputs": {
      "main": [
        [
          {
            "node": "Update Property Description",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Update Property Description": {
      "main": [
        [
          {
            "node": "Property Update OK?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Property Update OK?": {
      "main": [
        [
          {
            "node": "Create SocialPost \u2014 Facebook",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Log Property Update Error",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Log Property Update Error": {
      "main": [
        [
          {
            "node": "Send Telegram \u2014 Property Error",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Send Telegram \u2014 Property Error": {
      "main": [
        [
          {
            "node": "Create SocialPost \u2014 Facebook",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Create SocialPost \u2014 Facebook": {
      "main": [
        [
          {
            "node": "FB Post OK?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "FB Post OK?": {
      "main": [
        [
          {
            "node": "Capture FB Post ID",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Log FB Post Error",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Log FB Post Error": {
      "main": [
        [
          {
            "node": "Capture FB Post ID",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Capture FB Post ID": {
      "main": [
        [
          {
            "node": "Create SocialPost \u2014 Instagram",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Create SocialPost \u2014 Instagram": {
      "main": [
        [
          {
            "node": "IG Post OK?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "IG Post OK?": {
      "main": [
        [
          {
            "node": "Capture IG Post ID",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Log IG Post Error",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Log IG Post Error": {
      "main": [
        [
          {
            "node": "Capture IG Post ID",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Capture IG Post ID": {
      "main": [
        [
          {
            "node": "Create SocialPost \u2014 IG Reel (as Instagram)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Create SocialPost \u2014 IG Reel (as Instagram)": {
      "main": [
        [
          {
            "node": "Reel Post OK?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Reel Post OK?": {
      "main": [
        [
          {
            "node": "Capture Reel Post ID",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Log Reel Post Error",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Log Reel Post Error": {
      "main": [
        [
          {
            "node": "Capture Reel Post ID",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Capture Reel Post ID": {
      "main": [
        [
          {
            "node": "Create SocialPost \u2014 WhatsApp Broadcast (as Facebook)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Create SocialPost \u2014 WhatsApp Broadcast (as Facebook)": {
      "main": [
        [
          {
            "node": "WA Broadcast Post OK?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "WA Broadcast Post OK?": {
      "main": [
        [
          {
            "node": "Capture WA Post ID",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Log WA Broadcast Error",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Log WA Broadcast Error": {
      "main": [
        [
          {
            "node": "Capture WA Post ID",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Capture WA Post ID": {
      "main": [
        [
          {
            "node": "Create LISTING_REVIEW ReviewTask",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Create LISTING_REVIEW ReviewTask": {
      "main": [
        [
          {
            "node": "ReviewTask Created OK?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "ReviewTask Created OK?": {
      "main": [
        [
          {
            "node": "Capture ReviewTask ID",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Log ReviewTask Error",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Log ReviewTask Error": {
      "main": [
        [
          {
            "node": "Capture ReviewTask ID",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Capture ReviewTask ID": {
      "main": [
        [
          {
            "node": "Notify Agent \u2014 Telegram",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Notify Agent \u2014 Telegram": {
      "main": [
        [
          {
            "node": "Webhook Success Response",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Error Trigger": {
      "main": [
        [
          {
            "node": "Log B Error",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "active": false,
  "settings": {
    "executionOrder": "v1"
  },
  "tags": [
    {
      "name": "real-estate-automation"
    },
    {
      "name": "workflow-b"
    },
    {
      "name": "version-1.0"
    }
  ]
}