{
  "name": "Pinterest Board Automation for wordpress",
  "nodes": [
    {
      "parameters": {},
      "id": "aa9fe8c2-ab38-461a-89ee-eac7409b8b56",
      "name": "Manual Trigger",
      "type": "n8n-nodes-base.manualTrigger",
      "typeVersion": 1,
      "position": [
        -4096,
        -144
      ],
      "notes": "Manual trigger for processing one post at a time"
    },
    {
      "parameters": {
        "operation": "executeQuery",
        "query": "SELECT COALESCE(array_agg(wordpress_post_id), ARRAY[]::int[]) as processed_ids FROM pinterest_processed_posts WHERE status IN ('completed', 'processing', 'failed')",
        "options": {}
      },
      "id": "4e5b57e7-dde1-433f-a893-cb758d24eb8c",
      "name": "Get Processed Posts",
      "type": "n8n-nodes-base.postgres",
      "typeVersion": 2.4,
      "position": [
        -3872,
        -144
      ],
      "credentials": {
        "postgres": {
          "name": "<your credential>"
        }
      },
      "notes": "Retrieve list of already processed WordPress post IDs"
    },
    {
      "parameters": {
        "url": "https://your-wp-website.com/wp-json/wp/v2/posts",
        "sendQuery": true,
        "queryParameters": {
          "parameters": [
            {
              "name": "per_page",
              "value": "10"
            },
            {
              "name": "orderby",
              "value": "date"
            },
            {
              "name": "order",
              "value": "desc"
            },
            {
              "name": "_embed",
              "value": "true"
            },
            {
              "name": "status",
              "value": "publish"
            }
          ]
        },
        "options": {
          "timeout": 30000
        }
      },
      "id": "5f4b4cdc-f6d7-464f-85a0-5998a0de96e9",
      "name": "Fetch WordPress Posts",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.1,
      "position": [
        -3648,
        -144
      ],
      "notes": "Fetch 10 latest published posts from WordPress"
    },
    {
      "parameters": {
        "jsCode": "// Get processed IDs from database\nconst processedData = $('Get Processed Posts').all()[0]?.json || {};\nconst processedIds = processedData.processed_ids || [];\n\n// Get all WordPress posts fetched\nconst wpPosts = $input.all();\n\nif (!wpPosts || wpPosts.length === 0) {\n  throw new Error('No WordPress posts found');\n}\n\n// Log for debugging\nconsole.log(`Total posts fetched: ${wpPosts.length}`);\nconsole.log(`Processed IDs from DB: ${JSON.stringify(processedIds)}`);\n\n// Find the first unprocessed post\nlet unprocessedPost = null;\nlet processedCount = 0;\n\nfor (const post of wpPosts) {\n  const wpPost = post.json;\n  const postId = parseInt(wpPost.id);\n  const isProcessed = processedIds.some(id => parseInt(id) === postId);\n  \n  if (isProcessed) {\n    processedCount++;\n    console.log(`Post ${postId} - '${wpPost.title?.rendered}' is already processed`);\n  } else {\n    console.log(`Post ${postId} - '${wpPost.title?.rendered}' is NEW, will process this one`);\n    unprocessedPost = wpPost;\n    break; // Found first unprocessed post\n  }\n}\n\n// If no unprocessed posts found\nif (!unprocessedPost) {\n  console.log(`All ${processedCount} posts have been processed`);\n  return [{\n    json: {\n      alreadyProcessed: true,\n      message: `All ${processedCount} fetched posts have already been processed. No new posts to process.`,\n      processedCount: processedCount,\n      totalFetched: wpPosts.length\n    }\n  }];\n}\n\n// Return the first unprocessed post for processing\nconsole.log(`Processing post ID ${unprocessedPost.id}: ${unprocessedPost.title?.rendered}`);\nreturn [{\n  json: {\n    ...unprocessedPost,\n    alreadyProcessed: false,\n    processedCount: processedCount,\n    totalFetched: wpPosts.length\n  }\n}];"
      },
      "id": "d22db0e9-3edf-4987-a220-740eb3bc26b1",
      "name": "Find First Unprocessed Post",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        -3424,
        -144
      ],
      "notes": "Find first unprocessed post from fetched posts"
    },
    {
      "parameters": {
        "conditions": {
          "boolean": [
            {
              "value1": "={{$json.alreadyProcessed}}",
              "value2": "={{false}}"
            }
          ]
        }
      },
      "id": "8769ebab-4fb5-4eed-b7d7-f8219ded05a5",
      "name": "Already Processed?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 1,
      "position": [
        -3200,
        -144
      ],
      "notes": "Check if post was already processed"
    },
    {
      "parameters": {
        "jsCode": "// Extract and validate post data\nconst post = $input.item.json;\nconst postId = post.id;\nconst postTitle = post.title?.rendered || 'Untitled';\nconst postUrl = post.link || `https://your-wp-website.com/?p=${postId}`;\nconst content = post.content?.rendered || '';\nconst excerpt = post.excerpt?.rendered || '';\n\n// Clean title from HTML\nconst cleanTitle = postTitle.replace(/<[^>]*>/g, '').trim();\n\n// Extract featured image if available\nlet featuredImage = null;\nif (post._embedded && post._embedded['wp:featuredmedia']) {\n  const media = post._embedded['wp:featuredmedia'][0];\n  if (media && media.source_url) {\n    featuredImage = media.source_url;\n  }\n}\n\n// Extract all images from post content\nconst imageRegex = /<img[^>]+src=[\\\"']([^\\\"']+)[\\\"'][^>]*>/gi;\nconst images = [];\nlet match;\n\nwhile ((match = imageRegex.exec(content)) !== null) {\n  const imageUrl = match[1];\n  // Validate image URL and filter for actual Website Content \n  if (imageUrl && (imageUrl.includes('.jpg') || imageUrl.includes('.jpeg') || \n      imageUrl.includes('.png') || imageUrl.includes('.webp'))) {\n    // Ensure full URL\n    const fullUrl = imageUrl.startsWith('http') ? imageUrl : \n                   `https://your-wp-website.com${imageUrl.startsWith('/') ? '' : '/'}${imageUrl}`;\n    \n    // Avoid duplicates\n    if (!images.includes(fullUrl)) {\n      images.push(fullUrl);\n    }\n  }\n}\n\n// Add featured image if not already in list\nif (featuredImage && !images.includes(featuredImage)) {\n  images.unshift(featuredImage);\n}\n\n// Limit to 20 best images for quality\nconst limitedImages = images.slice(0, 20);\n\n// Generate board metadata with unique ID to avoid duplicates\nconst boardName = `${cleanTitle} - Website Content  ${postId}`;\nconst boardDescription = `Free Image ${cleanTitle.toLowerCase()} Website Content  for kids. High-quality Content sheets perfect for preschool, kindergarten, and elementary school children. Download and print these fun educational activities from your-wp-website.com!`;\n\n// Extract categories\nconst categories = [];\nif (post._embedded && post._embedded['wp:term']) {\n  const terms = post._embedded['wp:term'].flat();\n  terms.forEach(term => {\n    if (term.taxonomy === 'category' && term.name !== 'Uncategorized') {\n      categories.push(term.name);\n    }\n  });\n}\n\nif (limitedImages.length === 0) {\n  throw new Error(`No images found in post ${postId}`);\n}\n\nreturn {\n  postId,\n  postTitle: cleanTitle,\n  postUrl,\n  excerpt: excerpt.replace(/<[^>]*>/g, '').trim().substring(0, 200),\n  images: limitedImages,\n  imageCount: limitedImages.length,\n  boardName: boardName.substring(0, 50), // Pinterest board name limit\n  boardDescription: boardDescription.substring(0, 500), // Pinterest description limit\n  categories: categories.join(', '),\n  timestamp: new Date().toISOString()\n};"
      },
      "id": "867bff6d-3b5f-432a-8750-746a4ba56258",
      "name": "Extract Post Data & Images",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        -2976,
        -240
      ],
      "notes": "Extract images and metadata from WordPress post"
    },
    {
      "parameters": {
        "operation": "executeQuery",
        "query": "INSERT INTO pinterest_processed_posts (wordpress_post_id, post_title, post_url, total_images, status, created_at) VALUES ({{$json.postId}}, '{{$json.postTitle.replace(/'/g, \"''\")}}', '{{$json.postUrl}}', {{$json.imageCount}}, 'processing', NOW()) ON CONFLICT (wordpress_post_id) DO UPDATE SET status = 'processing', updated_at = NOW() RETURNING id, wordpress_post_id",
        "options": {}
      },
      "id": "31884099-2955-4188-8687-1378c7d1aeed",
      "name": "Create Tracking Record",
      "type": "n8n-nodes-base.postgres",
      "typeVersion": 2.4,
      "position": [
        -2752,
        -240
      ],
      "credentials": {
        "postgres": {
          "name": "<your credential>"
        }
      },
      "notes": "Insert tracking record in database"
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://api.pinterest.com/v5/boards",
        "authentication": "genericCredentialType",
        "genericAuthType": "oAuth2Api",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        },
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={\n  \"name\": \"{{$('Extract Post Data & Images').item.json.boardName}}\",\n  \"description\": \"{{$('Extract Post Data & Images').item.json.boardDescription}}\",\n  \"privacy\": \"PUBLIC\"\n}",
        "options": {
          "timeout": 20000
        }
      },
      "id": "857d2b8c-669c-429a-840c-77193e3a4826",
      "name": "Create Pinterest Board",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.1,
      "position": [
        -2528,
        -240
      ],
      "credentials": {
        "oAuth2Api": {
          "name": "<your credential>"
        }
      },
      "notes": "Create a new Pinterest board for this post"
    },
    {
      "parameters": {
        "jsCode": "// Handle board creation result\ntry {\n  const boardResponse = $input.item.json;\n  const trackingRecord = $('Create Tracking Record').item.json;\n  const postData = $('Extract Post Data & Images').item.json;\n\n  // Log the full response for debugging\n  console.log('Full board response type:', typeof boardResponse);\n  console.log('Board creation response:', JSON.stringify(boardResponse, null, 2));\n  \n  // First check if response is undefined or null\n  if (!boardResponse) {\n    throw new Error('No response received from Pinterest API');\n  }\n\n  // Check if this is a list response (GET boards instead of POST board creation)\n  if (boardResponse.items && Array.isArray(boardResponse.items)) {\n    console.log('Received board list instead of creation response. Checking if board already exists...');\n    \n    // Check if the board already exists in the list\n    const existingBoard = boardResponse.items.find(board => \n      board.name === postData.boardName || \n      board.name.includes(postData.postTitle)\n    );\n    \n    if (existingBoard) {\n      console.log(`Found existing board: ${existingBoard.name} with ID: ${existingBoard.id}`);\n      // Use the existing board\n      return {\n        boardId: existingBoard.id,\n        boardName: existingBoard.name,\n        boardUrl: `https://pinterest.com/board/${existingBoard.id}/`,\n        postId: trackingRecord.wordpress_post_id,\n        recordId: trackingRecord.id,\n        images: postData.images,\n        postData: postData,\n        wasExisting: true\n      };\n    } else {\n      // First board in the list if no exact match\n      if (boardResponse.items.length > 0) {\n        const firstBoard = boardResponse.items[0];\n        console.log(`Using first available board: ${firstBoard.name}`);\n        return {\n          boardId: firstBoard.id,\n          boardName: firstBoard.name,\n          boardUrl: `https://pinterest.com/board/${firstBoard.id}/`,\n          postId: trackingRecord.wordpress_post_id,\n          recordId: trackingRecord.id,\n          images: postData.images,\n          postData: postData,\n          wasExisting: true\n        };\n      }\n      throw new Error(`Board creation failed. API returned empty list of boards.`);\n    }\n  }\n\n  // Check for Pinterest API error codes\n  if (boardResponse.code && boardResponse.code !== 0) {\n    const errorMsg = boardResponse.message || 'Unknown Pinterest API error';\n    console.error(`Pinterest API error code ${boardResponse.code}: ${errorMsg}`);\n    throw new Error(`Pinterest API error: ${errorMsg}`);\n  }\n\n  // Check for various error formats\n  if (boardResponse.error) {\n    const errorDetail = typeof boardResponse.error === 'string' ? \n      boardResponse.error : JSON.stringify(boardResponse.error);\n    console.error('Pinterest API Error:', errorDetail);\n    throw new Error(`Board creation failed: ${errorDetail}`);\n  }\n\n  if (boardResponse.message && \n      (boardResponse.message.toLowerCase().includes('error') || \n       boardResponse.message.toLowerCase().includes('fail'))) {\n    console.error('Error message in response:', boardResponse.message);\n    throw new Error(`Board creation failed: ${boardResponse.message}`);\n  }\n\n  // Check if we have a successful board creation response\n  if (!boardResponse.id) {\n    console.error('No board ID in response. Full response:', JSON.stringify(boardResponse));\n    // Try to create a helpful error message\n    const responsePreview = JSON.stringify(boardResponse).substring(0, 500);\n    throw new Error(`Board creation failed: No ID in response. Response preview: ${responsePreview}`);\n  }\n\n  // Board created successfully\n  console.log(`Board created successfully with ID: ${boardResponse.id}`);\n\n  return {\n    boardId: boardResponse.id,\n    boardName: boardResponse.name || postData.boardName,\n    boardUrl: `https://pinterest.com/board/${boardResponse.id}/`,\n    postId: trackingRecord.wordpress_post_id,\n    recordId: trackingRecord.id,\n    images: postData.images,\n    postData: postData,\n    wasExisting: false\n  };\n} catch (error) {\n  console.error('Error in Validate Board Creation:', error.message);\n  console.error('Error stack:', error.stack);\n  throw error;\n}"
      },
      "id": "35c9175b-9b36-408d-ae70-c8f8ee15bfb1",
      "name": "Validate Board Creation",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        -2304,
        -240
      ],
      "notes": "Validate board was created successfully"
    },
    {
      "parameters": {
        "operation": "executeQuery",
        "query": "=UPDATE pinterest_processed_posts SET pinterest_board_id = '{{$json.boardId}}', pinterest_board_url = '{{$json.boardUrl}}', status = 'board_created', updated_at = NOW() WHERE id = {{$json.recordId}}",
        "options": {}
      },
      "id": "ad08e426-b3f0-4290-aca0-aa810b61cfec",
      "name": "Update Board Info in DB",
      "type": "n8n-nodes-base.postgres",
      "typeVersion": 2.4,
      "position": [
        -2080,
        -240
      ],
      "credentials": {
        "postgres": {
          "name": "<your credential>"
        }
      },
      "notes": "Update database with board information"
    },
    {
      "parameters": {
        "jsCode": "// Prepare images array for processing (without filesystem operations)\nconst currentData = $input.item.json;\n\n// Get board validation data which contains the images\nconst boardValidationData = $('Validate Board Creation').item.json;\n\nconsole.log('Current data received:', JSON.stringify(currentData, null, 2));\nconsole.log('Board validation data:', JSON.stringify(boardValidationData, null, 2));\n\n// Try to get images from multiple possible sources\nconst images = boardValidationData?.images || \n               boardValidationData?.postData?.images || \n               currentData?.images || \n               currentData?.postData?.images || \n               [];\n\n// Also get board and post data from the validation node\nconst boardId = boardValidationData?.boardId || currentData?.pinterest_board_id || currentData?.boardId;\nconst boardName = boardValidationData?.boardName || currentData?.boardName;\nconst recordId = boardValidationData?.recordId || currentData?.id || currentData?.recordId;\nconst postData = boardValidationData?.postData || currentData?.postData || {};\nconst postId = boardValidationData?.postId || currentData?.wordpress_post_id || currentData?.postId;\n\nif (!images || images.length === 0) {\n  console.error('No images found in any data source');\n  console.error('Available data keys in current:', Object.keys(currentData));\n  console.error('Available data keys in boardValidation:', boardValidationData ? Object.keys(boardValidationData) : 'undefined');\n  throw new Error('No images to process');\n}\n\nif (!boardId) {\n  throw new Error('Board ID not found in data');\n}\n\nconsole.log(`Processing ${images.length} images for board: ${boardName || boardId}`);\n\n// Create items for each image URL\nconst imageItems = [];\n\nfor (let index = 0; index < images.length; index++) {\n  const imageUrl = images[index];\n  \n  console.log(`Preparing image ${index + 1}/${images.length}: ${imageUrl}`);\n  \n  // Validate image URL\n  if (!imageUrl || !imageUrl.startsWith('http')) {\n    console.error(`Invalid image URL at index ${index + 1}: ${imageUrl}`);\n    continue;\n  }\n  \n  imageItems.push({\n    json: {\n      imageUrl: imageUrl,\n      imageIndex: index + 1,\n      totalImages: images.length,\n      boardId: boardId,\n      boardName: boardName,\n      recordId: recordId,\n      postData: postData,\n      postId: postId\n    }\n  });\n  \n  console.log(`Successfully prepared image ${index + 1}`);\n}\n\nif (imageItems.length === 0) {\n  throw new Error('No valid images to process');\n}\n\nconsole.log(`Successfully prepared ${imageItems.length} out of ${images.length} images for pinning`);\n\nreturn imageItems;"
      },
      "id": "59b820a9-47e6-4ff2-ba0d-614e88c8913c",
      "name": "Prepare Images for Pinning",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        -1856,
        -240
      ],
      "notes": "Split images into individual items for processing"
    },
    {
      "parameters": {
        "jsCode": "// Pass through all images for processing\nconst items = $input.all();\n\nconsole.log(`Starting to process ${items.length} images for Pinterest`);\n\n// Simply return all items to be processed\nreturn items;"
      },
      "id": "3892ba4b-e1ab-4671-aa63-749f558bea51",
      "name": "Process All Images",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        -1632,
        -240
      ],
      "notes": "Pass through all images for sequential processing"
    },
    {
      "parameters": {
        "amount": 3,
        "unit": "seconds"
      },
      "id": "13ff06b8-0394-4514-ab9b-e41d89d8baf2",
      "name": "Wait 3 Seconds (Rate Limit)",
      "type": "n8n-nodes-base.wait",
      "typeVersion": 1,
      "position": [
        -1408,
        -240
      ],
      "notes": "Pinterest rate limit delay between pins"
    },
    {
      "parameters": {
        "jsCode": "// Generate optimized Pinterest pin content with website promotion\nconst imageData = $input.item.json;\nconst postTitle = imageData.postData?.postTitle || 'Content Page';\nconst categories = imageData.postData?.categories || '';\nconst imageNum = imageData.imageIndex;\nconst totalImages = imageData.totalImages;\n\n// Your website URL for promotion\nconst websiteUrl = 'https://your-wp-website.com';\nconst postUrl = imageData.postData?.postUrl || websiteUrl;\n\n// Create SEO-optimized description with strong website promotion\nconst description = `\ud83c\udfa8 FREE Printable ${postTitle} Content Page (${imageNum}/${totalImages})\\n\\n\u2705 Instant Download\\n\u2705 High Quality Print-Ready\\n\u2705 Perfect for Kids Ages 3-12\\n\\n\ud83c\udf1f Get 1000s more FREE Website Content  at your-wp-website.com\\n\\nVisit: ${websiteUrl}`;\n\n// Generate relevant hashtags (Pinterest recommends 2-5)\nconst hashtags = '#ContentPages #PrintableContentKids #FreeContentPages #KidsActivities #ContentForKids';\n\n// Create pin title (max 100 chars)\nconst pinTitle = `FREE ${postTitle} Content Page - your-wp-website.com`.substring(0, 100);\n\n// Combine description with hashtags (max 500 chars)\nconst fullDescription = `${description}\\n\\n${hashtags}`.substring(0, 500);\n\nconsole.log('Pin metadata generated:');\nconsole.log('- Title:', pinTitle);\nconsole.log('- Link URL:', websiteUrl);\nconsole.log('- Image URL:', imageData.imageUrl);\nconsole.log('- Board ID:', imageData.boardId);\n\nreturn {\n  ...imageData,\n  pinTitle: pinTitle,\n  pinDescription: fullDescription,\n  pinLink: websiteUrl, // Always link to your main website\n  altText: `Free printable ${postTitle} Content page ${imageNum} from your-wp-website.com`\n};"
      },
      "id": "34b89136-4bd1-46c5-ac40-5a4efda3ec3a",
      "name": "Generate Pin Metadata",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        -1184,
        -240
      ],
      "notes": "Generate SEO-optimized pin title and description"
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://api.pinterest.com/v5/pins",
        "authentication": "genericCredentialType",
        "genericAuthType": "oAuth2Api",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        },
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={{ JSON.stringify({\n  board_id: $json.boardId,\n  media_source: {\n    source_type: \"image_url\",\n    url: $json.imageUrl\n  },\n  title: $json.pinTitle,\n  description: $json.pinDescription,\n  link: $json.pinLink,\n  alt_text: $json.altText\n}) }}",
        "options": {
          "timeout": 30000
        }
      },
      "id": "215d6ee8-ba80-4569-9cb0-cd607947a679",
      "name": "Create Pinterest Pin",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.1,
      "position": [
        -960,
        -240
      ],
      "credentials": {
        "oAuth2Api": {
          "name": "<your credential>"
        }
      },
      "notes": "Create pin on Pinterest board"
    },
    {
      "parameters": {
        "jsCode": "// Validate Pinterest pin creation response\nconst pinResponse = $input.item.json;\nconst pinMetadata = $('Generate Pin Metadata').item.json;\n\nconsole.log('Pinterest API Response:', JSON.stringify(pinResponse, null, 2));\nconsole.log('Pin Metadata:', JSON.stringify({\n  imageUrl: pinMetadata.imageUrl,\n  boardId: pinMetadata.boardId,\n  pinTitle: pinMetadata.pinTitle,\n  imageIndex: pinMetadata.imageIndex,\n  totalImages: pinMetadata.totalImages\n}, null, 2));\n\n// Check for various Pinterest API response formats\nlet pinId = null;\nlet pinUrl = null;\nlet errorMessage = null;\nlet uploadStatus = 'failed';\n\n// Success response should have an id field\nif (pinResponse.id && !pinResponse.code && !pinResponse.error) {\n  pinId = pinResponse.id;\n  pinUrl = `https://pinterest.com/pin/${pinResponse.id}`;\n  uploadStatus = 'uploaded';\n  console.log(`\u2705 Pin created successfully: ${pinUrl}`);\n} \n// Check for error responses\nelse if (pinResponse.code || pinResponse.error || pinResponse.message) {\n  errorMessage = pinResponse.message || pinResponse.error || `API Error Code: ${pinResponse.code}`;\n  console.error(`\u274c Pin creation failed: ${errorMessage}`);\n  \n  // Common Pinterest API errors:\n  if (pinResponse.code === 429) {\n    errorMessage = 'Rate limit exceeded - too many requests';\n  } else if (pinResponse.code === 401) {\n    errorMessage = 'Authentication failed - check Pinterest credentials';\n  } else if (pinResponse.code === 400) {\n    errorMessage = 'Bad request - check image URL and board ID';\n  }\n}\n// Unexpected response format\nelse {\n  errorMessage = 'Unexpected response format from Pinterest API';\n  console.error('Unexpected response:', pinResponse);\n}\n\nreturn {\n  ...pinMetadata,\n  pinResponse: pinResponse,\n  pinId: pinId,\n  pinUrl: pinUrl,\n  uploadStatus: uploadStatus,\n  errorMessage: errorMessage\n};"
      },
      "id": "16d61cec-e978-44fb-b27a-be0e7154c662",
      "name": "Validate Pin Creation",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        -736,
        -240
      ],
      "notes": "Validate Pinterest pin creation and log response"
    },
    {
      "parameters": {
        "operation": "executeQuery",
        "query": "=INSERT INTO pinterest_pins (\n  post_id,\n  wordpress_image_url,\n  pinterest_pin_id,\n  pinterest_pin_url,\n  pin_title,\n  pin_description,\n  upload_status,\n  error_message,\n  created_at\n) VALUES (\n  {{$json.recordId}},\n  '{{$json.imageUrl}}',\n  {{$json.pinId ? \"'\" + $json.pinId + \"'\" : 'NULL'}},\n  {{$json.pinUrl ? \"'\" + $json.pinUrl + \"'\" : 'NULL'}},\n  '{{$json.pinTitle.replace(/'/g, \"''\")}}',\n  '{{$json.pinDescription.replace(/'/g, \"''\")}}',\n  '{{$json.uploadStatus}}',\n  {{$json.errorMessage ? \"'\" + $json.errorMessage.replace(/'/g, \"''\") + \"'\" : 'NULL'}},\n  NOW()\n)",
        "options": {}
      },
      "id": "f37b6267-d655-4ffb-a95f-540b6a36c4eb",
      "name": "Save Pin to Database",
      "type": "n8n-nodes-base.postgres",
      "typeVersion": 2.4,
      "position": [
        -512,
        -240
      ],
      "credentials": {
        "postgres": {
          "name": "<your credential>"
        }
      },
      "notes": "Save pin details to database"
    },
    {
      "parameters": {
        "jsCode": "// No cleanup needed since we're not downloading images anymore\nconst data = $input.item.json;\n\nconsole.log('No file cleanup needed - images were processed directly from URLs');\n\nreturn {\n  ...data,\n  cleanupStatus: 'not_needed',\n  cleanupMessage: 'No cleanup required - images were processed directly from URLs without downloading'\n};"
      },
      "id": "e7cf5056-0391-4883-a8ee-50f12b465894",
      "name": "Clean Up Downloaded Images",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        -64,
        -240
      ],
      "notes": "Delete downloaded images after processing"
    },
    {
      "parameters": {
        "jsCode": "// Generate summary of the processing\nconst finalData = $input.item.json;\n\nreturn {\n  success: true,\n  message: 'Post processed successfully',\n  postId: finalData.wordpress_post_id,\n  boardId: finalData.pinterest_board_id,\n  boardUrl: finalData.pinterest_board_url,\n  totalImages: finalData.total_images,\n  imagesUploaded: finalData.images_uploaded,\n  imagesFailed: finalData.images_failed,\n  processedAt: finalData.processed_at,\n  cleanupStatus: finalData.cleanupStatus || 'not performed',\n  summary: `Successfully created Pinterest board with ${finalData.images_uploaded} pins from WordPress post ${finalData.wordpress_post_id}`\n};"
      },
      "id": "30763ca8-3228-47dd-a1db-7b4f941a9de6",
      "name": "Generate Summary",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        160,
        -240
      ],
      "notes": "Generate final summary of the workflow execution"
    },
    {
      "parameters": {
        "options": {}
      },
      "id": "3fadfc7c-e07f-4fba-9a66-d236fa976abb",
      "name": "Already Processed Message",
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.2,
      "position": [
        -2976,
        -48
      ],
      "notes": "Message when all posts are already processed"
    },
    {
      "parameters": {
        "operation": "executeQuery",
        "query": "=UPDATE pinterest_processed_posts \nSET \n  images_uploaded = (\n    SELECT COUNT(*) \n    FROM pinterest_pins \n    WHERE post_id = {{$('Generate Pin Metadata').item.json.recordId}} \n    AND upload_status = 'uploaded'\n  ),\n  images_failed = (\n    SELECT COUNT(*) \n    FROM pinterest_pins \n    WHERE post_id = {{$('Generate Pin Metadata').item.json.recordId}} \n    AND upload_status = 'failed'\n  ),\n  status = 'completed',\n  processed_at = NOW(),\n  updated_at = NOW()\nWHERE id = {{$('Generate Pin Metadata').item.json.recordId}}\nRETURNING *",
        "options": {}
      },
      "id": "b72110b3-2950-4485-8b4b-cec8e6da93da",
      "name": "Finalize Post Processing1",
      "type": "n8n-nodes-base.postgres",
      "typeVersion": 2.4,
      "position": [
        -288,
        -240
      ],
      "credentials": {
        "postgres": {
          "name": "<your credential>"
        }
      },
      "notes": "Update final status for the processed post"
    }
  ],
  "connections": {
    "Manual Trigger": {
      "main": [
        [
          {
            "node": "Get Processed Posts",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Processed Posts": {
      "main": [
        [
          {
            "node": "Fetch WordPress Posts",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Fetch WordPress Posts": {
      "main": [
        [
          {
            "node": "Find First Unprocessed Post",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Find First Unprocessed Post": {
      "main": [
        [
          {
            "node": "Already Processed?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Already Processed?": {
      "main": [
        [
          {
            "node": "Extract Post Data & Images",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Already Processed Message",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Extract Post Data & Images": {
      "main": [
        [
          {
            "node": "Create Tracking Record",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Create Tracking Record": {
      "main": [
        [
          {
            "node": "Create Pinterest Board",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Create Pinterest Board": {
      "main": [
        [
          {
            "node": "Validate Board Creation",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Validate Board Creation": {
      "main": [
        [
          {
            "node": "Update Board Info in DB",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Update Board Info in DB": {
      "main": [
        [
          {
            "node": "Prepare Images for Pinning",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Prepare Images for Pinning": {
      "main": [
        [
          {
            "node": "Process All Images",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Process All Images": {
      "main": [
        [
          {
            "node": "Wait 3 Seconds (Rate Limit)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Wait 3 Seconds (Rate Limit)": {
      "main": [
        [
          {
            "node": "Generate Pin Metadata",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Generate Pin Metadata": {
      "main": [
        [
          {
            "node": "Create Pinterest Pin",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Create Pinterest Pin": {
      "main": [
        [
          {
            "node": "Validate Pin Creation",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Validate Pin Creation": {
      "main": [
        [
          {
            "node": "Save Pin to Database",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Save Pin to Database": {
      "main": [
        [
          {
            "node": "Finalize Post Processing1",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Finalize Post Processing1": {
      "main": [
        [
          {
            "node": "Clean Up Downloaded Images",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Clean Up Downloaded Images": {
      "main": [
        [
          {
            "node": "Generate Summary",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "active": false,
  "settings": {
    "executionOrder": "v1"
  },
  "versionId": "abdd704e-79f1-4574-a104-17c35da625e0",
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "id": "N9TES0JaeUOTFt6V",
  "tags": []
}