Skip to main content

OAuth Delegation

OAuth delegation lets a third-party app connect a user’s social media account through PostSyncer and receive back a signed proof of ownership for that account. It works for any platform PostSyncer supports that uses OAuth (TikTok, Instagram, X/Twitter, LinkedIn, YouTube, Facebook, Pinterest, Threads). Instead of integrating each platform’s OAuth yourself, you redirect the user to PostSyncer, PostSyncer runs the platform OAuth, and then redirects the user back to you with the platform’s permanent user id, the handle, and an HMAC-SHA256 signature you can verify with your signing secret.
Delegation does not store a social account on your PostSyncer workspace. The platform_id and handle are read directly from the platform’s OAuth response and passed through to you - nothing else is persisted.
Your API key is a full-access secret and must never appear in the browser. You start delegation with a server-to-server call from your backend (the API key goes in the Authorization header), and PostSyncer returns an opaque, single-use authorize_url. Only that opaque URL is ever sent to the user’s browser.

Prerequisites

  1. A PostSyncer API key (create one).
  2. A signing secret for that API key. In Settings → API Keys, click the shield icon on the API key row and choose Generate signing secret. Copy it - it’s shown only once. Regenerating invalidates the previous secret.
Keep the signing secret private. It is the shared secret used to sign and verify ownership proofs. Anyone with it can forge proofs.

Flow overview

Step 1 - Create a delegation session (server-to-server)

From your backend, call:
with your API key in the Authorization header:
string
required
Bearer <your PostSyncer API key>. Sent server-to-server only - never exposed to the browser.
string
required
The platform to connect, e.g. tiktok, instagram, twitter, linkedin, youtube, facebook, pinterest, threads.
string
required
Absolute URL PostSyncer redirects the user back to after authentication.
string
required
A random, single-use string you generate. Used for CSRF protection; PostSyncer echoes it back unchanged.
The response contains an opaque, single-use authorize URL (valid for 15 minutes):
Node.js

Step 2 - Redirect the user to the authorize URL

Send the user’s browser to the authorize_url you received. It carries only an opaque request token - no API key, platform or callback URL.
Node.js
PostSyncer then runs the platform’s normal OAuth consent screen.

Step 3 - PostSyncer redirects back to your callback_url

On success, PostSyncer redirects the user to your callback_url with:
string
The platform that was connected.
string
The platform’s permanent unique identifier for the user (e.g. TikTok open_id, X user_id, Instagram user_id). This is the value you trust as ownership proof.
string
The username on that platform. For display only - handles can change.
string
The same value you sent in step 1.
integer
Unix timestamp, 5 minutes from when the proof was issued.
string
HMAC-SHA256 signature of the string platform={platform}&platform_id={platform_id}&handle={handle}&state={state}&expires={expires} using your signing secret.
Example callback
If something fails (or the user cancels), PostSyncer redirects to your callback_url with error, error_description, and state instead.

Step 4 - Verify the proof

On your server, before trusting platform_id:
  1. Verify state matches the value you generated and mark it as used (single use).
  2. Verify sig by recomputing the HMAC over the exact base string.
  3. Verify expires is in the future.
Only if all three pass should you treat platform_id as verified ownership.

Errors

Step 1 (session creation) returns a JSON error to your server with an HTTP 4xx status and a code:
error
No API key was provided in the Authorization header. (401)
error
The API key is invalid or expired. (401)
error
The API key has no signing secret. Generate one under Settings → API Keys. (422)
error
The platform value is not a supported OAuth platform. (422)
Step 3 (after the user authorizes) errors are redirected back to your callback_url with an error, error_description and state instead of a signed proof:
error
The user cancelled or declined the platform consent screen.
error
The platform OAuth did not complete.
error
The authorize URL was already used or expired before the user opened it.
The signature only covers successful proofs. Always treat any response that carries an error parameter as a failed connection.