ChatGPTCreativeWritingAI score · 85/100AI-assisted edit · no obvious prompt-injection riskn8n.io

Scheduled AI Blog Draft Generator and Ghost CMS Publisher in n8n

Schedule trigger → OpenAI generates post with metadata → Code extracts and formats → Ghost Admin API publishes

Some experienceStepsWorkflowPromptTip
Who
Blog operators and Ghost site owners who want hands-off content publishing
You get
Automatically triggers every 12 hours, uses gpt-4.1-mini to generate a post with title, tags, and SEO meta, then publishes directly to Ghost via the Admin API
You need
n8n · schedule · openai · http-request · code

Constraints

Prerequisites: a running n8n instance; openAiApi and ghostAdminApi credentials configured in n8n Credential Manager; a Ghost site with Admin API enabled. Costs: OpenAI API usage billed by token; Ghost hosting per your plan. Boundaries: default model is gpt-4.1-mini-2025-04-14, swappable to gpt-4.1 or gpt-4 in the OpenAI node; Ghost URL must be replaced with your actual domain; posts default to published status, changeable in the Code node.

What they did

Author darkrangeholdings built a 5-node n8n workflow template: a Schedule Trigger fires every 12 hours; an OpenAI node calls gpt-4.1-mini to generate a blog post body, title, SEO meta description, and 5 tags; the first Code node uses regex to extract metadata from the model output and converts the body to HTML; the second Code node wraps that HTML into Ghost's mobiledoc format; finally an HTTP Request node POSTs to the Ghost Admin API to publish the post. The template avoids hardcoded API keys by using n8n Credential Manager.

How it went

The template is listed as published on n8n.io with 4,232 total views at time of collection (author_report). No specific post-publish engagement or SEO performance data is provided.

Worth knowing

1. Default model is gpt-4.1-mini-2025-04-14; you can switch to gpt-4.1 or gpt-4 in the OpenAI node, but output format consistency affects downstream regex extraction. 2. The Code node regex depends on the model returning **Blog Post Title:**, **Meta Description:**, and **Tags:** markers; extraction fails if the format drifts. 3. The Ghost URL in the HTTP Request node is hardcoded as https://yourcustomurl.com/ghost/api/admin/posts/ and must be replaced with your real domain. 4. Posts default to published status; change to draft in the Code node if needed. 5. No error handling or retry logic is included; the workflow will fail on non-2xx Ghost API responses. 6. This site has not tested the workflow; test the Code node regex with sample data before relying on scheduled runs. [Boundary] Source default is only gpt-4.1-mini; "switch to gpt-4.1 or gpt-4" is an optional node setting note, not a configured model in this template.

n8n workflow template

Structure checked · Not run-tested

Use “Import from File” in n8n, or copy the JSON and paste it into the editor. Connect your own accounts, then try test data. The template is inactive by default.

Setup and test checklist

Credentials to connect

  • OpenAI node: connect OpenAI API credentials.
  • HTTP Request1 node: connect Ghost Admin API credentials.

Configuration to replace

  • Source template is n8n public workflow #5934 (~8 nodes). Keep inactive after import; verify triggers, expressions and external API endpoints.
  • Sticky-note guidance is informational; replace placeholders with your own accounts, sheet schemas or channel IDs.

Test input and expected output

  • Manually run the main path once in a test workspace and confirm credentialed nodes authenticate without errors.
  • Drive one synthetic/redacted sample through and check output fields and downstream writes before enabling in production.

Template changes

  • Author credential bindings, webhookId and execution data are removed; the workflow is inactive. Nodes, connections and original prompts are preserved. Not run-tested in n8n.
  • No business-logic edits; verify keys, quotas and privacy compliance before use.

Template retrieved: 2026-10-24

