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

ScopeAllows
service.social.viewList connected accounts, posts and per-account delivery targets.
service.social.manageCreate, edit and delete posts and their targets.
service.social.publishPublish 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

EventWhen
social.account_connectedAn account is connected.
social.account_disconnectedAn account is disconnected.
social.post_scheduledA post is scheduled for a future time.
social.post_publishedA post is published to one of its target accounts.
social.post_failedA target is dead-lettered after exhausting retries.

See Webhooks for payload + signature format.