Social Media Auto-Post Setup Guide
This guide walks you through setting up automated social media posting for Secure Roots blog articles. Once configured, any blog post pushed with publish_social: true in its front matter will automatically be shared to Twitter/X, LinkedIn, and Instagram.
Step 1: Twitter / X Developer Account
- Go to developer.x.com and sign in with the @secureroots_og account
- Click Sign up for Free Account (Free tier: 1,500 tweets/month — more than enough)
- Describe your use case: “Automated posting of cybersecurity blog articles from secureroots.io to our company Twitter account.”
- Once approved, go to Dashboard → Projects & Apps → Create App
- Name the app:
Secure Roots Blog Auto-Post - Under Keys and Tokens, generate:
- API Key (also called Consumer Key)
- API Key Secret (Consumer Secret)
- Access Token
- Access Token Secret
- Make sure Access Token permissions are set to Read and Write
You’ll need these 4 values for GitHub Secrets:
| Secret Name | Value |
|—|—|
| TWITTER_API_KEY | Your API Key |
| TWITTER_API_SECRET | Your API Key Secret |
| TWITTER_ACCESS_TOKEN | Your Access Token |
| TWITTER_ACCESS_TOKEN_SECRET | Your Access Token Secret |
Step 2: LinkedIn Developer App
- Go to linkedin.com/developers/apps and sign in with your LinkedIn account (must be admin of the Secure Roots company page)
- Click Create App
- Fill in:
- App name:
Secure Roots Blog Auto-Post - LinkedIn Page: Select “Secure Roots”
- App logo: Upload the Secure Roots logo
- Legal agreement: Check the box
- App name:
- After creation, go to the Auth tab
- Under OAuth 2.0 Scopes, request:
w_member_social(post on behalf of the authenticated member)r_organization_social(read company page)w_organization_social(post to company page)
- Go to the Products tab and request access to Share on LinkedIn and Marketing Developer Platform
- To generate an Access Token:
- Go to linkedin.com/developers/tools/oauth
- Select your app
- Check the scopes listed above
- Click Request access token
- Copy the generated token
Note: LinkedIn access tokens expire after 60 days. You’ll need to refresh periodically, or set up a token refresh mechanism. The setup guide will remind you when it’s time.
You’ll need this value for GitHub Secrets:
| Secret Name | Value |
|—|—|
| LINKEDIN_ACCESS_TOKEN | Your OAuth 2.0 Access Token |
Step 3: Instagram / Meta Developer App
Instagram posting requires a Meta (Facebook) developer account and an Instagram Professional (Business or Creator) account.
3a. Prepare Your Instagram Account
- Open Instagram → Settings → Account → Switch to Professional Account (if not already)
- Choose Business account type
- Connect to a Facebook Page (create one for Secure Roots if needed)
3b. Create Meta Developer App
- Go to developers.facebook.com and log in
- Click My Apps → Create App
- Select Business as the app type
- Name:
Secure Roots Blog Auto-Post - After creation, on the app dashboard, click Add Product → find Instagram Graph API → click Set Up
3c. Get Your Instagram Business Account ID
- In the Meta App Dashboard, go to Tools → Graph API Explorer
- Select your app from the dropdown
- Generate a User Token with these permissions:
instagram_basicinstagram_content_publishpages_read_engagement
- Run this query:
me/accounts— this returns your Facebook Pages - From the response, copy the Page ID
- Run:
{page_id}?fields=instagram_business_account— this returns your Instagram Business Account ID - Copy the
instagram_business_account.idvalue
3d. Generate a Long-Lived Token
- In Graph API Explorer, your token is short-lived (1 hour)
- Exchange it for a long-lived token (60 days) by running:
GET https://graph.facebook.com/v21.0/oauth/access_token? grant_type=fb_exchange_token& client_id={app_id}& client_secret={app_secret}& fb_exchange_token={short_lived_token} - Copy the long-lived access token from the response
You’ll need these values for GitHub Secrets:
| Secret Name | Value |
|—|—|
| INSTAGRAM_ACCESS_TOKEN | Your long-lived Page Access Token |
| INSTAGRAM_ACCOUNT_ID | Your Instagram Business Account ID |
Step 4: Store Credentials in GitHub Secrets
- Go to your repo: github.com/OG-MrB/secure-roots-website/settings/secrets/actions
- Click New repository secret for each of these:
| Secret Name | Platform |
|---|---|
TWITTER_API_KEY |
Twitter/X |
TWITTER_API_SECRET |
Twitter/X |
TWITTER_ACCESS_TOKEN |
Twitter/X |
TWITTER_ACCESS_TOKEN_SECRET |
Twitter/X |
LINKEDIN_ACCESS_TOKEN |
|
INSTAGRAM_ACCESS_TOKEN |
|
INSTAGRAM_ACCOUNT_ID |
Note: GITHUB_TOKEN is automatically available in GitHub Actions — you don’t need to create it.
Step 5: Test the Workflow
- Edit any blog post in
_posts/and addpublish_social: trueto the front matter - Push to
main - Go to Actions tab in GitHub — you should see the “Post to Social Media” workflow running
- Check each platform to verify the posts appeared
- Check
data/posted_urls.jsonin the repo — it should now contain the posted article’s tracking data
Maintenance
Token Refresh Schedule
- LinkedIn: Access token expires every 60 days. Set a calendar reminder to refresh.
- Instagram/Meta: Long-lived token expires every 60 days. Refresh using the Graph API.
- Twitter/X: Access tokens don’t expire unless revoked. No refresh needed.
Refreshing LinkedIn Token
- Go to linkedin.com/developers/tools/oauth
- Re-authorize and generate a new token
- Update the
LINKEDIN_ACCESS_TOKENsecret in GitHub
Refreshing Instagram Token
Run this API call before the token expires:
GET https://graph.facebook.com/v21.0/oauth/access_token?
grant_type=fb_exchange_token&
client_id={app_id}&
client_secret={app_secret}&
fb_exchange_token={current_long_lived_token}
Update the INSTAGRAM_ACCESS_TOKEN secret in GitHub with the new token.
Troubleshooting
| Issue | Solution |
|---|---|
| Workflow doesn’t trigger | Make sure the push modified a file in _posts/ |
| Twitter post fails | Verify API keys have Read+Write permissions |
| LinkedIn post fails | Check if access token expired (60-day limit) |
| Instagram post fails | Verify account is Professional/Business type and token has instagram_content_publish scope |
| Duplicate posts | Check data/posted_urls.json — the URL may already be tracked |
| Image upload fails | Ensure the image file exists at the path specified in the post’s image: field |