Guides
Connect social accounts
Send your user through the platform's consent screen and connect the Pages and accounts they pick, all from your own app.
How it works
Connecting is an embedded OAuth flow. Your user never leaves your product except for the platform's own consent screen.
1. Register your redirect URI
After consent, the user is sent back to a URL in your app. It must be on an allow-list first:
# partner key: one URI for all your orgs
curl -X POST https://connect.social.wasparks.com/v1/partner/redirect-uris \
-H "Authorization: Bearer $WASPARKS_KEY" \
-H "Content-Type: application/json" \
-d '{"uri": "https://app.example.com/social/connected"}'
await fetch("https://connect.social.wasparks.com/v1/partner/redirect-uris", {
method: "POST",
headers: { Authorization: `Bearer ${process.env.WASPARKS_KEY}`, "Content-Type": "application/json" },
body: JSON.stringify({ uri: "https://app.example.com/social/connected" }),
});
import os, requests
requests.post(
"https://connect.social.wasparks.com/v1/partner/redirect-uris",
headers={"Authorization": f"Bearer {os.environ['WASPARKS_KEY']}"},
json={"uri": "https://app.example.com/social/connected"},
)The rules:
- The URI must be
https. - Matching is exact. A different path, query string or port is a different URI.
- A partner registers URIs once for all its orgs with
POST /v1/partner/redirect-uris. An org can add its own withPOST /v1/connect/redirect-uris. - Adding a URI that is already listed returns it.
2. Create a session
curl -X POST https://connect.social.wasparks.com/v1/connect/sessions \
-H "Authorization: Bearer $WASPARKS_KEY" \
-H "X-Tenant-Id: acme-001" \
-H "Content-Type: application/json" \
-d '{
"platform": "meta",
"redirect_uri": "https://app.example.com/social/connected",
"features": ["publishing", "comments", "messaging"]
}'
const res = await fetch("https://connect.social.wasparks.com/v1/connect/sessions", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.WASPARKS_KEY}`,
"X-Tenant-Id": "acme-001",
"Content-Type": "application/json",
},
body: JSON.stringify({
platform: "meta",
redirect_uri: "https://app.example.com/social/connected",
features: ["publishing", "comments", "messaging"],
}),
});
const session = await res.json(); // { id, url, expires_at }
import os, requests
r = requests.post(
"https://connect.social.wasparks.com/v1/connect/sessions",
headers={"Authorization": f"Bearer {os.environ['WASPARKS_KEY']}", "X-Tenant-Id": "acme-001"},
json={
"platform": "meta",
"redirect_uri": "https://app.example.com/social/connected",
"features": ["publishing", "comments", "messaging"],
},
)
session = r.json() # {"id": ..., "url": ..., "expires_at": ...}platformismetafor Facebook Pages and the Instagram professional accounts linked to them. LinkedIn is not open for connections yet; this page and the changelog will say when it is.featuresdecides which permissions are requested:publishing,comments,messaging. Ask only for what you use. The default is all three.workspace_idis optional. Accounts go to the org's default workspace without it.- The response has
id,urlandexpires_at. A session expires 15 minutes after it is created.
3. Send the user to the consent URL
Redirect the user's browser to url. They sign in to the platform and choose which Pages or accounts to share. The platform then sends them to WaSparks, and we send them on to your redirect_uri with two query parameters added:
https://app.example.com/social/connected?session_id=0192a7c6-9100-7f22-8d33-e4f5a6b7c001&status=successstatus is success, error or cancelled. Use session_id to look the session up.
4. Read the session
GET /v1/connect/sessions/{sessionId}{
"id": "0192a7c6-9100-7f22-8d33-e4f5a6b7c001",
"platform": "meta",
"status": "READY",
"error": null,
"expires_at": "2026-11-02T09:15:00Z",
"available_accounts": [
{
"platform": "FACEBOOK_PAGE",
"external_id": "104857600123456",
"display_name": "Acme Coffee",
"username": "acmecoffee",
"avatar_url": "https://cdn.example.com/avatar.jpg",
"capabilities": { "publish": true, "comments": true, "messaging": true, "...": "..." },
"already_connected": false
}
]
}The session status is one of:
| Status | Meaning |
|---|---|
PENDING |
The user has not come back yet. |
READY |
Pick from available_accounts. |
CANCELLED |
The user declined. |
ERROR |
Something failed. error says what. |
COMPLETED |
Accounts were connected; the session is spent. |
available_accounts lists what the user granted. capabilities shows what each account will be able to do, based on the permissions they accepted. Show the list to your user, or connect everything.
5. Connect the accounts
Send the external_id of each account to connect:
POST /v1/connect/sessions/{sessionId}/accounts
{ "external_ids": ["104857600123456"] }The response is the connected accounts. Each one counts against the plan's social_accounts limit; over it you get 402 quota_exceeded. We send an account.connected event for each. Use the account id in account_ids when you publish.
Reconnect
Platform credentials expire or are revoked. When that happens the account's status becomes NEEDS_REAUTH, you receive an account.needs_reauth event, and publishing or replying fails with 422 account_needs_reauth.
To fix it, run the same flow again: create a new session and connect the account. An account with a matching external_id is updated in place, keeping its id and history, and already_connected is true in the list. You also reconnect when a capability turns on that the account did not have when it was connected, for example after Instagram comments are released.
To check an account's credential at any time, call POST /v1/accounts/{accountId}/check.
Disconnect
DELETE /v1/accounts/{accountId} deletes our copy of the credentials and stops platform notifications for it. Its posts, comments and conversations are kept. You get an account.disconnected event. The same event arrives if the user removes the app on the platform.
Next: Publish posts