Posts

Edit a draft or scheduled post

Changed on

Modify the content, target platforms, scheduled time, status, platformSettings, or mediaUrls of an existing draft or scheduled post. Only post groups whose status is draft or scheduled can be updated. pending and processing belong to separate processing/per-platform state, not to the post-group status enum. At least one of publishNow, status, scheduledTime, content, platforms, platformSettings, firstComment, firstComments, or mediaUrls must be provided. publishNow: true publishes a draft or scheduled post immediately at server time. Every field is a patch — omit it to leave the stored value unchanged. content replaces the base text and rewrites each platform post to its effective content, preserving explicit per-account overrides. platforms replaces the whole target set (it is not merged); added connections are validated for ownership and plan entitlement, and adding a target to a scheduled post re-runs scheduling quota and post validation. platformSettings are merged per-platform with existing settings — omitted fields are preserved. Updating a post group also updates all associated platform-specific posts. A content or platform edit is refused with POST_NOT_EDITABLE (400) or POST_PUBLISH_IN_PROGRESS / POST_GROUP_VERSION_CONFLICT (409) rather than partially applied.

put/update-post/{postGroupId}

Request

  • Base URL
    https://api.publora.com/api/v1
  • Auth
    API key in header x-publora-key

Path parameters

postGroupIdstring required

Headers

x-publora-user-idstring
Idempotency-Keystring

Opt-in idempotency. Omit the header for the previous behaviour — every call applies the update again.

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. The update is not re-applied.
  • 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 changed.

The key is bound to this postGroupId as well as the body, so the same key reused against a different post counts as a different request. 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

Replacement base post text. Every platform post without an explicit per-account override is rewritten to this text; overrides created in the web editor are preserved. Editing the text of a Twitter or Threads target clears its derived thread split so it is recomputed.

An empty string is accepted while the post stays a draft. Scheduling still enforces each platform's content and media rules.

platformsstring[]

Replacement target set. The array REPLACES the stored one — it is not merged, so send the complete final list. Use the exact connection IDs returned by GET /platform-connections; duplicates are rejected with INVALID_PLATFORMS and unknown/foreign IDs with INVALID_PLATFORM_CONNECTION.

IDs removed from the array have their platform posts deleted. An empty array is accepted only while the post remains a draft; scheduling an empty set returns PLATFORMS_REQUIRED.

status'draft' | 'scheduled'

New status

scheduledTimestring date-time

New ISO 8601 UTC scheduled time. Send a future time.

If the time is in the past, it is clamped to the current server time and a SCHEDULED_TIME_COERCED warning is returned — the update still succeeds. 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.

The same check applies to the post's existing scheduledTime when you move a post to status: "scheduled" without sending a new time — a long-stale draft can therefore be rejected.

publishNowboolean

true publishes this draft or scheduled post immediately at server time, with the same checks as create-post. A scheduled post moved to Publish Now frees its scheduled-queue slot and keeps its monthly reservation when the month and targets are unchanged. Combining it with a future scheduledTime or status: "draft" returns 400 PUBLISH_NOW_CONFLICT; a non-boolean value returns 400 INVALID_PUBLISH_NOW.

mediaUrlsstring[]

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

Example request

{
  "platforms": [
    "linkedin-ABC123",
    "twitter-XYZ789"
  ],
  "publishNow": true,
  "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"
    }
  },
  "firstComment": {
    "text": "Full write-up with the numbers: https://example.com/launch-week",
    "platforms": [
      "linkedin"
    ]
  }
}

Response

Post updated

successboolean
messagestring
scheduledTimestring date-time nullable

The effective scheduled time that was actually stored. Always present. null when the post has no scheduled time (e.g. it was moved back to draft). 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

{
  "message": "Post updated successfully",
  "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",
      "platforms": [
        "instagram"
      ]
    }
  ],
  "postGroup": {
    "content": "Corrected launch announcement.",
    "platforms": [
      "linkedin-ABC123",
      "twitter-XYZ789"
    ]
  }
}

Changes

    • info

      added the new optional request property //

    • info

      added the new optional request property //

    • info

      added the new optional request property //

    • info

      added the new optional request property //

    • info

      added the new optional request property //