Skip to content

Social Posts API ​

Manage social media posts, drafts, and scheduling.

Endpoints ​

List Posts ​

GET /api/social-posts

Returns a list of all posts for the authenticated company.

Response:

json
[
  {
    "id": "uuid",
    "content": "Hello world",
    "status": "DRAFT",
    "platforms": ["twitter"],
    "createdAt": "2026-01-01T00:00:00Z"
  }
]

Create Draft ​

POST /api/social-posts

Creates a new post in DRAFT status.

Body:

json
{
  "content": "Draft content",
  "platforms": ["linkedin"],
  "status": "DRAFT",
  "mediaAssetIds": []
}

Publish / Schedule ​

POST /api/social-posts/batch-schedule

Publishes immediately or schedules for a future date.

Body:

json
{
  "content": "Going live!",
  "platforms": ["twitter", "linkedin"],
  "scheduledAt": "2026-02-20T10:00:00Z", // Optional, defaults to now
  "mediaAssetIds": ["uuid-1"]
}

Create Scheduled Occurrence ​

POST /api/social-posts/scheduled

The canonical scheduling endpoint (Story 6.18). Creates one multi-platform occurrence referencing a source draft (sourcePostId, or a new draft created from content + platforms when omitted), and enqueues its publish via the durable scheduling outbox.

Exactly one of scheduledAt (shared time for every platform) or platformSchedules (Story 6.24: one time per platform) must be provided.

Body — shared time:

json
{
  "sourcePostId": "uuid",
  "platforms": ["instagram", "linkedin"],
  "scheduledAt": "2026-02-20T10:00:00Z",
  "campaignId": null,
  "mediaAssetIds": []
}

Body — per-platform split (Story 6.24): platformSchedules must have exactly one entry per platform in platforms (no missing, extra, or duplicate platforms). The route resolves every platform's social account before writing anything and creates one occurrence per platform — sharing a single generated splitGroupId — in one transaction (all-or-nothing).

json
{
  "sourcePostId": "uuid",
  "platforms": ["linkedin", "tiktok"],
  "platformSchedules": [
    { "platform": "linkedin", "scheduledAt": "2026-02-20T09:00:00Z" },
    { "platform": "tiktok", "scheduledAt": "2026-02-20T19:00:00Z" }
  ]
}

Response: occurrenceId and top-level scheduledAt are always set from the earliest member, for backward compatibility with non-split callers. occurrences and splitGroupId are present only for a split create.

json
{
  "success": true,
  "occurrenceId": "uuid-of-earliest-member",
  "scheduledAt": "2026-02-20T09:00:00Z",
  "results": [
    { "platform": "linkedin", "success": true },
    { "platform": "tiktok", "success": true }
  ],
  "occurrences": [
    { "occurrenceId": "uuid-1", "platform": "linkedin", "scheduledAt": "2026-02-20T09:00:00Z" },
    { "occurrenceId": "uuid-2", "platform": "tiktok", "scheduledAt": "2026-02-20T19:00:00Z" }
  ],
  "splitGroupId": "uuid-or-null"
}

List Scheduled Occurrences ​

GET /api/social-posts/scheduled

Returns lifecycle-filtered occurrences for the calendar (excludes cancelled by default; pass includeCancelled=true to override). Each occurrence includes splitGroupId — null unless it's a member of a Story 6.24 split group, in which case all members of the group share the same value.

Recommend Posting Schedule ​

POST /api/social-posts/scheduling/recommend

Resolves one recommended slot per requested target for Auto-place, honoring company posting-schedule overrides, max-posting limits, and blackout windows.

Body:

json
{
  "targets": [{ "key": "post-1", "platforms": ["linkedin", "tiktok"] }],
  "after": "2026-02-20T00:00:00Z",
  "strategy": "shared"
}

strategy is "shared" (default — one time for the whole target, byte-identical to pre-6.24 behavior) or "split" (Story 6.24 — each platform gets its own peak-window slot).

Response — strategy: "split" on a multi-platform target: the top-level time/source mirror the earliest platform slot; reason is a split summary. Each platformSlots entry carries its own timezoneOffset (a split can straddle a DST transition, so the top-level offset is not trusted for members). When every platform's time resolves to the identical instant, the response collapses to a shared slot (no platformSlots) with a sharedWindowNote explaining the coincidence.

json
{
  "slots": [
    {
      "key": "post-1",
      "time": "2026-02-20T09:00:00Z",
      "reason": "Split across 2 platforms at their own peak windows",
      "source": "default",
      "timezone": "America/New_York",
      "timezoneOffset": "-05:00",
      "platformSlots": [
        { "platform": "linkedin", "time": "2026-02-20T09:00:00Z", "reason": "linkedin peak posting window", "source": "default", "timezoneOffset": "-05:00" },
        { "platform": "tiktok", "time": "2026-02-20T19:00:00Z", "reason": "tiktok peak posting window", "source": "default", "timezoneOffset": "-05:00" }
      ]
    }
  ]
}

Single-platform targets always return a normal shared slot regardless of strategy.

Update Post ​

PUT /api/social-posts/:id

Updates an existing draft or scheduled post.

Body:

json
{
  "content": "Updated content",
  "platforms": ["twitter"],
  "status": "DRAFT"
}

Delete Post ​

DELETE /api/social-posts/:id

Removes a post record.

TendSocial Documentation