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.
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))
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 App → From scratch
2. Name it something like "ViralGhost Poster"
3. Select your workspace
Add the Slash Command
1. In the left sidebar, click Slash Commands → Create 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:
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:
Share this article: