This workflow corresponds to n8n.io template #15899 — we link there as the canonical source.
This workflow follows the Form Trigger → 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 →
{
"id": "2HnxTYzRb1O8xmXi",
"name": "Post text and images to X from an n8n form",
"tags": [],
"nodes": [
{
"id": "96ed6a36-e137-420b-8942-f1e90ad1c116",
"name": "Form Submission Note",
"type": "n8n-nodes-base.stickyNote",
"position": [
752,
480
],
"parameters": {
"color": 7,
"width": 352,
"height": 304,
"content": "## Form submission trigger\n\nThis form is the user-facing entry point. Users enter post text and can optionally upload one image file before submitting."
},
"typeVersion": 1
},
{
"id": "25008822-6bc9-4305-a4b3-16299ffb4ebe",
"name": "Input Validation Note",
"type": "n8n-nodes-base.stickyNote",
"position": [
1024,
480
],
"parameters": {
"color": 7,
"width": 416,
"height": 304,
"content": "## Normalize and validate input\n\nThis step trims the post text, detects the uploaded binary image, verifies supported image types, and blocks empty or oversized files before any X API call runs."
},
"typeVersion": 1
},
{
"id": "a4b54a69-c925-4b9e-9d3d-e585f207e988",
"name": "Invalid Input Note",
"type": "n8n-nodes-base.stickyNote",
"position": [
1488,
656
],
"parameters": {
"color": 7,
"height": 320,
"content": "## Handle invalid input\n\nInvalid submissions return a structured response with `success`, `error_code`, and `error_message` so users can correct the form input."
},
"typeVersion": 1
},
{
"id": "8e9e7139-92d0-4f73-a6c6-a6ef58a84d12",
"name": "Image Upload Note",
"type": "n8n-nodes-base.stickyNote",
"position": [
1760,
400
],
"parameters": {
"color": 7,
"width": 1792,
"height": 576,
"content": "## Image upload path\n\nIf the form includes an image, the workflow uploads it to X first, extracts the returned media ID, and passes that ID into the final post request."
},
"typeVersion": 1
},
{
"id": "eedf3f45-bfd8-4c12-8ac9-118fbdfd6043",
"name": "Text Post Note",
"type": "n8n-nodes-base.stickyNote",
"position": [
2400,
1024
],
"parameters": {
"color": 7,
"width": 688,
"height": 464,
"content": "## Text-only post path\n\nIf no image is present, the workflow sends the post text directly to X and returns the published post URL."
},
"typeVersion": 1
},
{
"id": "4a88d8dd-1ffb-4af2-bdcf-08d383582432",
"name": "Connection Test Note",
"type": "n8n-nodes-base.stickyNote",
"position": [
752,
1024
],
"parameters": {
"color": 7,
"width": 912,
"height": 464,
"content": "## Test connection\n\nRun **Manual X Connection Test** after selecting your X OAuth2 credential. It calls `GET /2/users/me` and returns the connected account username and display name."
},
"typeVersion": 1
},
{
"id": "12237a08-a949-4415-bc3a-f613cfd3254b",
"name": "When Form Submitted",
"type": "n8n-nodes-base.formTrigger",
"position": [
800,
608
],
"parameters": {
"options": {
"path": "a858ac63-43af-41d4-9a63-fe14290d5166",
"buttonLabel": "Post to X",
"appendAttribution": true,
"respondWithOptions": {
"values": {
"formSubmittedText": "Submitted. Check the workflow output for the tweet URL."
}
}
},
"formTitle": "Post to X",
"formFields": {
"values": [
{
"fieldName": "text",
"fieldType": "textarea",
"fieldLabel": "Post text",
"requiredField": true
},
{
"fieldName": "image",
"fieldType": "file",
"fieldLabel": "Image",
"multipleFiles": false,
"acceptFileTypes": ".jpg,.jpeg,.png,.webp,.gif"
}
]
},
"responseMode": "lastNode",
"formDescription": "Publish a text post or a text post with one image directly to X."
},
"typeVersion": 2.5
},
{
"id": "33d4e4ff-a5d8-44ba-9288-3175a0626142",
"name": "Normalize Form Input",
"type": "n8n-nodes-base.code",
"position": [
1072,
608
],
"parameters": {
"jsCode": "const item = $input.first();\nconst text = String(item.json.text || item.json['field-0'] || '').trim();\n\nif (!text) {\n return [{ json: { success: false, error_code: 'missing_text', error_message: 'Post text is required.' } }];\n}\n\nconst binary = item.binary || {};\nconst imageKey = binary.image ? 'image' : (binary['field-1'] ? 'field-1' : Object.keys(binary)[0]);\n\nif (!imageKey) {\n return [{ json: { post_id: item.json.submittedAt || DateTime.now().toISO(), text, hasImage: false } }];\n}\n\nconst image = binary[imageKey];\nconst mediaType = String(image.mimeType || '').toLowerCase();\nconst fileName = image.fileName || 'x-post-image';\n\nif (!mediaType.startsWith('image/')) {\n return [{ json: { success: false, error_code: 'unsupported_media_type', error_message: 'Only image uploads are supported in the free workflow.' } }];\n}\n\nlet mediaCategory;\nif (mediaType === 'image/gif') mediaCategory = 'tweet_gif';\nelse if (['image/jpeg', 'image/jpg', 'image/png', 'image/webp'].includes(mediaType)) mediaCategory = 'tweet_image';\nelse {\n return [{ json: { success: false, error_code: 'unsupported_image_type', error_message: `Unsupported image type: ${mediaType}. Use JPG, PNG, WEBP, or GIF.` } }];\n}\n\nconst buffer = await this.helpers.getBinaryDataBuffer(0, imageKey);\nconst maxBytes = mediaCategory === 'tweet_gif' ? 15 * 1024 * 1024 : 5 * 1024 * 1024;\nif (!buffer.length) {\n return [{ json: { success: false, error_code: 'empty_image', error_message: 'The uploaded image file is empty.' } }];\n}\nif (buffer.length > maxBytes) {\n return [{ json: { success: false, error_code: 'image_too_large', error_message: `The uploaded image is too large for X. Limit: ${Math.round(maxBytes / 1024 / 1024)} MB.` } }];\n}\n\nreturn [{\n json: {\n post_id: item.json.submittedAt || DateTime.now().toISO(),\n text,\n hasImage: true,\n file_name: fileName,\n media_type: mediaType === 'image/jpg' ? 'image/jpeg' : mediaType,\n media_category: mediaCategory,\n total_bytes: buffer.length,\n },\n binary: {\n media: image,\n },\n}];"
},
"typeVersion": 2
},
{
"id": "8be1ef2e-6408-4fa7-b269-0c83a7c80e3b",
"name": "Check Input Validity",
"type": "n8n-nodes-base.if",
"position": [
1296,
608
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "loose"
},
"combinator": "and",
"conditions": [
{
"id": "cond-input-valid",
"operator": {
"type": "boolean",
"operation": "false",
"singleValue": true
},
"leftValue": "={{ $json.success === false }}"
}
]
}
},
"typeVersion": 2.3
},
{
"id": "5361d720-c788-4349-ac6b-f39e819715e4",
"name": "Set Input Error Status",
"type": "n8n-nodes-base.set",
"position": [
1552,
848
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "ie1",
"name": "success",
"type": "boolean",
"value": false
},
{
"id": "ie2",
"name": "error_code",
"type": "string",
"value": "={{ $json.error_code || 'invalid_input' }}"
},
{
"id": "ie3",
"name": "error_message",
"type": "string",
"value": "={{ $json.error_message || 'The form submission is invalid.' }}"
}
]
}
},
"typeVersion": 3.4
},
{
"id": "706c4540-52ae-4daf-9e82-afcb6c609e15",
"name": "Check If Image Exists",
"type": "n8n-nodes-base.if",
"position": [
1792,
496
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "cond-has-image",
"operator": {
"type": "boolean",
"operation": "true",
"singleValue": true
},
"leftValue": "={{ $json.hasImage === true }}"
}
]
}
},
"typeVersion": 2.3
},
{
"id": "de8d1cdd-24c1-4948-af80-40055d9ae06f",
"name": "Post New Tweet",
"type": "n8n-nodes-base.httpRequest",
"onError": "continueRegularOutput",
"position": [
2448,
1232
],
"parameters": {
"url": "https://api.x.com/2/tweets",
"method": "POST",
"options": {
"timeout": 300000,
"response": {
"response": {
"responseFormat": "json"
}
}
},
"jsonBody": "={{ { text: $json.text } }}",
"sendBody": true,
"specifyBody": "json",
"authentication": "genericCredentialType",
"genericAuthType": "oAuth2Api"
},
"credentials": {
"oAuth2Api": {
"name": "<your credential>"
}
},
"typeVersion": 4.4
},
{
"id": "6d8c0e91-71a4-4ac9-b74b-89262bd51196",
"name": "Check Tweet Status",
"type": "n8n-nodes-base.if",
"position": [
2672,
1232
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "loose"
},
"combinator": "and",
"conditions": [
{
"id": "cond-tweet-error",
"operator": {
"type": "string",
"operation": "notEmpty",
"singleValue": true
},
"leftValue": "={{ $json.error }}"
}
]
}
},
"typeVersion": 2.3
},
{
"id": "fb563e73-b5b7-408b-84cf-b2e42ac1f68b",
"name": "Set Tweet Error Status",
"type": "n8n-nodes-base.set",
"position": [
2896,
1328
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "te1",
"name": "success",
"type": "boolean",
"value": false
},
{
"id": "te2",
"name": "error_code",
"type": "string",
"value": "tweet_failed"
},
{
"id": "te3",
"name": "error_message",
"type": "string",
"value": "={{ ($json.error && $json.error.message) || $json.error || 'Tweet failed. Check the X API response in this execution.' }}"
}
]
}
},
"typeVersion": 3.4
},
{
"id": "72c58bc7-c618-476c-b7cf-441c2a316505",
"name": "Confirm Post Success",
"type": "n8n-nodes-base.set",
"position": [
2896,
1136
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "ps1",
"name": "success",
"type": "boolean",
"value": true
},
{
"id": "ps2",
"name": "tweet_id",
"type": "string",
"value": "={{ $json.id || (($json.data && $json.data.id) || '') || $json.tweet_id }}"
},
{
"id": "ps3",
"name": "tweet_url",
"type": "string",
"value": "={{ 'https://x.com/i/web/status/' + ($json.id || (($json.data && $json.data.id) || '') || $json.tweet_id) }}"
}
]
}
},
"typeVersion": 3.4
},
{
"id": "66c979a5-3bd8-473b-9a41-0429f086297f",
"name": "Upload Image to X",
"type": "n8n-nodes-base.httpRequest",
"onError": "continueRegularOutput",
"position": [
2016,
496
],
"parameters": {
"url": "https://api.x.com/2/media/upload",
"method": "POST",
"options": {
"timeout": 300000,
"response": {
"response": {
"responseFormat": "json"
}
}
},
"sendBody": true,
"contentType": "multipart-form-data",
"authentication": "genericCredentialType",
"bodyParameters": {
"parameters": [
{
"name": "media",
"parameterType": "formBinaryData",
"inputDataFieldName": "media"
},
{
"name": "media_category",
"value": "={{ $json.media_category }}"
}
]
},
"genericAuthType": "oAuth2Api"
},
"credentials": {
"oAuth2Api": {
"name": "<your credential>"
}
},
"typeVersion": 4.4
},
{
"id": "fe48981f-6b93-47b1-a8b5-2bae2f4bb217",
"name": "Check Image Upload",
"type": "n8n-nodes-base.if",
"position": [
2208,
496
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "loose"
},
"combinator": "and",
"conditions": [
{
"id": "cond-media-init-error",
"operator": {
"type": "string",
"operation": "notEmpty",
"singleValue": true
},
"leftValue": "={{ $json.error }}"
}
]
}
},
"typeVersion": 2.3
},
{
"id": "6fe217ac-dada-42de-84e2-481848e35621",
"name": "Set Media Upload Error",
"type": "n8n-nodes-base.set",
"position": [
3360,
784
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "muf1",
"name": "success",
"type": "boolean",
"value": false
},
{
"id": "muf2",
"name": "error_code",
"type": "string",
"value": "media_upload_failed"
},
{
"id": "muf3",
"name": "error_message",
"type": "string",
"value": "={{ $json.error_message || ($json.error && $json.error.message) || $json.error || 'Media upload failed. Check the X API response in this execution.' }}"
}
]
}
},
"typeVersion": 3.4
},
{
"id": "6f8fcd36-b2fc-44fe-8684-ee09712a030b",
"name": "Generate Media IDs",
"type": "n8n-nodes-base.code",
"position": [
2464,
608
],
"parameters": {
"jsCode": "const prepared = $('Normalize Form Input').first().json;\nconst upload = $input.first().json;\nconst id = upload.media_id || upload.media_id_string || upload.id || (upload.data && upload.data.id) || (upload.data && upload.data.media_id_string);\nif (!id) {\n return [{ json: { success: false, error_code: 'media_upload_failed', error_message: 'No media ID returned from X image upload.', raw: upload } }];\n}\nreturn [{ json: { text: prepared.text, media_ids: [String(id)] } }];"
},
"typeVersion": 2
},
{
"id": "96237921-c613-4cf7-8b99-6a0e9f13976b",
"name": "Verify Media ID Availability",
"type": "n8n-nodes-base.if",
"position": [
2640,
480
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "strict"
},
"combinator": "and",
"conditions": [
{
"id": "cond-media-success",
"operator": {
"type": "boolean",
"operation": "false",
"singleValue": true
},
"leftValue": "={{ $json.success === false }}"
}
]
}
},
"typeVersion": 2.3
},
{
"id": "7696fc91-1ef4-46fa-adb7-f059cdb09793",
"name": "Post Tweet With Media",
"type": "n8n-nodes-base.httpRequest",
"onError": "continueRegularOutput",
"position": [
2880,
432
],
"parameters": {
"url": "https://api.x.com/2/tweets",
"method": "POST",
"options": {
"timeout": 300000,
"response": {
"response": {
"responseFormat": "json"
}
}
},
"jsonBody": "={{ { text: $json.text, media: { media_ids: $json.media_ids } } }}",
"sendBody": true,
"specifyBody": "json",
"authentication": "genericCredentialType",
"genericAuthType": "oAuth2Api"
},
"credentials": {
"oAuth2Api": {
"name": "<your credential>"
}
},
"typeVersion": 4.4
},
{
"id": "b12f266d-5db5-47d8-9c3d-3f46ad3f38d5",
"name": "Check Media Post Status",
"type": "n8n-nodes-base.if",
"position": [
3072,
448
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "loose"
},
"combinator": "and",
"conditions": [
{
"id": "cond-media-post-error",
"operator": {
"type": "string",
"operation": "notEmpty",
"singleValue": true
},
"leftValue": "={{ $json.error }}"
}
]
}
},
"typeVersion": 2.3
},
{
"id": "0d202b4f-834d-43a9-a9ff-dc1bb7fdc94b",
"name": "Confirm Media Post Success",
"type": "n8n-nodes-base.set",
"position": [
3344,
464
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "pms1",
"name": "success",
"type": "boolean",
"value": true
},
{
"id": "pms2",
"name": "tweet_id",
"type": "string",
"value": "={{ $json.data.id || $json.id || $json.tweet_id }}"
},
{
"id": "pms3",
"name": "tweet_url",
"type": "string",
"value": "={{ 'https://x.com/i/web/status/' + ($json.data.id || $json.id || $json.tweet_id) }}"
},
{
"id": "pms4",
"name": "media_ids",
"type": "array",
"value": "={{ $('Generate Media IDs').first().json.media_ids }}"
}
]
}
},
"typeVersion": 3.4
},
{
"id": "68196d09-24a7-4264-a3bd-c17427051cfb",
"name": "Manual X Connection Test",
"type": "n8n-nodes-base.manualTrigger",
"position": [
800,
1232
],
"parameters": {},
"typeVersion": 1
},
{
"id": "d7baeb25-6a7c-42fc-8a44-4b3e48b35fba",
"name": "Fetch X User Info",
"type": "n8n-nodes-base.httpRequest",
"onError": "continueRegularOutput",
"position": [
1024,
1232
],
"parameters": {
"url": "https://api.x.com/2/users/me",
"options": {
"timeout": 300000,
"response": {
"response": {
"responseFormat": "json"
}
}
},
"authentication": "genericCredentialType",
"genericAuthType": "oAuth2Api"
},
"credentials": {
"oAuth2Api": {
"name": "<your credential>"
}
},
"typeVersion": 4.4
},
{
"id": "b33aa285-7443-4525-aabd-9e6ed064a921",
"name": "Check Test Outcome",
"type": "n8n-nodes-base.if",
"position": [
1248,
1232
],
"parameters": {
"options": {},
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "loose"
},
"combinator": "and",
"conditions": [
{
"id": "cond-test-error",
"operator": {
"type": "string",
"operation": "notEmpty",
"singleValue": true
},
"leftValue": "={{ $json.error }}"
}
]
}
},
"typeVersion": 2.3
},
{
"id": "6748859d-adc2-499e-93f6-1a9025d9476d",
"name": "Record Test Error",
"type": "n8n-nodes-base.set",
"position": [
1472,
1136
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "ter1",
"name": "success",
"type": "boolean",
"value": false
},
{
"id": "ter2",
"name": "error_code",
"type": "string",
"value": "auth_failed"
},
{
"id": "ter3",
"name": "error_message",
"type": "string",
"value": "={{ $json.error || 'Connection test failed. Verify your X OAuth2 credential.' }}"
}
]
}
},
"typeVersion": 3.4
},
{
"id": "d05a6fc3-8ffd-4809-9e47-4bdb2fa8a4ec",
"name": "Record Test Success",
"type": "n8n-nodes-base.set",
"position": [
1472,
1328
],
"parameters": {
"options": {},
"assignments": {
"assignments": [
{
"id": "ts1",
"name": "success",
"type": "boolean",
"value": true
},
{
"id": "ts2",
"name": "username",
"type": "string",
"value": "={{ $json.data.username }}"
},
{
"id": "ts3",
"name": "name",
"type": "string",
"value": "={{ $json.data.name }}"
}
]
}
},
"typeVersion": 3.4
},
{
"id": "9157aea5-a759-42f1-9123-3ccf092f1d98",
"name": "Sticky Note",
"type": "n8n-nodes-base.stickyNote",
"position": [
144,
480
],
"parameters": {
"width": 560,
"height": 640,
"content": "## Post text and images to X from an n8n form\n\n## Who's it for\n\nThis workflow is for creators, founders, marketers, and small teams who want a simple n8n form for publishing short posts to X without building a separate app.\n\n## How it works\n\nA public n8n form collects post text and an optional image. The workflow validates the input, checks whether an image was uploaded, and then either posts text directly to X or uploads the image first and publishes the post with the returned media ID. It returns a success payload with the X post URL, or a clear error message if validation, upload, or posting fails.\n\n## How to set up\n\nCreate an X Developer app with OAuth 2.0 user-context access. In n8n, create an OAuth2 API credential with these scopes: `tweet.read tweet.write users.read offline.access media.write`. Select that credential on each X HTTP Request node, then run **Manual X Connection Test** before sharing the form URL.\n\n## Requirements\n\nAn n8n instance, an X Developer account, and an X OAuth2 API credential with write and media permissions.\n\n## How to customize the workflow\n\nEdit the form fields, adjust image size/type checks in **Normalize Form Input**, or add approval, logging, notifications, or scheduling before the post nodes."
},
"typeVersion": 1
}
],
"active": false,
"settings": {
"binaryMode": "separate",
"executionOrder": "v1"
},
"versionId": "995f21de-5c59-475f-aa15-a65c167d5cef",
"connections": {
"Post New Tweet": {
"main": [
[
{
"node": "Check Tweet Status",
"type": "main",
"index": 0
}
]
]
},
"Fetch X User Info": {
"main": [
[
{
"node": "Check Test Outcome",
"type": "main",
"index": 0
}
]
]
},
"Upload Image to X": {
"main": [
[
{
"node": "Check Image Upload",
"type": "main",
"index": 0
}
]
]
},
"Check Image Upload": {
"main": [
[
{
"node": "Set Media Upload Error",
"type": "main",
"index": 0
}
],
[
{
"node": "Generate Media IDs",
"type": "main",
"index": 0
}
]
]
},
"Check Test Outcome": {
"main": [
[
{
"node": "Record Test Error",
"type": "main",
"index": 0
}
],
[
{
"node": "Record Test Success",
"type": "main",
"index": 0
}
]
]
},
"Check Tweet Status": {
"main": [
[
{
"node": "Set Tweet Error Status",
"type": "main",
"index": 0
}
],
[
{
"node": "Confirm Post Success",
"type": "main",
"index": 0
}
]
]
},
"Generate Media IDs": {
"main": [
[
{
"node": "Verify Media ID Availability",
"type": "main",
"index": 0
}
]
]
},
"When Form Submitted": {
"main": [
[
{
"node": "Normalize Form Input",
"type": "main",
"index": 0
}
]
]
},
"Check Input Validity": {
"main": [
[
{
"node": "Check If Image Exists",
"type": "main",
"index": 0
}
],
[
{
"node": "Set Input Error Status",
"type": "main",
"index": 0
}
]
]
},
"Normalize Form Input": {
"main": [
[
{
"node": "Check Input Validity",
"type": "main",
"index": 0
}
]
]
},
"Check If Image Exists": {
"main": [
[
{
"node": "Upload Image to X",
"type": "main",
"index": 0
}
],
[
{
"node": "Post New Tweet",
"type": "main",
"index": 0
}
]
]
},
"Post Tweet With Media": {
"main": [
[
{
"node": "Check Media Post Status",
"type": "main",
"index": 0
}
]
]
},
"Check Media Post Status": {
"main": [
[
{
"node": "Set Media Upload Error",
"type": "main",
"index": 0
}
],
[
{
"node": "Confirm Media Post Success",
"type": "main",
"index": 0
}
]
]
},
"Manual X Connection Test": {
"main": [
[
{
"node": "Fetch X User Info",
"type": "main",
"index": 0
}
]
]
},
"Verify Media ID Availability": {
"main": [
[
{
"node": "Post Tweet With Media",
"type": "main",
"index": 0
}
],
[
{
"node": "Set Media Upload Error",
"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.
oAuth2Api
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
This workflow uses an n8n Form trigger and the X (Twitter) API to publish a text-only post or a post with a single uploaded image, then returns a success/error payload including the resulting tweet URL. Receives a form submission with required post text and an optional image…
Source: https://n8n.io/workflows/15899/ — 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.
This workflow allows you to import any workflow from a file or another n8n instance and map the credentials easily. A multi-form setup guides you through the entire process At the beginning you have t
[n8n] Advanced URL Parsing and Shortening Workflow - Switchy.io Integration. Uses splitInBatches, stickyNote, httpRequest, html. Event-driven trigger; 56 nodes.
[](https://youtu.be/c7yCZhmMjtI)
N8n recently introduced folders and it has been a big improvement on workflow management on top of the tags.
This workflow automates the creation of press releases for music artists releasing a new single. Upload your MP3, fill in basic info, and receive a publication-ready press release saved as a Google Do