This workflow follows the HTTP Request → Postgres recipe pattern — see all workflows that pair these two integrations.
The workflow JSON
Copy or download the full n8n JSON below. Paste it into a new n8n workflow, add your credentials, activate. Full import guide →
{
"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": []
}
Credentials you'll need
Each integration node will prompt for credentials when you import. We strip credential IDs before publishing — you'll add your own.
oAuth2Apipostgres
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
Pinterest Board Automation for wordpress. Uses postgres, httpRequest. Event-driven trigger; 21 nodes.
Source: https://gist.github.com/omerbsh/40de85f005694bd32b6e9d852ffa1b1b — original creator credit. Request a take-down →
Related workflows
Workflows that share integrations, category, or trigger type with this one. All free to copy and import.
Creating high-quality, SEO-friendly blog posts consistently can be time-consuming. This template helps content creators, bloggers, SEO specialists, and agencies fully automate their blogging workflow.
Auto WordPress Blog Generator (GPT + Postgres + WP Media). Uses httpRequest, postgres. Scheduled trigger; 46 nodes.
AI-Generated Summary Block for WordPress Posts - with OpenAI, WordPress, Google Sheets & Slack. Uses httpRequest, googleSheets, slack, wordpress. Event-driven trigger; 32 nodes.
This workflow is for content teams, marketers, and solo site owners who draft posts in Google Sheets and want a reliable way to publish to WordPress automatically — including featured images and tags.
wordpress_fb. Uses googleSheets, httpRequest. Event-driven trigger; 28 nodes.