Posts

Create and schedule a social media post

Create a post to be published across one or more social media platforms. Supports text, images, and video (1 video per post). The image count per post is platform-specific: Twitter/X, Bluesky, and Mastodon allow up to 4; Instagram, LinkedIn, Facebook, and Telegram allow up to 10; Threads allows up to 20. There is no separate "carousel" field — a carousel is formed implicitly by attaching multiple images to one postGroupId (Instagram: 2-10 images = carousel, 1 image = single photo), in upload order. See the Media Uploads guide for the full matrix. If scheduledTime is provided, the post will be published at that time. If omitted, the post is saved as a draft.

post/create-post

Headers

x-publora-user-idstring

Managed user ID (workspace only)

Idempotency-Keystring

Opt-in idempotency. Omit the header for the previous behaviour — every call creates a new post.

Send a unique value (a UUID is a good choice) per logical operation to make retries safe:

  • Same key + identical body, original request finished → the original status code and response body are replayed. No second post.
  • Same key + identical body, original request still in flight → 409 (IDEMPOTENCY_IN_FLIGHT). Retry shortly.
  • Same key + different body → 422 (IDEMPOTENCY_KEY_CONFLICT). Nothing is created.

Keys are scoped to the acting user (the managed user when x-publora-user-id is set), so they never collide across accounts. Records expire 24 hours after they are created; reusing a key after that window is treated as a brand-new request.

Request body

contentstring

Normally required and non-empty. May be omitted or empty only when a targeted LinkedIn connection has repost intent via platformSettings.linkedin.repostEnabled=true or a non-empty repostParentUrn; repost validation still requires a valid parent/setting combination.

platformsstring[] required

Array of platform connection IDs (format: platform-platformId). Each ID must appear at most once; a repeated ID is rejected with 400 "Platforms must not contain duplicates".

scheduledTimestring date-time

ISO 8601 UTC datetime for scheduling. Omit for draft.

If the time is in the past, it is clamped to the current server time and a SCHEDULED_TIME_COERCED warning is returned — the post is still created. Read the scheduledTime in the response for the time actually stored.

Times less than 5 minutes in the past are always tolerated this way. Strict rejection for a time 5 or more minutes in the past is scheduled to begin on 2026-08-25, unless production configuration overrides that date. Before strict mode it is clamped with a warning. Send a future time to avoid both.

mediaUrlsstring[]

Public https URLs downloaded server-side. Images: 25 MB each; videos: 150 MB each; aggregate: 300 MB. Ingestion is all-or-nothing.

Example request

{
  "content": "Excited to share our new product launch! 🚀 #launch",
  "platforms": [
    "twitter-123456789",
    "linkedin-ABC123"
  ],
  "scheduledTime": "2027-03-01T14:00:00.000Z",
  "platformSettings": {
    "instagram": {
      "coverUrl": "https://cdn.example.com/covers/reel-cover.jpg",
      "cover_url": "https://cdn.example.com/covers/reel-cover.jpg"
    },
    "youtube": {
      "playlist": {
        "id": "PLxxxxxxxx",
        "platformId": "youtube-UCxxxxxxxx"
      },
      "thumbnail": {
        "mediaId": "665f...",
        "url": "https://media.publora.com/..."
      }
    },
    "twitter": {
      "replyTo": "https://x.com/customer/status/1234567890123456789",
      "quoteTweet": "987654321098765432"
    }
  }
}

Response

Post created

successboolean
postGroupIdstring
scheduledTimestring date-time nullable

The effective scheduled time that was actually stored. Always present. null when the post was saved as a draft (no scheduledTime sent). This may differ from the value you sent — if so, a SCHEDULED_TIME_COERCED warning explains why. Trust this field over your requested value.

Example response

{
  "success": true,
  "postGroupId": "507f1f77bcf86cd799439011",
  "scheduledTime": "2026-03-01T14:00:00.000Z",
  "warnings": [
    {
      "code": "SCHEDULED_TIME_COERCED",
      "message": "Requested scheduled time 2026-03-01T14:00:00.000Z was in the past and was changed to server time 2026-03-01T14:02:11.412Z.",
      "requested": "2026-03-01T14:00:00.000Z",
      "effective": "2026-03-01T14:02:11.412Z",
      "mediaFileId": "507f1f77bcf86cd799439012",
      "mediaStatus": "uploading",
      "pendingCode": "MEDIA_VALIDATION_PENDING",
      "attempts": 3
    }
  ]
}

Changes

No recorded changes to this endpoint across all 1 revision of this API.