Back to Blog
Developer Tutorial

How to Post from Slack to Instagram via ViralGhost

Set up a Slack slash command that publishes to Instagram in under 15 minutes. No complex infrastructure — just a serverless function and the ViralGhost API.

February 26, 202612 min read

How to Post from Slack to Instagram via ViralGhost

Imagine typing /post Just shipped our new feature! 🚀 in Slack and having it show up on your Instagram feed. No app switching, no copy-pasting, no logging into yet another dashboard.

This is one of the most-requested workflows for teams that live in Slack: see something worth sharing, post it to social immediately (or schedule it for later), and get back to work.

In this tutorial, we'll build this exact workflow using a Cloudflare Worker (free tier) and the ViralGhost Agent API. Total setup time: about 15 minutes.

What We're Building

A Slack slash command (/post) that:

1. Takes your message text as input
2. Sends it to the ViralGhost API
3. Publishes it to Instagram (and optionally Facebook, X, LinkedIn)
4. Confirms the post back in Slack

We'll also add an optional approval flow where the command schedules a draft and a teammate can approve it before it goes live.

Prerequisites

  • ViralGhost account with Instagram linked ([sign up free](https://viralghost.xyz))

  • Slack workspace where you can install apps

  • Cloudflare account ([free tier](https://workers.cloudflare.com/) works perfectly)

  • Basic familiarity with JavaScript
  • Step 1: Get Your ViralGhost Credentials

    1. Sign up at [viralghost.xyz](https://viralghost.xyz) (free, no credit card)
    2. Link your Instagram account from the dashboard
    3. Go to Settings → API → Create API Key
    4. Note your Instagram account ID from the Accounts page (e.g., acc_instagram_123)

    Step 2: Create the Cloudflare Worker

    We'll use a Cloudflare Worker as our serverless backend. It receives the Slack slash command webhook and forwards it to ViralGhost.

    Create a new directory and initialize:

    bash
    mkdir slack-to-instagram && cd slack-to-instagram
    npm init -y
    npm install -D wrangler

    Create wrangler.toml:

    toml
    name = "slack-to-instagram"
    main = "worker.js"
    compatibility_date = "2026-02-01"

    [vars]
    VIRALGHOST_API_KEY = "vg_live_your_api_key"
    INSTAGRAM_ACCOUNT_ID = "acc_instagram_123"

    Now create worker.js:

    javascript
    export default {
    async fetch(request, env) {
    // Slack sends slash commands as form data
    const formData = await request.formData();
    const text = formData.get("text");
    const userName = formData.get("user_name");
    const channelName = formData.get("channel_name");

    // Handle empty command
    if (!text || text.trim() === "") {
    return jsonResponse({
    response_type: "ephemeral",
    text: "📝 Usage: /post Your caption here\nOptional scheduling: /post 2026-03-01T10:00 Your caption here"
    });
    }

    // Parse optional schedule datetime
    const dateRegex = /^(\d{4}-\d{2}-\d{2}T\d{2}:\d{2})\s+(.+)$/;
    const match = text.match(dateRegex);

    let postText = text;
    let scheduleAt = undefined;

    if (match) {
    scheduleAt = match[1] + ":00Z";
    postText = match[2];
    }

    // Build the ViralGhost API request
    const payload = {
    accounts: [env.INSTAGRAM_ACCOUNT_ID],
    content: { text: postText }
    };

    if (scheduleAt) {
    payload.schedule_at = scheduleAt;
    }

    // Post to ViralGhost
    try {
    const vgResp await fetch("https://api.viralghost.xyz/v1/posts", {
    method: "POST",
    headers: {
    "Authorization": Bearer ${env.VIRALGHOST_API_KEY},
    "Content-Type": "application/json"
    },
    body: JSON.stringify(payload)
    });

    const result = await vgResponse.json();

    if (vgResponse.ok) {
    const timeInfo = scheduleAt
    ? 📅 Scheduled for ${new Date(scheduleAt).toLocaleString()}
    : "📤 Posted immediately";

    return jsonResponse({
    response_type: "in_channel",
    text: Instagram post ${scheduleAt ? "scheduled" : "published"}!\n\n> ${postText}\n\n${timeInfo}\n👤 Posted by @${userName} from #${channelName}
    });
    } else {
    return jsonResponse({
    response_type: "ephemeral",
    text: ❌ Failed to post: ${result.message}
    });
    }
    } catch (error) {
    return jsonResponse({
    response_type: "ephemeral",
    text: ❌ Error: ${error.message}
    });
    }
    }
    };

    function jsonResponse(data) {
    return new Response(JSON.stringify(data), {
    headers: { "Content-Type": "application/json" }
    });
    }

    Step 3: Deploy the Worker

    bash
    npx wrangler deploy

    You'll get a URL like https://slack-to-instagram.your-subdomain.workers.dev. Copy this — you'll need it for the Slack app.

    Step 4: Create the Slack App

    1. Go to [api.slack.com/apps](https://api.slack.com/apps) → Create New AppFrom scratch
    2. Name it something like "ViralGhost Poster"
    3. Select your workspace

    Add the Slash Command

    1. In the left sidebar, click Slash CommandsCreate New Command
    2. Fill in:
    - Command: /post
    - Request URL: Your Cloudflare Worker URL
    - Short Description: Post to Instagram via ViralGhost
    - Usage Hint: [optional: YYYY-MM-DDTHH:MM] your caption text
    3. Click Save

    Install to Workspace

    1. Go to Install App in the sidebar
    2. Click Install to Workspace
    3. Authorize the permissions

    Step 5: Test It

    Open any Slack channel and type:


    /post Just launched our new feature! Check it out 🚀

    You should see a confirmation message in the channel, and the post will appear on your Instagram.

    To schedule a post for later:


    /post 2026-03-01T10:00 Big announcement coming Monday! Stay tuned 👀

    Bonus: Multi-Platform Posting

    Want to post to Facebook, X, and LinkedIn too? Just add more account IDs:

    javascript
    const payload = {
    accounts: [
    env.INSTAGRAM_ACCOUNT_ID,
    env.FACEBOOK_ACCOUNT_ID,
    env.X_ACCOUNT_ID,
    env.LINKEDIN_ACCOUNT_ID
    ],
    content: { text: postText }
    };

    Update your wrangler.toml with the additional account IDs and redeploy.

    Bonus: Approval Flow

    For teams that want a review step before posts go live, here's how to add an approval flow:

    javascript
    // Instead of posting immediately, schedule as a draft
    const payload = {
    accounts: [env.INSTAGRAM_ACCOUNT_ID],
    content: { text: postText },
    dry_run: true // Validate without publishing
    };

    // Send an interactive message to Slack with Approve/Reject buttons
    return jsonResponse({
    response_type: "in_channel",
    blocks: [
    {
    type: "section",
    text: {
    type: "mrkdwn",
    text: 📝 New post pending approval\n\n> ${postText}\n\n👤 Submitted by @${userName}
    }
    },
    {
    type: "actions",
    elements: [
    {
    type: "button",
    text: { type: "plain_text", text: "✅ Approve & Post" },
    style: "primary",
    action_id: "approve_post",
    value: JSON.stringify({ text: postText, accounts: [env.INSTAGRAM_ACCOUNT_ID] })
    },
    {
    type: "button",
    text: { type: "plain_text", text: "❌ Reject" },
    style: "danger",
    action_id: "reject_post"
    }
    ]
    }
    ]
    });

    When the "Approve" button is clicked, Slack sends an interaction payload to a separate endpoint where you make the actual ViralGhost API call. This gives your team a lightweight content review process without leaving Slack.

    Bonus: AI-Generated Captions from Slack

    Add a /draft command that generates a caption using ViralGhost AI:

    javascript
    // Handle /draft command
    if (command === "/draft") {
    const generated = await fetch("https://api.viralghost.xyz/v1/ai/generate", {
    method: "POST",
    headers: {
    "Authorization": Bearer ${env.VIRALGHOST_API_KEY},
    "Content-Type": "application/json"
    },
    body: JSON.stringify({
    type: "caption",
    prompt: text,
    tone: "casual",
    platform: "instagram"
    })
    });

    const result = await generated.json();

    return jsonResponse({
    response_type: "ephemeral",
    text: 📝 AI-generated draft:\n\n> ${result.content}\n\nTo post this, copy and use: \/post ${result.content}\`
    });
    }

    Now your team can type /draft product launch announcement to get AI-generated copy, review it, and then /post` the final version.

    Free Tier Limits

    The [ViralGhost free tier](/docs/agent-api) is generous enough for most small teams:

  • 2 social accounts

  • 30 posts/month

  • 15 AI generations/month
  • No credit card required. For teams posting more frequently, Pro ($49/mo) gives you 10 accounts and 500 posts/month.

    Wrapping Up

    You just connected Slack to Instagram in about 15 minutes. The same Cloudflare Worker pattern works for any webhook-based tool — Discord bots, GitHub Actions, Zapier webhooks, or your own internal tools.

    The key insight: ViralGhost abstracts away all the platform-specific complexity. Your Worker doesn't need to know anything about Instagram's API, OAuth tokens, or content publishing requirements. It just sends a POST request with text and account IDs.

    [Try it free at viralghost.xyz](https://viralghost.xyz) — link your Instagram, grab an API key, and build your Slack integration today. Full API docs at [/docs/agent-api](/docs/agent-api).


    Free tier available — try it at [viralghost.xyz/docs/agent-api](/docs/agent-api). No credit card, no sales call.

    Topics covered:

    Slack to Instagramsocial media Slack botautomate social posts from SlackViralGhost APISlack slash command social mediaInstagram Slack integration

    Share this article:

    Deploy Your AI Social Agent

    Stop spending hours on social media. Train your AI agent on your voice and maintain consistent presence in just 30 minutes daily.

    Start Free Trial