Steps

  1. Set up the Schedule Trigger

    Add a Schedule Trigger node. Set the rule to run every 12 hours (field: hours, hoursInterval: 12). You may set triggerAtMinute (e.g., 34) to control the exact minute. Save the node as the workflow entry point.

  2. Configure the OpenAI generation node

    Add an OpenAI node (@n8n/n8n-nodes-langchain.openAi) and select your openAiApi credential. Choose model gpt-4.1-mini-2025-04-14 (or switch to gpt-4.1 / gpt-4). Enter the prompt in Messages to request the blog body, title, SEO meta description, and 5 tags.

    Execution prompt · Source text。OpenAI node messages.values[0].content field

    Write a high-quality blog post on a creative or thought-provoking topic. The tone should be engaging and immersive. Length: 2–4 paragraphs.
    
    Then add a brief paragraph offering an alternative perspective or logical counterpoint.
    
    Finally, generate:
    - Blog post title
    - Meta description
    - 5 tags
  3. Extract content and metadata (Code node)

    Add a Code node connected after OpenAI. Use JavaScript to parse the model output with regex, extracting values after **Blog Post Title:**, **Meta Description:**, and **Tags:**; separate body from metadata; wrap the body in a centered HTML div preserving line breaks; return a JSON object with title, html, status (published), tags array, and meta_description.

    Execution prompt · Source text。First Code node (id=4c460c9f-0074-4b9a-be5c-f0cfb8bae9bd) jsCode field

    const content = $json.message.content;
    
    // Extract Blog Post Title
    const titleMatch = content.match(/\*\*Blog Post Title:\*\*\s*(.+)/i);
    const title = titleMatch ? titleMatch[1].trim() : 'Untitled';
    
    // Extract Meta Description
    const metaMatch = content.match(/\*\*Meta Description:\*\*\s*(.+)/i);
    const meta = metaMatch ? metaMatch[1].trim() : '';
    
    // Extract Tags
    const tagsMatch = content.match(/\*\*Tags:\*\*\s*(.+)/i);
    const rawTags = tagsMatch ? tagsMatch[1] : '';
    const tags = rawTags.split(',').map(t => t.trim()).filter(t => t);
    
    // Strip metadata from the body
    const body = content
      .split('\n\n')
      .filter(p => !/\*\*Blog Post Title:\*\*/i.test(p) && !/\*\*Meta Description:\*\*/i.test(p) && !/\*\*Tags:\*\*/i.test(p))
      .join('\n\n');
    
    // Wrap in clean HTML
    const html = `<div style="text-align:center; white-space:pre-line;">${body.trim()}</div>`;
    
    // Return Ghost-compatible JSON
    return [
      {
        json: {
          title,
          html,
          status: 'published',
          tags,
          meta_description: meta
        }
      }
    ];
  4. Format as Ghost mobiledoc (Code1 node)

    Add a second Code node receiving the previous JSON. Embed the HTML into a mobiledoc structure (version: 0.3.1, cards: [['html', {html: htmlContent}]], sections: [[10, 0]]); convert the tags string array into {name: tag} objects; assemble a posts array with title, mobiledoc (stringified JSON), status, and tags; pass output to the HTTP Request node.

    Execution prompt · Source text。Code1 node (id=cb384074-daf5-4945-adf6-40657498493d) jsCode field

    const title = $json.title;
    const status = $json.status || 'published';
    const tags = ($json.tags || []).map(tag => ({ name: tag }));
    
    // Already wrapped with styling and line breaks — use as-is
    const htmlContent = $json.html;
    
    const mobiledoc = JSON.stringify({
      version: '0.3.1',
      atoms: [],
      cards: [['html', { html: htmlContent }]],
      markups: [],
      sections: [[10, 0]]
    });
    
    return [
      {
        posts: [
          {
            title,
            mobiledoc,
            status,
            tags
          }
        ]
      }
    ];
  5. Publish to Ghost (HTTP Request node)

    Add an HTTP Request node with POST method. Replace the URL with your actual Ghost domain: https://yourdomain.com/ghost/api/admin/posts/. Enable sendBody, set body type to JSON, and enter {{ JSON.stringify($json) }}. Add header Content-Type: application/json. Set Authentication to predefinedCredentialType, choose ghostAdminApi, and select your configured Ghost Admin API credential.

  6. Save and activate the workflow

    Verify connections: Schedule Trigger → OpenAI → Code → Code1 → HTTP Request1. Confirm the Ghost URL and credentials are replaced with real values. Save the workflow and toggle Active. For first-time testing, click Execute Workflow and check your Ghost admin for the new post. [First-test safety] Before the first test, set status to draft in the first Code node, or temporarily disconnect the HTTP Request node; restore publishing only after the output shape looks correct.

Prompt you can copy

Setup guidance · Editorial suggestion。Hand to setup assistant; confirm inputs and permissions first

This block is the prompt. Ads are never placed here.

You are an n8n workflow setup assistant. The user wants to recreate the "Scheduled AI blog draft generator and Ghost CMS publisher" template. Assist in this order:
1. Confirm the user has access to an n8n instance, plus an OpenAI API Key and a Ghost Admin API Key.
2. Guide them to import workflow.json or build the nodes manually.
3. Emphasize replacing the HTTP Request URL from yourcustomurl.com to the actual Ghost domain.
4. Recommend a manual execution before scheduling to verify the post appears in Ghost.
5. If OpenAI output format deviates from the regex expectations, advise adjusting the match rules in the Code node.

Before building, prepare your n8n account, OpenAI API Key, Ghost Admin API Key, and your Ghost blog's Admin API URL.

Source

Automated blog post generation with GPT-4 and publishing to Ghost CMS · darkrangeholdings · n8n.io · 2025-07-13

Comments & feedback

Tried this method? Share results, problems or improvements. Comments appear after moderation. Please avoid sensitive information.

Loading…

    Related