Connect API — Build Your Own Connect Page
The Connect API lets you build the "connect your social accounts" experience inside your own product, on your own domain, with your own design — instead of sending end users to the hosted access_url page described in the White-label Integration Guide.
Your page requests a platform authorize URL from Upload-Post, redirects the end user to the social network's consent screen, and Upload-Post handles the OAuth exchange and token storage. After the connection completes, the user is sent back to the URL you choose.
When to use which:
Hosted page (access_url) | Connect API (this page) | |
|---|---|---|
| Setup effort | None — one API call | You build the UI |
| Branding | Logo, title, colors, language | 100% yours — it's your page |
| Domain the user sees | app.upload-post.com | Yours (except the OAuth hops) |
Note: During the OAuth flow the user always visits the social network's own consent screen, and the network redirects briefly through
app.upload-post.com(the OAuth callback registered with each platform) before returning to yourredirect_url. This hop lasts milliseconds and is required by the platforms' OAuth policies — it applies to every provider in the industry.
How it works
Your backend Your connect page Upload-Post Social network
│ │ │ │
│ 1. generate-jwt (API key) │ │ │
│───────────────────────────>│ │ │
│ profile JWT │ │ │
│ │ 2. POST /oauth/<platform>/start (profile JWT) │
│ │───────────────────────────>│ │
│ │ authorize_url + state │ │
│ │ 3. redirect user ──────────────────────────────────>│
│ │ │ 4. user authorizes │
│ │ │<── code + state ───────│
│ │ │ 5. token exchange, │
│ │ │ account stored │
│ │<── 6. redirect to your redirect_url ────────────────│
- Your backend calls
generate-jwtwith your API key to obtain a profile token for the end user's profile. - Your connect page calls the start endpoint below with that profile token and receives the
authorize_url. - You redirect the user to
authorize_url(the social network's consent screen). - After consent, the network redirects to Upload-Post's registered callback, which completes the exchange — authenticated by the single-use
state, so the user's browser needs no session with Upload-Post. - The user lands back on your
redirect_urlwith?connect_status=success&platform=<platform>.
Start endpoint
POST /api/uploadposts/oauth/{platform}/start
Supported platforms: tiktok, instagram, facebook, linkedin, youtube, x (alias: twitter), threads, reddit, pinterest, google-business, snapchat
Authentication
Any of:
- Profile JWT (recommended for browser calls):
Authorization: Bearer PROFILE_JWT— the token fromgenerate-jwt. The profile is taken from the token. - API key (server-to-server):
Authorization: Apikey YOUR_API_KEY— pass the profile in the body.
Never expose your API key in a browser; use the profile JWT there.
Request body
| Field | Type | Required | Description |
|---|---|---|---|
profile | string | Only with API key auth | Profile username the connected account will be linked to. Ignored when a profile JWT is used. |
redirect_url | string | No | Absolute http(s) URL (max 2000 chars) to send the end user back to after a successful connection. ?connect_status=success&platform=<platform> is appended. |
Response
{
"success": true,
"platform": "instagram",
"authorize_url": "https://www.instagram.com/oauth/authorize?client_id=...&state=...",
"state": "e34zUEyZjWy6EFeI7IzGSEiUfY8O4K4_",
"expires_in": 900
}
Redirect the end user to authorize_url within 15 minutes (expires_in) — the underlying state is single-use and expires after that window. Mint a fresh one per connection attempt.
Example — browser (profile JWT)
const resp = await fetch(
'https://api.upload-post.com/api/uploadposts/oauth/instagram/start',
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${profileJwt}`,
},
body: JSON.stringify({
redirect_url: 'https://yourapp.com/social/connected',
}),
}
);
const { authorize_url } = await resp.json();
window.location.href = authorize_url;
Example — server (API key)
curl -X POST "https://api.upload-post.com/api/uploadposts/oauth/tiktok/start" \
-H "Authorization: Apikey YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"profile": "end-user-123",
"redirect_url": "https://yourapp.com/social/connected"
}'
Errors
| Status | Meaning |
|---|---|
400 | Missing profile (API key auth) or invalid redirect_url |
401 | Missing or invalid authentication |
404 | Unknown platform, or profile not found for this account (error_code: PROFILE_NOT_FOUND) |
500 | Platform client not configured on the server |
Handling the return
On success the end user arrives at your redirect_url:
https://yourapp.com/social/connected?connect_status=success&platform=instagram
To confirm the connection server-side (recommended), call GET /api/uploadposts/users and check the profile's social_accounts.
If the user cancels on the consent screen or the connection fails, they remain on the Upload-Post error screen with a human-readable message; they can simply retry from your page with a freshly minted start.
Security model
- The
statein the authorize URL is a 192-bit random value stored server-side, single-use and valid for 15 minutes. It is what authenticates the OAuth callback, so the end user's browser never needs an Upload-Post session. - The
stateis bound to the platform, profile and account that minted it — it cannot be replayed, reused across platforms, or combined with another account's session. - OAuth secrets (PKCE verifiers for TikTok and X) never leave the server; only the S256 challenge appears in the authorize URL.
redirect_urlis validated to be an absolutehttp(s)URL and is only used after a successful connection.
Notes per platform
youtube: profiles configured with custom YouTube credentials authorize against their own Google client automatically.x/twitter: both names are accepted; responses always reportplatform: "x".snapchat: connection is limited to basic profile scopes (posting requires Snapchat Public Profile API approval).instagram: the end user's Instagram must be a Business or Creator account, same as the hosted flow.