This workflow corresponds to n8n.io template #14595 — we link there as the canonical source.
This workflow follows the Airtable → HTTP Request 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 →
{
"nodes": [
{
"id": "e2c7ef94-f917-4ac0-8553-68ed48d514af",
"name": "Sticky \u2014 Webhook + Normalize",
"type": "n8n-nodes-base.stickyNote",
"position": [
-1104,
1824
],
"parameters": {
"color": 7,
"width": 688,
"height": 740,
"content": "Webhook Trigger: Listens for POST payloads (Shopify, Airtable, or manual). It uses a Webhook Respond node to immediately return a 200 ACK, preventing timeout errors from the sender.\n\nSet (Normalize Fields): Sanitizes raw data into a consistent schema. It maps nested fields (e.g., Shopify\u2019s body.title) to flat variables, applies fallback defaults for missing captions/hashtags, and pulls the ig_account_id from environment variables if the payload is empty."
},
"typeVersion": 1
},
{
"id": "864aab29-bfa0-4ca8-a487-d67b1948c65e",
"name": "Sticky \u2014 Fetch + Upload",
"type": "n8n-nodes-base.stickyNote",
"position": [
-336,
1824
],
"parameters": {
"color": 7,
"width": 518,
"height": 472,
"content": "HTTP \u2014 Fetch Product Image: Downloads the image from image_url as a binary file (imageData). It supports any public source, including Shopify CDNs or S3 buckets.\n\nUpload to URL: Uploads the binary to a media host and returns a public CDN URL."
},
"typeVersion": 1
},
{
"id": "4984fd3c-c727-49b8-b6e1-699e6bf7d750",
"name": "Sticky \u2014 Caption + Container",
"type": "n8n-nodes-base.stickyNote",
"position": [
240,
1840
],
"parameters": {
"color": 7,
"width": 624,
"height": 380,
"content": "Code \u2014 Build Caption: Assembles the final text using the product name, price, and links. It automatically truncates to 2,200 characters to meet Instagram\u2019s API limits and outputs a single final_caption string.\n\nIG \u2014 Create Media Container: Initiates the first half of Instagram\u2019s two-step publishing flow. It sends the image_url and final_caption to the Graph API, which processes the image asynchronously and returns a container_id."
},
"typeVersion": 1
},
{
"id": "aff2900c-09af-4369-8133-b2bb7f347f30",
"name": "Sticky \u2014 Wait + Publish",
"type": "n8n-nodes-base.stickyNote",
"position": [
960,
1840
],
"parameters": {
"color": 7,
"width": 576,
"height": 396,
"content": "Wait (5s Buffer): Pauses the workflow to ensure Instagram completes its asynchronous image processing. For high-resolution files, this can be extended or replaced with a status-check polling loop.\n\nIG \u2014 Publish Container: Executes the final media_publish call using the container_id. Once successful, Instagram returns a Live Post ID, confirming the content is officially on the feed."
},
"typeVersion": 1
},
{
"id": "91928124-30f5-4fd0-a9fa-c6ba9d35b7d0",
"name": "Sticky \u2014 Log + Notify",
"type": "n8n-nodes-base.stickyNote",
"position": [
1584,
1840
],
"parameters": {
"color": 7,
"width": 736,
"height": 384,
"content": "Airtable \u2014 Log Post: Records the Post ID, product name, and timestamp into a tracking base for audits and analytics. Easily swappable with Google Sheets.\n\nSlack \u2014 Notify Team: Sends a formatted alert confirming the post is live. Includes the live link and trigger source. Can be replaced with Email or Discord nodes as needed."
},
"typeVersion": 1
},
{
"id": "3e9bb439-0467-407e-acec-84096aa3d3ba",
"name": "\ud83d\udccb Template Overview1",
"type": "n8n-nodes-base.stickyNote",
"position": [
-2000,
1088
],
"parameters": {
"width": 708,
"height": 724,
"content": "## \ud83d\udce6 Brand Product Drop \u2014 Single Image Scheduler\n\n**What this workflow does:**\nAutomates the full Instagram product announcement pipeline \u2014 from a new product trigger (Shopify, Airtable, or manual webhook) all the way to a published Instagram post with a structured caption and hashtag block.\n\n**Flow Overview:**\n1. \ud83d\udd14 **Webhook Trigger** \u2014 Receives product data (name, image URL, price, hashtags, caption intro)\n2. \ud83e\uddf9 **Set / Normalize Fields** \u2014 Cleans and structures the incoming payload into consistent fields\n3. \ud83c\udf10 **Fetch Product Image** \u2014 Downloads the raw image binary from the external product image URL\n4. \u2601\ufe0f **Upload to URL** \u2014 Uploads the image binary and returns a public CDN URL\n5. \u270d\ufe0f **Build Caption** \u2014 Constructs a brand-ready caption with emoji, price, CTA, and hashtag block\n6. \ud83d\udcf8 **IG: Create Media Container** \u2014 Calls Instagram Graph API to create a media container using the public image URL\n7. \u23f3 **Wait** \u2014 Pauses 5 seconds to allow Instagram to process the media container\n8. \u2705 **IG: Publish Container** \u2014 Publishes the approved container to the Instagram feed\n9. \ud83d\udcca **Log to Airtable** \u2014 Records post ID, timestamp, caption, and image URL for tracking\n10. \ud83d\udd14 **Notify via Slack** \u2014 Sends a confirmation message with the published post details\n\n**Prerequisites:**\n- Instagram Graph API access token (Business or Creator account)\n- Instagram Business Account ID\n- uploadtourl node credentials configured in n8n\n- (Optional) Airtable API key + Base ID for post logging\n- (Optional) Slack API credentials for team notifications\n\n**Supported Trigger Sources:**\n- Shopify `products/create` webhook\n- Airtable automation webhook\n- Manual HTTP POST for testing"
},
"typeVersion": 1
},
{
"id": "86e26a7e-4da6-4ad7-b3d7-caa2ef228265",
"name": "Webhook \u2014 Product Trigger1",
"type": "n8n-nodes-base.webhook",
"position": [
-1040,
2016
],
"parameters": {
"path": "product-drop",
"options": {},
"httpMethod": "POST",
"responseMode": "responseNode"
},
"typeVersion": 2
},
{
"id": "b4b02df3-5716-4752-91c1-729a7f9745d0",
"name": "Webhook Response1",
"type": "n8n-nodes-base.respondToWebhook",
"position": [
-912,
2400
],
"parameters": {
"options": {},
"respondWith": "json",
"responseBody": "={ \"status\": \"received\", \"message\": \"Product drop workflow triggered successfully\" }"
},
"typeVersion": 1
},
{
"id": "d2efad45-7d2d-475f-99c1-a345b9761495",
"name": "Set \u2014 Normalize Fields1",
"type": "n8n-nodes-base.set",
"position": [
-656,
2016
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "f1",
"name": "product_name",
"type": "string",
"value": "={{ $json.body.product_name ?? $json.body.title ?? 'New Product' }}"
},
{
"id": "f2",
"name": "image_url",
"type": "string",
"value": "={{ $json.body.image_url ?? $json.body.images?.[0]?.src ?? '' }}"
},
{
"id": "f3",
"name": "price",
"type": "string",
"value": "={{ $json.body.price ?? $json.body.variants?.[0]?.price ?? '' }}"
},
{
"id": "f4",
"name": "caption_intro",
"type": "string",
"value": "={{ $json.body.caption_intro ?? 'Something new just dropped.' }}"
},
{
"id": "f5",
"name": "hashtags",
"type": "string",
"value": "={{ $json.body.hashtags ?? '#newdrop #product #brand' }}"
},
{
"id": "f6",
"name": "ig_account_id",
"type": "string",
"value": "={{ $json.body.ig_account_id ?? $env.IG_ACCOUNT_ID }}"
},
{
"id": "f7",
"name": "trigger_source",
"type": "string",
"value": "={{ $json.body.source ?? 'webhook' }}"
}
]
}
},
"typeVersion": 3.4
},
{
"id": "a2ac043b-f271-418e-aaad-908f1c8f7d73",
"name": "HTTP \u2014 Fetch Product Image1",
"type": "n8n-nodes-base.httpRequest",
"position": [
-256,
2016
],
"parameters": {
"url": "={{ $json.image_url }}",
"options": {
"response": {
"response": {
"responseFormat": "file",
"outputPropertyName": "imageData"
}
}
}
},
"typeVersion": 4.2
},
{
"id": "ba514950-4bae-4010-a35b-4b11a57cea78",
"name": "Code \u2014 Build Caption1",
"type": "n8n-nodes-base.code",
"position": [
304,
2016
],
"parameters": {
"jsCode": "const item = $input.item.json;\nconst norm = $('Set \u2014 Normalize Fields1').item.json;\n\nconst productName = norm.product_name || 'New Drop';\nconst price = norm.price ? `\ud83d\udcb0 ${norm.price}` : '';\nconst captionIntro = norm.caption_intro || 'Something new just dropped.';\nconst hashtags = norm.hashtags || '#newdrop';\n\nconst lines = [\n captionIntro,\n '',\n `\ud83d\udecd\ufe0f ${productName}`,\n price,\n '',\n '\ud83d\udc49 Link in bio to shop now.',\n '.',\n '.',\n '.',\n hashtags\n].filter(l => l !== undefined && l !== null);\n\nlet finalCaption = lines.join('\\n');\n\nif (finalCaption.length > 2200) {\n finalCaption = finalCaption.substring(0, 2196) + '...';\n}\n\nreturn {\n ...item,\n final_caption: finalCaption,\n public_image_url: item.public_url || item.url || item.file_url || item.cdn_url\n};"
},
"typeVersion": 2
},
{
"id": "15aae634-3e02-4adb-826e-e7b9edeab8cf",
"name": "IG \u2014 Create Media Container1",
"type": "n8n-nodes-base.httpRequest",
"position": [
576,
2016
],
"parameters": {
"url": "=https://graph.facebook.com/v19.0/{{ $('Set \u2014 Normalize Fields1').item.json.ig_account_id }}/media",
"method": "POST",
"options": {},
"sendQuery": true,
"queryParameters": {
"parameters": [
{
"name": "image_url",
"value": "={{ $json.public_image_url }}"
},
{
"name": "caption",
"value": "={{ $json.final_caption }}"
},
{
"name": "access_token",
"value": "={{ $credentials.instagramGraphApi.accessToken }}"
}
]
}
},
"typeVersion": 4.2
},
{
"id": "58cd9531-c677-47e6-a5ee-eeb226809ee8",
"name": "Wait \u2014 5s Processing Buffer1",
"type": "n8n-nodes-base.wait",
"position": [
1024,
2016
],
"parameters": {
"unit": "seconds",
"amount": 5
},
"typeVersion": 1
},
{
"id": "96b03332-c1ba-4e97-8448-038836efdaa1",
"name": "IG \u2014 Publish Container1",
"type": "n8n-nodes-base.httpRequest",
"position": [
1344,
2016
],
"parameters": {
"url": "=https://graph.facebook.com/v19.0/{{ $('Set \u2014 Normalize Fields1').item.json.ig_account_id }}/media_publish",
"method": "POST",
"options": {},
"sendQuery": true,
"queryParameters": {
"parameters": [
{
"name": "creation_id",
"value": "={{ $('IG \u2014 Create Media Container1').item.json.id }}"
},
{
"name": "access_token",
"value": "={{ $credentials.instagramGraphApi.accessToken }}"
}
]
}
},
"typeVersion": 4.2
},
{
"id": "d1c572eb-4e70-4938-8daa-ec2e6bf8d2ec",
"name": "Airtable \u2014 Log Post1",
"type": "n8n-nodes-base.airtable",
"position": [
1664,
2016
],
"parameters": {
"base": {
"__rl": true,
"mode": "id",
"value": "={{ $env.AIRTABLE_BASE_ID }}"
},
"table": {
"__rl": true,
"mode": "name",
"value": "IG Post Log"
},
"columns": {
"value": {
"Status": "Published",
"Caption": "={{ $('Code \u2014 Build Caption1').item.json.final_caption }}",
"Post ID": "={{ $('IG \u2014 Publish Container1').item.json.id }}",
"Image URL": "={{ $('Code \u2014 Build Caption1').item.json.public_image_url }}",
"Product Name": "={{ $('Set \u2014 Normalize Fields1').item.json.product_name }}",
"Published At": "={{ new Date().toISOString() }}",
"Trigger Source": "={{ $('Set \u2014 Normalize Fields1').item.json.trigger_source }}"
},
"schema": [],
"mappingMode": "defineBelow",
"matchingColumns": []
},
"options": {},
"operation": "create"
},
"typeVersion": 2.1
},
{
"id": "b112898f-c983-4126-b2f1-21d420d2d1a8",
"name": "Slack \u2014 Notify Team1",
"type": "n8n-nodes-base.slack",
"position": [
2128,
2016
],
"parameters": {
"select": "channel",
"channelId": {
"__rl": true,
"mode": "id",
"value": "={{ $env.SLACK_CHANNEL_ID }}"
},
"otherOptions": {}
},
"typeVersion": 2.3
},
{
"id": "ff9789d6-0c7a-4d4d-aa7a-43e1add05b8c",
"name": "Upload a File",
"type": "n8n-nodes-uploadtourl.uploadToUrl",
"position": [
0,
2016
],
"parameters": {},
"credentials": {
"uploadToUrlApi": {
"name": "<your credential>"
}
},
"typeVersion": 1
}
],
"connections": {
"Upload a File": {
"main": [
[
{
"node": "Code \u2014 Build Caption1",
"type": "main",
"index": 0
}
]
]
},
"Airtable \u2014 Log Post1": {
"main": [
[
{
"node": "Slack \u2014 Notify Team1",
"type": "main",
"index": 0
}
]
]
},
"Code \u2014 Build Caption1": {
"main": [
[
{
"node": "IG \u2014 Create Media Container1",
"type": "main",
"index": 0
}
]
]
},
"IG \u2014 Publish Container1": {
"main": [
[
{
"node": "Airtable \u2014 Log Post1",
"type": "main",
"index": 0
}
]
]
},
"Set \u2014 Normalize Fields1": {
"main": [
[
{
"node": "HTTP \u2014 Fetch Product Image1",
"type": "main",
"index": 0
}
]
]
},
"Webhook \u2014 Product Trigger1": {
"main": [
[
{
"node": "Webhook Response1",
"type": "main",
"index": 0
},
{
"node": "Set \u2014 Normalize Fields1",
"type": "main",
"index": 0
}
]
]
},
"HTTP \u2014 Fetch Product Image1": {
"main": [
[
{
"node": "Upload a File",
"type": "main",
"index": 0
}
]
]
},
"IG \u2014 Create Media Container1": {
"main": [
[
{
"node": "Wait \u2014 5s Processing Buffer1",
"type": "main",
"index": 0
}
]
]
},
"Wait \u2014 5s Processing Buffer1": {
"main": [
[
{
"node": "IG \u2014 Publish Container1",
"type": "main",
"index": 0
}
]
]
}
}
}
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.
uploadToUrlApi
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
📦 Automated Instagram Product Drop via uploadtourl
Source: https://n8n.io/workflows/14595/ — 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.
Convert your customer satisfaction into high-converting social media content with this fully automated social proof pipeline. This workflow scans your database for top-tier reviews, generates a brande
Automate your entire Instagram carousel publishing pipeline from a single webhook call. This workflow receives a product collection payload, loops through each slide image, uploads every asset via Upl
Automate your post-event Instagram carousel using a fan-out and merge pattern. One Code node splits the photos array into individual n8n items. Every photo then flows through HTTP Fetch, Upload to URL
This workflow automates the post-publish process for YouTube videos, combining advanced SEO optimization, cross-platform promotion, and analytics reporting. It is designed for creators, marketers, and
Automatically scrape LinkedIn posts with Apify, transform them into optimized tweets and threads using Claude AI, store them in Airtable for approval, and publish to X on a daily schedule.