{
  "id": "DemoApparelWf004",
  "name": "04 · Calendar publisher (Demo Apparel Co.)",
  "nodes": [
    {
      "parameters": {
        "content": "## 1 · What is due today\nRuns 09:00 in the brand timezone (workflow setting). Only rows the owner approved and dated today.",
        "height": 330,
        "width": 1160,
        "color": 5
      },
      "id": "914c7815-a814-4ed7-9898-10db1226e8bd",
      "name": "Sticky Note",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        -50,
        160
      ]
    },
    {
      "parameters": {
        "content": "## 2 · Publish per channel\nInstagram: Graph API container + media_publish. Blog: Shopify article as an unpublished draft. Everything else: Buffer.",
        "height": 750,
        "width": 680,
        "color": 6
      },
      "id": "7fbb110f-31cc-4965-9911-4b5fe504ad34",
      "name": "Sticky Note2",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        1190,
        -60
      ]
    },
    {
      "parameters": {
        "content": "## 3 · Write back\nPost URL and status go back to the calendar row.",
        "height": 340,
        "width": 440,
        "color": 4
      },
      "id": "e92e6ee4-df38-4385-a4f8-ded9c0804ecb",
      "name": "Sticky Note3",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        1970,
        150
      ]
    },
    {
      "parameters": {
        "content": "## Error branch\nAny failed API call marks the row \"failed\" and pings the owner.",
        "height": 340,
        "width": 440,
        "color": 3
      },
      "id": "045c4ac0-88db-40bb-81d1-13010b996370",
      "name": "Sticky Note4",
      "type": "n8n-nodes-base.stickyNote",
      "typeVersion": 1,
      "position": [
        1970,
        590
      ]
    },
    {
      "parameters": {
        "rule": {
          "interval": [
            {
              "triggerAtHour": 9
            }
          ]
        }
      },
      "id": "a73268f4-0c92-496a-8309-098b1fcfae90",
      "name": "Daily 09:00 brand time",
      "type": "n8n-nodes-base.scheduleTrigger",
      "typeVersion": 1.4,
      "position": [
        0,
        300
      ]
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "set-0",
              "name": "ig_user_id",
              "value": "REPLACE_WITH_IG_BUSINESS_ACCOUNT_ID",
              "type": "string"
            },
            {
              "id": "set-1",
              "name": "graph_version",
              "value": "v23.0",
              "type": "string"
            },
            {
              "id": "set-2",
              "name": "buffer_profile_id",
              "value": "REPLACE_WITH_BUFFER_PROFILE_ID",
              "type": "string"
            },
            {
              "id": "set-3",
              "name": "shop_domain",
              "value": "REPLACE_WITH_SHOP.myshopify.com",
              "type": "string"
            },
            {
              "id": "set-4",
              "name": "shopify_api_version",
              "value": "2025-07",
              "type": "string"
            },
            {
              "id": "set-5",
              "name": "blog_id",
              "value": "REPLACE_WITH_SHOPIFY_BLOG_ID",
              "type": "string"
            }
          ]
        },
        "options": {}
      },
      "id": "73d1f20a-20f0-4b0d-b205-45d4d50efaf0",
      "name": "Publishing settings",
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.5,
      "position": [
        240,
        300
      ]
    },
    {
      "parameters": {
        "documentId": {
          "__rl": true,
          "mode": "url",
          "value": "https://docs.google.com/spreadsheets/d/REPLACE_WITH_CONTENT_CALENDAR_SHEET_ID/edit"
        },
        "sheetName": {
          "__rl": true,
          "mode": "name",
          "value": "Content calendar"
        },
        "options": {},
        "filtersUI": {
          "values": [
            {
              "lookupColumn": "status",
              "lookupValue": "approved"
            }
          ]
        }
      },
      "id": "d7eaddfc-a890-47c1-993d-fcac5a2f8dc8",
      "name": "Read approved posts",
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4.7,
      "position": [
        480,
        300
      ],
      "credentials": {
        "googleSheetsOAuth2Api": {
          "id": "credGSheets00001",
          "name": "Google Sheets (client)"
        }
      }
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict",
            "version": 3
          },
          "conditions": [
            {
              "id": "c6bfee62-109d-4bce-9837-59a573df1b77",
              "leftValue": "={{ $json.planned_date }}",
              "operator": {
                "type": "string",
                "operation": "equals"
              },
              "rightValue": "={{ $today.toFormat('yyyy-MM-dd') }}"
            },
            {
              "id": "c4d8efd2-0912-4163-8a95-05b5ef0ee8ef",
              "leftValue": "={{ $json.channel }}",
              "operator": {
                "type": "string",
                "operation": "notEquals"
              },
              "rightValue": "email_block"
            }
          ],
          "combinator": "and"
        },
        "looseTypeValidation": true,
        "options": {}
      },
      "id": "a593001a-56ea-4abd-bdc1-28858246f61c",
      "name": "Due today",
      "type": "n8n-nodes-base.filter",
      "typeVersion": 2.3,
      "position": [
        720,
        300
      ]
    },
    {
      "parameters": {
        "rules": {
          "values": [
            {
              "conditions": {
                "options": {
                  "caseSensitive": true,
                  "leftValue": "",
                  "typeValidation": "strict",
                  "version": 3
                },
                "conditions": [
                  {
                    "id": "060d62ff-7c3d-4969-9ec9-448007062783",
                    "leftValue": "={{ $json.channel }}",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    },
                    "rightValue": "instagram"
                  }
                ],
                "combinator": "and"
              },
              "renameOutput": true,
              "outputKey": "Instagram"
            },
            {
              "conditions": {
                "options": {
                  "caseSensitive": true,
                  "leftValue": "",
                  "typeValidation": "strict",
                  "version": 3
                },
                "conditions": [
                  {
                    "id": "1b4a5c0e-d46d-409c-a0dd-56d67e17fd00",
                    "leftValue": "={{ $json.channel }}",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    },
                    "rightValue": "blog_intro"
                  }
                ],
                "combinator": "and"
              },
              "renameOutput": true,
              "outputKey": "Blog"
            }
          ]
        },
        "options": {
          "fallbackOutput": "extra",
          "renameFallbackOutput": "Other channels (Buffer)"
        }
      },
      "id": "17a51d0b-3356-48c8-8419-3f10d890e16a",
      "name": "Route by channel",
      "type": "n8n-nodes-base.switch",
      "typeVersion": 3.4,
      "position": [
        960,
        300
      ]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "=https://graph.facebook.com/{{ $('Publishing settings').first().json.graph_version }}/{{ $('Publishing settings').first().json.ig_user_id }}/media",
        "authentication": "predefinedCredentialType",
        "nodeCredentialType": "facebookGraphApi",
        "sendQuery": true,
        "queryParameters": {
          "parameters": [
            {
              "name": "image_url",
              "value": "={{ $json.image_url }}"
            },
            {
              "name": "caption",
              "value": "={{ $json.text }}"
            }
          ]
        },
        "options": {}
      },
      "id": "8091593b-e2a6-4d6d-a827-8a4b6a0753c8",
      "name": "IG: create media container",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.5,
      "position": [
        1240,
        100
      ],
      "credentials": {
        "facebookGraphApi": {
          "id": "credMetaGraph001",
          "name": "Meta Graph API (client)"
        }
      },
      "onError": "continueErrorOutput"
    },
    {
      "parameters": {
        "method": "POST",
        "url": "=https://graph.facebook.com/{{ $('Publishing settings').first().json.graph_version }}/{{ $('Publishing settings').first().json.ig_user_id }}/media_publish",
        "authentication": "predefinedCredentialType",
        "nodeCredentialType": "facebookGraphApi",
        "sendQuery": true,
        "queryParameters": {
          "parameters": [
            {
              "name": "creation_id",
              "value": "={{ $json.id }}"
            }
          ]
        },
        "options": {}
      },
      "id": "775458f3-e92f-4e8a-91b6-ceb538f32d02",
      "name": "IG: publish container",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.5,
      "position": [
        1480,
        100
      ],
      "credentials": {
        "facebookGraphApi": {
          "id": "credMetaGraph001",
          "name": "Meta Graph API (client)"
        }
      },
      "onError": "continueErrorOutput"
    },
    {
      "parameters": {
        "url": "=https://graph.facebook.com/{{ $('Publishing settings').first().json.graph_version }}/{{ $json.id }}",
        "authentication": "predefinedCredentialType",
        "nodeCredentialType": "facebookGraphApi",
        "sendQuery": true,
        "queryParameters": {
          "parameters": [
            {
              "name": "fields",
              "value": "id,permalink"
            }
          ]
        },
        "options": {}
      },
      "id": "117879c8-a2ef-4a73-b321-7e755f6c57fb",
      "name": "IG: get permalink",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.5,
      "position": [
        1720,
        100
      ],
      "credentials": {
        "facebookGraphApi": {
          "id": "credMetaGraph001",
          "name": "Meta Graph API (client)"
        }
      },
      "onError": "continueErrorOutput"
    },
    {
      "parameters": {
        "method": "POST",
        "url": "=https://{{ $('Publishing settings').first().json.shop_domain }}/admin/api/{{ $('Publishing settings').first().json.shopify_api_version }}/blogs/{{ $('Publishing settings').first().json.blog_id }}/articles.json",
        "authentication": "predefinedCredentialType",
        "nodeCredentialType": "shopifyAccessTokenApi",
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={{ JSON.stringify({ article: {\n  title: $json.source,\n  body_html: '<p>' + $json.text.split('\\n\\n').join('</p><p>') + '</p>',\n  tags: 'demo-apparel, ' + $json.segment,\n  published: false\n} }) }}",
        "options": {}
      },
      "id": "9a189b71-19b2-4440-a4b7-1cadcb06b3e5",
      "name": "Shopify: create blog draft",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.5,
      "position": [
        1240,
        300
      ],
      "credentials": {
        "shopifyAccessTokenApi": {
          "id": "credShopify00001",
          "name": "Shopify (client)"
        }
      },
      "onError": "continueErrorOutput"
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://api.bufferapp.com/1/updates/create.json",
        "authentication": "genericCredentialType",
        "genericAuthType": "httpQueryAuth",
        "sendBody": true,
        "contentType": "form-urlencoded",
        "bodyParameters": {
          "parameters": [
            {
              "name": "profile_ids[]",
              "value": "={{ $('Publishing settings').first().json.buffer_profile_id }}"
            },
            {
              "name": "text",
              "value": "={{ $json.text }}"
            },
            {
              "name": "now",
              "value": "true"
            }
          ]
        },
        "options": {}
      },
      "id": "8124f7a1-136a-433b-b5f3-3401b98d2451",
      "name": "Buffer: queue post",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.5,
      "position": [
        1240,
        500
      ],
      "credentials": {
        "httpQueryAuth": {
          "id": "credBuffer000001",
          "name": "Buffer access token (client)"
        }
      },
      "onError": "continueErrorOutput"
    },
    {
      "parameters": {
        "jsCode": "// Normalise the three API responses into one shape for the sheet.\nconst row = $('Route by channel').item.json;\nconst r = $json;\nlet post_url = '';\nlet status = 'published';\nif (r.permalink) post_url = r.permalink;                                     // Instagram\nelse if (r.article) { post_url = 'https://' + $('Publishing settings').first().json.shop_domain + '/admin/articles/' + r.article.id; status = 'blog draft created'; }\nelse if (r.updates) { post_url = 'buffer:' + r.updates[0]?.id; status = 'sent to Buffer'; }\nreturn { json: { draft_id: row.draft_id, post_url, status, published_at: $now.toISO() } };",
        "mode": "runOnceForEachItem"
      },
      "id": "f67b7114-a2a3-47b9-ac72-09945135f4fc",
      "name": "Collect post URL",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        2020,
        300
      ]
    },
    {
      "parameters": {
        "operation": "update",
        "documentId": {
          "__rl": true,
          "mode": "url",
          "value": "https://docs.google.com/spreadsheets/d/REPLACE_WITH_CONTENT_CALENDAR_SHEET_ID/edit"
        },
        "sheetName": {
          "__rl": true,
          "mode": "name",
          "value": "Content calendar"
        },
        "columns": {
          "mappingMode": "defineBelow",
          "value": {
            "draft_id": "={{ $json.draft_id }}",
            "status": "={{ $json.status }}",
            "post_url": "={{ $json.post_url }}",
            "published_at": "={{ $json.published_at }}"
          },
          "matchingColumns": [
            "draft_id"
          ],
          "schema": [
            {
              "id": "draft_id",
              "displayName": "draft_id",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": false
            },
            {
              "id": "status",
              "displayName": "status",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": false
            },
            {
              "id": "post_url",
              "displayName": "post_url",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": false
            },
            {
              "id": "published_at",
              "displayName": "published_at",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": false
            }
          ]
        },
        "options": {}
      },
      "id": "ee3e1bed-e615-49d7-88af-20ad83c6eeae",
      "name": "Write back URL + status",
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4.7,
      "position": [
        2260,
        300
      ],
      "credentials": {
        "googleSheetsOAuth2Api": {
          "id": "credGSheets00001",
          "name": "Google Sheets (client)"
        }
      }
    },
    {
      "parameters": {
        "operation": "update",
        "documentId": {
          "__rl": true,
          "mode": "url",
          "value": "https://docs.google.com/spreadsheets/d/REPLACE_WITH_CONTENT_CALENDAR_SHEET_ID/edit"
        },
        "sheetName": {
          "__rl": true,
          "mode": "name",
          "value": "Content calendar"
        },
        "columns": {
          "mappingMode": "defineBelow",
          "value": {
            "draft_id": "={{ $('Route by channel').item.json.draft_id }}",
            "status": "failed",
            "error": "={{ $json.error?.message || JSON.stringify($json.error || $json) }}"
          },
          "matchingColumns": [
            "draft_id"
          ],
          "schema": [
            {
              "id": "draft_id",
              "displayName": "draft_id",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": false
            },
            {
              "id": "status",
              "displayName": "status",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": false
            },
            {
              "id": "error",
              "displayName": "error",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true,
              "removed": false
            }
          ]
        },
        "options": {}
      },
      "id": "7ccaf666-b807-4f24-933b-63c50118549a",
      "name": "Set status: failed",
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4.7,
      "position": [
        2020,
        740
      ],
      "credentials": {
        "googleSheetsOAuth2Api": {
          "id": "credGSheets00001",
          "name": "Google Sheets (client)"
        }
      }
    },
    {
      "parameters": {
        "chatId": "REPLACE_WITH_OWNER_CHAT_ID",
        "text": "=Publishing failed for {{ $('Route by channel').item.json.channel }} post {{ $('Route by channel').item.json.draft_id }}.\nError: {{ $('Set status: failed').item.json.error || 'see execution log' }}\nRow marked \"failed\" in the Content calendar.",
        "additionalFields": {
          "appendAttribution": false
        }
      },
      "id": "f13291a9-d0f5-4a40-92ce-8b99a973ef2f",
      "name": "Alert owner on Telegram",
      "type": "n8n-nodes-base.telegram",
      "typeVersion": 1.2,
      "position": [
        2260,
        740
      ],
      "credentials": {
        "telegramApi": {
          "id": "credTelegram0001",
          "name": "Telegram bot (client)"
        }
      }
    }
  ],
  "connections": {
    "Daily 09:00 brand time": {
      "main": [
        [
          {
            "node": "Publishing settings",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Publishing settings": {
      "main": [
        [
          {
            "node": "Read approved posts",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Read approved posts": {
      "main": [
        [
          {
            "node": "Due today",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Due today": {
      "main": [
        [
          {
            "node": "Route by channel",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Route by channel": {
      "main": [
        [
          {
            "node": "IG: create media container",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Shopify: create blog draft",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Buffer: queue post",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "IG: create media container": {
      "main": [
        [
          {
            "node": "IG: publish container",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Set status: failed",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "IG: publish container": {
      "main": [
        [
          {
            "node": "IG: get permalink",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Set status: failed",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "IG: get permalink": {
      "main": [
        [
          {
            "node": "Collect post URL",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Set status: failed",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Shopify: create blog draft": {
      "main": [
        [
          {
            "node": "Collect post URL",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Set status: failed",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Buffer: queue post": {
      "main": [
        [
          {
            "node": "Collect post URL",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Set status: failed",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Collect post URL": {
      "main": [
        [
          {
            "node": "Write back URL + status",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Set status: failed": {
      "main": [
        [
          {
            "node": "Alert owner on Telegram",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "active": false,
  "settings": {
    "executionOrder": "v1",
    "timezone": "America/New_York"
  },
  "pinData": {},
  "meta": {
    "templateCredsSetupCompleted": false
  },
  "tags": []
}
