Social Publishing API
Publish a post once and fan it out to a tenant's connected LinkedIn, Facebook Page and Instagram Business accounts, immediately or scheduled. Server endpoints live under /api/v1/services/social and are tenant-scoped (sk_test_* routes to your paired sandbox, where publishing is simulated). Responses are wrapped in { "data": … }; lists add a meta block. Each request is server-to-server - browser origins are rejected.
Scopes
| Scope | Allows |
|---|---|
service.social.view | List connected accounts, posts and per-account delivery targets. |
service.social.manage | Create, edit and delete posts and their targets. |
service.social.publish | Publish a post now or schedule it for later. |
Connecting and disconnecting accounts happens through OAuth in the dashboard (service.social.connect) and is not part of the server API.
List connected accounts
GET /api/v1/services/social/accounts · scope service.social.view
Returns the active accounts you can target, with their platform, display name and connection status.
curl https://app.softsolz.uk/api/v1/services/social/accounts \
-H "Authorization: Bearer sk_live_…"
{ "data": { "accounts": [
{ "id": 12, "platform": "linkedin", "display_name": "Acme Co", "status": "active" },
{ "id": 13, "platform": "instagram", "display_name": "@acme", "status": "active" } ] } }
Create a post
POST /api/v1/services/social/posts · scope service.social.manage
Provide the body text, optional media, and one target per account you want to publish to. A post starts as a draft; publishing is a separate call. Send an Idempotency-Key header to make retries safe.
curl -X POST https://app.softsolz.uk/api/v1/services/social/posts \
-H "Authorization: Bearer sk_live_…" -H "Content-Type: application/json" \
-d '{ "body": "We just shipped a big update.",
"media": [ { "url": "https://cdn.example/launch.png", "alt": "Launch" } ],
"targets": [ { "account_id": 12 }, { "account_id": 13 } ] }'
Each target may carry an override_body to customise the text for that platform. One post can only have one target per account.
Publish or schedule
POST /api/v1/services/social/posts/{id}/publish · scope service.social.publish
Omit scheduled_for to publish immediately; pass an RFC 3339 timestamp to schedule. Scheduled posts publish automatically when due.
# publish now
curl -X POST https://app.softsolz.uk/api/v1/services/social/posts/{id}/publish \
-H "Authorization: Bearer sk_live_…" -H "Content-Type: application/json" -d '{}'
# schedule
curl -X POST https://app.softsolz.uk/api/v1/services/social/posts/{id}/publish \
-H "Authorization: Bearer sk_live_…" -H "Content-Type: application/json" \
-d '{ "scheduled_for": "2026-06-10T09:00:00Z" }'
Delivery is per account, with automatic retries and exponential backoff. A target that keeps failing is dead-lettered after five attempts; the tenant is notified. Publishing a target fires social.post_published; a dead-lettered target fires social.post_failed.
Per-account delivery
GET /api/v1/services/social/posts/{id}/targets · scope service.social.view
Returns one row per account with its status (pending, publishing, published, failed, dead), the external post id once published, and the last error if any.
Edit & delete
GET /api/v1/services/social/posts?status=&search=&limit=&offset=
GET /api/v1/services/social/posts/{id}
PATCH /api/v1/services/social/posts/{id} (draft / scheduled only)
DELETE /api/v1/services/social/posts/{id}
PATCH supports optimistic concurrency via expected_updated_at (stale write returns 409). A post that is already publishing or sent can no longer be edited. DELETE returns 204 and does not retract posts already live on a platform.
Connecting accounts
Account connections are made by a signed-in dashboard user via OAuth (the same way you connect Google or Microsoft sign-in): Social Publishing → Accounts → Connect. SoftSolz stores the access and refresh tokens encrypted at rest and refreshes them automatically; your integration never handles the tokens. There is no server API to create a connection.
Webhook events
| Event | When |
|---|---|
social.account_connected | An account is connected. |
social.account_disconnected | An account is disconnected. |
social.post_scheduled | A post is scheduled for a future time. |
social.post_published | A post is published to one of its target accounts. |
social.post_failed | A target is dead-lettered after exhausting retries. |
See Webhooks for payload + signature format.