How to add social media publishing to your SaaS product
For product teams that want 'share to social' or automated publishing inside their app. Covers API design, account connection UX, and why embedding a unified API beats building integrations yourself.
The feature request that derails roadmaps
At some point, someone on your team says: “We should let users share directly to social media from our app.”
It sounds small. A “Share to Instagram” button. A “Post to LinkedIn” option in the export flow. Maybe automated publishing when a user hits a milestone or finishes a design.
Then you look into what it actually takes. Eight platforms, each with its own OAuth implementation, its own upload protocol, its own app review process, its own format constraints. Instagram requires screencasts submitted to Meta for every permission scope. TikTok’s app audit takes multiple rounds. YouTube requires a Google Cloud project with scope verification. LinkedIn needs separate product approval before you can access posting scopes.
You are not building a “share” button. You are building a social media publishing platform inside your product. And maintaining it indefinitely.
Most teams either abandon the idea, build a half-working integration with one platform, or spend months on infrastructure that has nothing to do with their core product.
There is another option.
What embedding publishing actually requires
Before reaching for a solution, it helps to understand the full scope. A “publish to social media” feature in a SaaS product requires:
Account connection. Your users need to connect their social media accounts to your app. That means OAuth flows — different ones for each platform. Meta uses token exchange with no refresh tokens. X requires PKCE. LinkedIn gives most developers 60-day tokens with no programmatic refresh. Each flow needs its own implementation, its own error handling, its own token storage.
App review and verification. Every platform requires your app to pass a review process before it can publish on behalf of users in production. Meta requires separate screencast submissions for each permission scope. TikTok requires UX compliance audits. YouTube requires scope verification. These reviews take weeks and often require multiple rounds.
Publishing logic. Each platform accepts content differently. Instagram uses a container model — you create a container, wait for processing, then publish it in a second call. LinkedIn requires uploading media separately with ETag tracking before creating the post. X uses chunked uploads. Facebook uses a resumable upload API. You cannot call seven similar endpoints. You are implementing seven distinct publishing pipelines.
Token lifecycle management. Tokens expire. Some expire in hours, some in days, some in months. Your system needs to detect expiration, handle renewal where possible, and prompt users to reconnect when renewal is not available. Silently expired tokens mean silently failed publishes.
Multi-tenant isolation. Your users’ connected accounts need to be isolated from each other. User A should not be able to publish to User B’s Instagram. This requires scoped access control at every level.
This is months of engineering work. And it is not your product — it is plumbing.
How Postproxy handles this
Postproxy is publishing infrastructure. It has already passed app review on every platform, already implements every OAuth flow, already handles every upload protocol, and already manages token lifecycles. Your product calls one API.
The integration has three parts: organizing users, connecting accounts, and publishing content.
Part 1: Organizing users with profile groups
Profile groups are how Postproxy handles multi-tenancy. A profile group is a container that holds a set of connected social media profiles. You create one per customer, per project, per brand — whatever maps to your product’s model.
Create a profile group when a new customer signs up or enables social publishing:
curl -X POST "https://api.postproxy.dev/api/profile_groups" \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "profile_group": { "name": "Acme Corp" } }'Response:
{ "id": "grp_abc123", "name": "Acme Corp", "profiles_count": 0}You can also create scoped API keys that only have access to a specific profile group. A scoped key can list profiles, create posts, and manage content — but only within its group. It cannot see or affect other groups. This is how you give each customer isolated access without exposing your full account.
Part 2: Connecting social accounts
This is where most teams get stuck when building it themselves. Postproxy’s Initialize Connection endpoint handles the OAuth flow for every platform.
When your user wants to connect their Instagram account, your backend calls:
curl -X POST "https://api.postproxy.dev/api/profile_groups/grp_abc123/initialize_connection" \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "platform": "instagram", "redirect_url": "https://yourapp.com/settings/social/callback" }'Response:
{ "url": "https://app.postproxy.dev/...", "success": true}Your app redirects the user to the returned URL. The user authenticates with Instagram (or whichever platform). After authentication, they are redirected back to your redirect_url. A new profile is automatically created in the profile group.
That is the entire OAuth integration. No client secrets to manage per platform. No token exchange logic. No screencast submissions to Meta. No scope verification with Google. Postproxy’s app is already approved on every platform. Your user connects their account through Postproxy’s OAuth flow, and the profile appears in their group.
The same endpoint works for all eight platforms — just change the platform parameter:
| Platform value | Account type |
|---|---|
twitter | X (Twitter) account |
instagram | Instagram Business/Creator account |
facebook | Facebook Page |
linkedin | LinkedIn profile |
youtube | YouTube channel |
tiktok | TikTok account |
threads | Threads account |
pinterest | Pinterest account |
Part 3: Publishing content
Once a user has connected accounts, publishing from your app is a single API call:
curl -X POST "https://api.postproxy.dev/api/posts" \ -H "Authorization: Bearer YOUR_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "post": { "body": "Just finished our new brand guide — here is a preview." }, "profiles": ["instagram", "linkedin", "threads"], "media": ["https://yourapp.com/exports/brand-guide-preview.png"], "profile_group_id": "grp_abc123" }'The profile_group_id scopes the request to that customer’s connected accounts. If you are using a scoped API key, the group is inferred automatically.
Postproxy handles the container creation on Instagram and Threads, the image upload to LinkedIn, the format validation, the processing waits, and the publish calls. Your app makes one HTTP request.
What the UX looks like in your product
The integration maps to three screens in your product’s UI.
Account connection screen. Show a list of platforms with “Connect” buttons. When the user clicks one, your backend calls Initialize Connection and redirects the user to the returned URL. After they authenticate, they land back on your callback URL. Call GET /api/profiles to show their connected accounts.
Publishing screen. When the user is ready to share — from your editor, your dashboard, your export flow — show their connected profiles and let them pick where to publish. Your backend calls POST /api/posts with the selected profiles and content.
Status screen. After publishing, show per-platform results. Call GET /api/posts/:id to get the status of each platform — published, failed, or processing. Let the user see exactly what happened.
Handling connected account status
Connected accounts can expire. The profile object includes a status field (active, expired, or inactive) and an expires_at timestamp when applicable. Your UI should check these and prompt users to reconnect when a token expires — by calling Initialize Connection again with the same platform.
curl "https://api.postproxy.dev/api/profiles" \ -H "Authorization: Bearer YOUR_API_KEY"Each profile in the response tells you its status:
{ "id": "prof_xyz", "name": "acme_instagram", "platform": "instagram", "status": "active", "expires_at": null}If status is expired, show a “Reconnect” prompt in your UI and trigger a new Initialize Connection flow.
Placements: Facebook Pages and LinkedIn organizations
Some platforms have sub-accounts. A LinkedIn user might have a personal profile and multiple company pages. A Facebook user might manage several Pages.
The Placements endpoint lets your user choose where to publish:
curl "https://api.postproxy.dev/api/profiles/prof_xyz/placements" \ -H "Authorization: Bearer YOUR_API_KEY"For LinkedIn, this returns the personal profile and any organizations. For Facebook, this returns connected Pages. Your UI shows these as a dropdown when the user selects that platform, and you pass the chosen placement ID in the platforms parameter when creating the post.
Drafts and scheduling
Not every publish action should happen immediately. Your product might want a review step or scheduled publishing.
Drafts. Create a post with draft: true. The content is saved but nothing is published until your app calls the publish endpoint:
curl -X POST "https://api.postproxy.dev/api/posts/POST_ID/publish" \ -H "Authorization: Bearer YOUR_API_KEY"This maps to a “save for review” or “queue for approval” pattern in your product. A team lead reviews drafts and publishes what looks right.
Scheduling. Include scheduled_at with an ISO 8601 timestamp to publish at a specific time:
{ "post": { "body": "Launching next week!", "scheduled_at": "2026-03-01T09:00:00Z" }, "profiles": ["twitter", "linkedin"]}Both features work through the same API. Your product controls the UX — Postproxy handles the execution.
Why this beats building it yourself
Building social media publishing integrations in-house means taking on every problem described in this guide — and maintaining it indefinitely.
App review is the longest lead time. Meta alone requires separate screencast submissions for each permission scope, with reviews taking 2–4 weeks. TikTok’s audit process requires specific UX elements in your app. YouTube requires Google Cloud scope verification. You are looking at weeks to months before you can publish a single post in production — and any change to your app’s flow may require re-review.
OAuth maintenance never ends. Platforms change their OAuth flows, deprecate scopes, update API versions. LinkedIn deprecated r_liteprofile in 2023. Instagram sunset the Basic Display API in 2024. Facebook Graph API versions deprecate every two years. Each change requires engineering work in your product.
Seven upload protocols are seven codebases. Instagram’s container model, LinkedIn’s ETag-tracked chunked uploads, X’s INIT/APPEND/FINALIZE, YouTube’s resumable PUT — these are not variations of the same pattern. They are distinct implementations that each need their own error handling, retry logic, and status polling.
Token management is operational work. Tokens expire at different intervals. Some platforms support refresh, some require re-authentication. A silently expired token means a user’s “Share to Instagram” button stops working with no explanation. Your team is now debugging OAuth token lifecycle issues instead of building your product.
Postproxy has already done all of this. The app reviews are passed. The OAuth flows are implemented. The upload protocols work. The tokens are managed. Your product calls one API and gets per-platform results.
What Postproxy handles
When you embed Postproxy in your product, here is what you do not have to build:
- Seven OAuth implementations with per-platform token lifecycle management
- App review submissions for Meta, TikTok, YouTube, and LinkedIn
- Seven different media upload protocols
- Automatic video transcoding to each platform’s required codec and format
- Per-platform format validation and content constraints
- Processing status polling across platforms
- Per-platform outcome reporting — what published, what failed, and why
- Token expiration detection and renewal
What you build:
- A “Connect” button that redirects to the URL from Initialize Connection
- A publishing screen that calls
POST /api/posts - A status view that shows per-platform results from
GET /api/posts/:id
Your product stays focused on what it does best. Postproxy handles the publishing infrastructure.
Connect your accounts and start embedding publishing in your product through the Postproxy API.