API Changelog and Deprecation Ledger

View as Markdown

This page records externally relevant REST and MCP contract changes. Dates are deployment or documentation publication dates, not announcement estimates.

Upcoming

2026-08-25 — scheduledTime strict-mode ramp

  • Affected surface: REST create-post and update-post, plus MCP tools that schedule through them.
  • Tag: Potentially breaking, configuration-dependent.
  • Change: A scheduledTime at least five minutes in the past is scheduled to return 400 SCHEDULED_TIME_IN_PAST starting on 2026-08-25. This calendar behavior applies only when production configuration does not explicitly override it with SCHEDULED_TIME_STRICT; an explicit flag wins in either direction.
  • Migration action: Always send a future ISO 8601 UTC time. During the warn-first period, inspect warnings[].code === "SCHEDULED_TIME_COERCED" and the returned scheduledTime to find callers that need correction.

2026-10-15

Post-log retention — publora.com #663

  • Affected surface: GET /api/v1/post-logs/:postGroupId.
  • Tag: Potentially breaking retention change.
  • Change: Responses include events from the last 90 days by event createdAt, oldest first, limited to 100 entries. Old diagnostic events expire asynchronously; post groups and published post history remain available.
  • Migration action: Handle HTTP 200 with success: true and logs: [] for an owned group without recent events. Events older than 90 days are no longer returned; store diagnostic events on your side if your integration requires a longer history. Ownership and 404 behavior are unchanged. See Post logs.

2026-10-02

Publish Now — publora.com #608, #610

  • Affected surface: personal REST create-post and update-post; MCP create_post and update_post; GET /account-context and MCP account_context queue counts.
  • Tag: Additive.
  • Change: publishNow: true publishes immediately at server time. It uses the monthly post allowance (POST_LIMIT_REACHED still applies) but takes no slot in the scheduled-post queue and ignores the schedule horizon. Combining it with a future scheduledTime or status: "draft" returns 400 PUBLISH_NOW_CONFLICT; a non-boolean value returns 400 INVALID_PUBLISH_NOW. Moving a scheduled post to Publish Now frees its queue slot and keeps its monthly reservation. A scheduledTime within a few seconds of server time is still treated as immediate for existing clients.
  • Docs: GET /account-context, released with publora.com #589 on 2026-10-01, now has its own REST reference page and OpenAPI entry.
  • Migration action: None. New integrations should send publishNow: true instead of a current scheduledTime. See Publish now.

2026-10-01

YouTube granted scopes — publora.com #490

  • Affected surface: REST POST /test-connection/{platformId}, YouTube only. Released in backend PR #590 on 2026-10-01.
  • Tag: Potentially breaking permission values, with an additive permissionsKnown field.
  • Change: permissions returns Google's recorded full scope URIs, such as https://www.googleapis.com/auth/youtube, instead of the fixed short youtube.upload and youtube.readonly list. A broad grant is not expanded into narrower grants. Connections without recorded scopes return permissions: [] with permissionsKnown: false.
  • Migration action: Handle permissionsKnown: false as unknown; recognize broad youtube access when checking capabilities. Do not require the old short scope labels or treat an unknown empty list as denied access. See YouTube connection diagnostics.

account_context MCP tool — publora.com #589

  • Affected surface: New MCP tool account_context, which brings the personal MCP toolset from 19 to 20 tools. It reads the new REST endpoint GET /api/v1/account-context with the same API key.
  • Tag: Additive.
  • Changes:
    • account_context is read-only. It returns the plan, available features, publishing quotas (monthly platform posts, scheduled posts, connected channels), the schedule horizon with maxScheduledDate, and allowedPlatforms for the personal account behind the key. Quotas count platform posts, limit: null means unlimited, and a per-connection monthly limit (scope: "connection") is broken down in quotas.monthlyPosts.connections[].
    • The optional scheduledTime (ISO 8601 with Z or an offset) selects the monthly quota period of a planned batch. A past time resolves to the current period, and effectiveQuotaTime shows the date used. A malformed value returns 400 INVALID_SCHEDULED_TIME from the REST endpoint; the MCP tool rejects it before calling the API.
    • The response is an advisory snapshot (advisory: true): it reserves nothing, and every write is still checked against the limits.
  • Migration action: None. Optionally, call account_context before a batch of posts and after a quota or feature error. See account_context.

2026-09-30

connectionStatus on connections — publora.com #526

  • Affected surface: REST GET /platform-connections; MCP list_connections.
  • Tag: Additive. The field has been returned since publora.com #526 was merged on 2026-09-25, and is documented from 2026-09-30.
  • Changes:
    • Each connection carries connectionStatus: active or reconnect_required, Publora's verdict on whether it can publish with the connection now. It is computed from stored data (reconnect flags, stored credentials, access-token expiry) without calling the platform. See connectionStatus and tokenStatus.
    • It differs from tokenStatus in two systematic cases. Telegram connections store no expiry date and are never flagged, so tokenStatus is always unknown while connectionStatus is active. An X or YouTube account reported suspended is reconnect_required while its tokenStatus stays valid.
    • Documentation corrections, no behavior change: displayName and profileImageUrl can be null, and subscriptionType (X only: None, Basic, Premium, PremiumPlus, re-checked when publishing to X at most once a day) is now in the OpenAPI schema.
  • Migration action: Prompt a reconnect on connectionStatus: "reconnect_required" instead of tokenStatus: "expired", and keep using tokenStatus: "expiring_soon" for advance warnings. Clients generated from the OpenAPI spec should treat displayName and profileImageUrl as nullable.

2026-09-29

Ordered first comments and Agency first comment — publora.com #537, #538, #552

  • Affected surface: personal REST create-post, update-post, get-post; MCP create_post, update_post, get_post; Agency Company post save and detail routes and MCP tools.
  • Tag: Additive.
  • Change: Personal posts accept firstComments[] with up to three ordered {text, platforms?, delaySeconds?} items. Delay is 0–86,400 seconds and delivery runs on scheduler ticks. A later comment waits for the previous one to post; failure stops the sequence. firstComments: null or [] clears it on update. Supplying both firstComment and firstComments returns 400 FIRST_COMMENT_INVALID.
  • Read contract: get-post / get_post return group firstComments[] and per-target firstCommentResults[]. Existing firstComment / firstCommentResult fields remain aliases for item 0; old documents read as a one-element array.
  • Agency: Company draft save accepts one firstComment; Company post detail returns its per-target result. Multiple comments and delays remain personal-only. See the Agency first comment guide.
  • Migration action: none for clients using the single-comment fields.

Telegram: private channels over a bot connection — publora.com #570

  • Affected surface: Telegram bot connections (dashboard and client connection pages); REST GET /platform-connections and POST /test-connection/{platformId}; MCP list_connections.
  • Tag: Additive; potentially breaking for clients that assume username is always a string. Behavioral change for test-connection: a Telegram bot connection whose token still works can now return error where it returned ok, and its message strings changed.
  • Changes:
    • The channel field accepts a public channel's @username or t.me/username, and for a private channel its -100… chat ID or a t.me/c/… link to any of its messages. Invite links (t.me/+…, t.me/joinchat/…) are rejected with guidance, because the Bot API cannot identify a channel from them. See Telegram.
    • A channel without a public username is saved with username: null, and displayName holds the channel title. GET /platform-connections, test-connection and list_connections return that null. The platform ID comes from the chat ID, as for any bot connection, for example telegram--1001234567890.
    • test-connection on a Telegram bot connection now also checks that the bot can read the channel and is an administrator allowed to post, not only that the token works. A passing check returns Bot can post to <channel title> instead of Connected as @<bot username>; the new failure messages include Channel is unavailable and Bot needs channel administrator posting rights, and Telegram API errors from the new channel checks (for example, a bot removed from a private channel) return Request failed with status code <code>. MTProto connections now fail with Invalid bot connection instead of Invalid bot token. See Test Connection.
  • Migration action: Treat username as nullable and label connections by displayName when it is null. Keep targeting posts by platformId. If you gate scheduling on test-connection, a Telegram connection that now returns error needs its bot's administrator posting rights restored in Telegram.

Media URL errors: hosts that resolve to private addresses report MEDIA_URL_BLOCKED_HOST — publora.com #565

  • Affected surface: REST create-post and update-post (mediaUrls) and POST /attach-media/{postGroupId}; MCP create_post, update_post and attach_media.
  • Tag: Fix.
  • Change: When a media URL's host, or a redirect target, resolves to a private or reserved address, mediaResults[].code is now MEDIA_URL_BLOCKED_HOST. It was reported as MEDIA_URL_FETCH_FAILED before. The download was refused then and still is; only the code and message change. A DNS lookup that fails is still reported as MEDIA_URL_FETCH_FAILED, whether the domain does not exist or the host has no A or AAAA records. See mediaUrls per-URL codes.
  • Migration action: Clients that treated MEDIA_URL_FETCH_FAILED as retryable for such hosts should not retry MEDIA_URL_BLOCKED_HOST; it needs a publicly reachable URL.

2026-09-28

ChatGPT file attachments — publora.com #539

  • Affected surface: New REST endpoint POST /attach-media/{postGroupId} and new MCP tool attach_media, which brings the personal MCP toolset from 18 to 19 tools. The existing mediaUrls and presigned-upload flows are unchanged.
  • Tag: Additive.
  • Changes:
    • attach_media declares _meta["openai/fileParams"]: ["file"], so ChatGPT passes a file from the conversation as { download_url, file_id, mime_type?, file_name? }. Images ChatGPT generates can be attached when the client exposes them as file references. The openai/fileParams declaration is included in authenticated tools/list, in unauthenticated discovery and in the server card.
    • The endpoint accepts only { file, fileName? }. It downloads the file right away through the mediaUrls pipeline (images up to 25 MB, videos up to 150 MB, type detected from the bytes, then probed) and attaches it as ready. It always leaves the post in draft, so a scheduled post is demoted (without a post.demoted webhook), and it never publishes. The optional fileName (1–255 characters, cleaned to a safe name) becomes media[].sourceFileName. Without it, the name comes from file.file_name, or is chatgpt-media when that is absent.
    • Idempotency-Key matches on the post ID, file_id and file name, not the expiring download_url, so a retry with a refreshed URL replays the first result instead of attaching the file twice. The MCP tool sends a stable key by default. A failed download frees the key.
    • New top-level 400 code INVALID_MEDIA_FILE for any other body field, a malformed file object, a download_url that fails the static URL checks, or an invalid file name.
  • Migration action: None for existing callers. ChatGPT users who already added the Publora connector should refresh its tool list so attach_media appears.

First comment for LinkedIn company Pages — publora.com #533, #534

  • Affected surface: REST create-post, update-post, get-post; MCP create_post, update_post, get_post — LinkedIn targets only.
  • Tag: Additive.
  • Change: A LinkedIn company Page connection now gets the first comment too, posted as the organization (urn:li:organization:<id>), the way its posts are published. Before this change, Page targets were recorded as skipped with skipReason: "unsupported_account_type"; that reason now only remains on results recorded before the change.
  • Migration action: none.

First comment (wave 1) — publora.com #529, #530

  • Affected surface: REST create-post, update-post and get-post; MCP create_post, update_post and get_post.
  • Tag: Additive.
  • Changes:
    • New optional top-level firstComment { text, platforms? } on create and update: a text comment posted under the account's own post right after it publishes. platforms are platform types (never connection IDs); omitted = every supported target. Wave 1 platforms: LinkedIn member profiles (company Pages followed in publora.com #534), X, Threads, Bluesky, Mastodon.
    • Length is validated per applicable platform (X 280 / 25,000 Premium-only, LinkedIn 1,250, Threads 500 code points, Bluesky 300 graphemes and 3,000 bytes, Mastodon 500 with links = 23) with an absolute cap of 25,000. New 400 codes FIRST_COMMENT_INVALID, FIRST_COMMENT_PLATFORM_UNKNOWN, FIRST_COMMENT_TOO_LONG; new warnings FIRST_COMMENT_PLATFORM_NOT_TARGETED, FIRST_COMMENT_UNSUPPORTED_PLATFORMS.
    • get-post / get_post return the stored firstComment (or null) and a per-target posts[].firstCommentResult (pending | posted | failed | skipped, commentId, skipReason, attemptedAt, postedAt, error with outcomeUnknown). The comment is attempted once, right after publish, and never retried; a failed comment never changes the published post.
    • On update-post, firstComment: null removes the comment; changing only the time leaves it untouched; the stored comment is re-validated when platforms change.
  • Migration action: none. Clients that mirror get-post responses should tolerate the two new keys.

2026-09-24

LinkedIn image-only comments

  • Affected surface: REST POST /linkedin-comments and MCP linkedin_create_comment.
  • Tag: Additive validation change.
  • Change: message remains a required string, but message: "" with a valid imageUrl is accepted for personal and organization comments. Text and image URLs are trimmed. Blank text without an image still returns HTTP 400, now with message cannot be empty without imageUrl. No placeholder text is inserted; failed image validation or upload prevents publication.
  • Migration action: Callers can send an empty string with imageUrl instead of padding image-only comments with text. Existing text comments are unchanged.

2026-09-14

Threads multi-part chains — publora.com #472, #474

  • Affected surface: REST create-post, update-post and get-post; MCP create_post, update_post and get_post; GET /platform-limits.
  • Tag: Additive, with one behaviour change for over-limit Threads content.
  • Changes:
    • Meta Threads now supports multi-part chains. platforms.threads.requirements.supportsThreading in GET /platform-limits is true; it was false.
    • Threads content over 500 characters is no longer rejected. It is accepted and split into a chain instead of being rejected with CONTENT_TOO_LONG in validation.errors[]. A successful request carries no validation object, so there is nothing new to read on success — the split is simply performed. A standalone --- line, or a complete [n/m] marker set, splits the content explicitly; automatically derived parts get a (1/N) suffix, explicit ones are published verbatim. Media attach to the first part only.
    • get-post posts[] entries gained isThread (boolean) and threadParts (array). Both are always present — false and [] for a non-chain target. Each part carries index (zero-based), content, status (pending, published, failed) and publishedId (null until confirmed). postedId is the first part's ID. list-posts does not include these fields. The same fields reach MCP through get_post, which passes the REST body through unchanged. They are populated for X/Twitter chains as well as Threads.
    • Scheduling a Threads chain requires the connection to have granted threads_manage_replies. The grant is checked at scheduling (from a per-connection cache up to five minutes old) and re-checked against Meta immediately before publishing: a missing grant returns 400 with THREADS_PERMISSION_REQUIRED in validation.errors[] and nothing is published. A failure of the permission lookup itself returns 503 with { "code": "THREADS_PERMISSIONS_UNAVAILABLE", "error": … } and no validation object — that one never means reconnect. GET /platform-connections does not report granted scopes, so the grant cannot be pre-checked.
    • New publish-time codes in posts[].error.code: THREADS_INVALID_THREAD_PART, and THREAD_PARTIALLY_PUBLISHED / PUBLISH_OUTCOME_UNKNOWN now reachable for Threads. A chain with published parts is never republished from the beginning.
    • The error message "Multi-part threads (nested replies) are temporarily blocked while we wait for Meta to approve additional permissions for our app." no longer occurs.
  • Migration action: If your client special-cased Threads by truncating text at 500 characters or by treating over-limit content as a hard error, that workaround is now unnecessary. After a partial or unknown publication, read threadParts and publish only the missing parts — do not resubmit the post.

2026-09-08

Mastodon and Bluesky statistics — publora.com #460

  • Affected surface: New REST endpoints POST /post-statistics and POST /profile-statistics, and new MCP tools post_stats and profile_stats. Nothing existing changes shape.
  • Tag: Additive.
  • Changes:
    • POST /post-statistics takes { posts: [{ platform, platformId, postedId }] } (1–50 entries, mastodon and bluesky, both platforms may be mixed in one request) and returns stats keyed by postedId. Each value is a metric object — reactions, comments, reposts, quotes, saves, impressions, reach, clicks — or null. Every key is always present; a metric the platform does not expose is null, never 0.
    • POST /profile-statistics takes { platform, platformId } and returns profile (followers, following, posts) with cached and fetchedAt, or 404 when the connection is not the caller's.
    • platformId is accepted with or without the <platform>- prefix returned by GET /platform-connections.
    • Per-connection problems do not fail the request: post-statistics reports them in an issues object keyed by connection ID (CONNECTION_NOT_FOUND, AUTH_REVOKED, FORBIDDEN, RATE_LIMITED, FETCH_FAILED), and profile-statistics reports them in unavailable and rateLimited.
    • Both endpoints, and both MCP tools, are gated by the analytics plan feature that already gated the LinkedIn analytics endpoints; a plan without it receives 403 ANALYTICS_PLAN_REQUIRED. A request that exceeds its 90-second budget returns 504 ANALYTICS_REQUEST_TIMEOUT.
    • Values are fetched from the platform on request and cached for about 2 hours. Nothing is collected in the background, so there is no history — only the counters as they stand when you ask.
  • Known limitation: only posts Publora published for you and stored a platform post ID for can be queried. Publora began storing postedId for Mastodon and Bluesky on 2026-09-07; posts published before that have no postedId and are answered null. For a thread, only the root part is addressable.
  • Migration action: None. New capability.

2026-08-27

Native Zapier app (beta)

  • Affected surface: No REST or MCP contract change. Publora now has a native app on Zapier (beta, v1.2.0), built on the public API and webhooks: instant triggers New Published Post and New Scheduled Post, actions Create Post, Update Post and Delete Post, and searches Find Connected Account and Find Posts.
  • Tag: Additive.
  • Change: The Zapier guide was rewritten for the native app. It previously documented a workaround through Webhooks by Zapier; that approach still works and stays documented on the same page for endpoints the app does not expose.
  • Migration action: None. Existing Webhooks-by-Zapier Zaps keep working; the native app is the recommended path for new Zaps.

2026-08-24

Connection health reporting corrected — publora.com #407

  • Affected surface: REST GET /platform-connections and the MCP list_connections tool, which passes that response through unchanged. test-connection reports the same corrected expiry.
  • Tag: Behavioral correction. No request shape changes, no field is added or removed; two response fields now report different — and correct — values for some connections.
  • Changes:
    • accessTokenExpiresAt now carries the effective credential expiry, the same value tokenStatus and tokenExpiresIn are derived from, instead of the raw stored access-token timestamp. For YouTube it is now always null: the access token is refreshed on demand before each publish, and Google publishes no refresh-token lifetime, so no authoritative date exists. TikTok continues to report its real refresh-token expiry; every other platform is unchanged.
    • Previously, healthy YouTube and TikTok connections returned a timestamp already in the past alongside tokenStatus: "valid". Clients that compared that date against the current time concluded the connection was dead and prompted users to reconnect working channels.
    • tokenStatus now returns expired for any connection Publora has flagged for reconnection after the platform rejected its credential — including on platforms that never expire on a schedule, where such a connection previously reported valid. In that case accessTokenExpiresAt may be null or still in the future.
  • Migration action: Decide about reconnecting from tokenStatus, not by comparing accessTokenExpiresAt against the clock. Treat expired as "prompt the user to reconnect" and expiring_soon as "warn". Treat a null expiry as "no scheduled expiry", never as a problem. The examples on the endpoint, guide and MCP pages were rewritten accordingly; code copied from earlier versions of those examples should be updated.

X replies and quote posts — publora.com #405

  • Affected surface: REST create-post and update-post, the MCP create_post and update_post tools, and the posts[].error.code reported by GET /get-post and the post.failed webhook.
  • Tag: Additive. No existing request shape changes behavior.
  • Changes:
    • platformSettings accepts a new top-level twitter object with two string keys, replyTo and quoteTweet. Both take a full x.com/twitter.com status URL or a bare 1–19 digit post ID, and both are normalized to the numeric ID before storage, so GET /get-post echoes the ID rather than the URL you sent.
    • replyTo publishes the post — or the head part of a thread — as a reply to the target; the remaining thread parts chain under it as before. quoteTweet applies to the single post or the thread head only. The two fields combine with each other and with media. An empty string clears either one.
    • A malformed reference is rejected at intake with 400 and a plain error message (platformSettings.twitter.replyTo must be a tweet URL (https://x.com/user/status/123...) or a numeric tweet ID); no code field accompanies it. An unknown key under twitter is still 400 PLATFORM_SETTING_UNKNOWN, which is evaluated first.
    • Two publish-time codes were added: X_REPLY_NOT_AUTHORIZED when X refuses the reply or quote relationship, and X_TARGET_REJECTED when the target is deleted, protected, or its author blocked the account. Both are permanent — retryable: false.
    • twitter is no longer an example of a rejected platformSettings platform; the allowlist now has seven platforms.
  • Restriction to know before integrating: X allows a programmatic reply or quote on self-serve API tiers only when the target post's author mentioned the connected account in that same post, quoted one of the account's posts, or the connected account authored the target. Enterprise apps are exempt. Publora cannot check that relationship at intake, so an unrelated target is accepted by create-post/update-post and fails later at publish time.
  • Migration action: None for existing callers. New integrations should treat these fields as inbound-engagement and own-post tools, match on error.code rather than message text, and not retry X_REPLY_NOT_AUTHORIZED or X_TARGET_REJECTED with the same target.

2026-08-03

  • Affected surface: the OAuth 2.1 flow on mcp.publora.com (Dynamic Client Registration + PKCE), used by claude.ai's custom connector, Claude Code, Codex/ChatGPT, Cursor, VS Code, Manus and other browser-capable clients.
  • Tag: Behavioral, non-breaking for existing credentials.
  • Change: The consent step is a sign-in-and-approve page — "An application is requesting access to your Publora account", naming the account being authorized, with Approve and Cancel. It no longer asks you to paste an sk_... key; that step was replaced when SSO login shipped on 2026-07-27, and the wording was finalized on 2026-08-03. On approval Publora mints a dedicated API key for that client registration, named MCP (<client> #<id>), and returns it as the access token. Static API-key headers (Authorization: Bearer sk_... / x-publora-key) are unaffected and remain the option for headless clients.
  • Migration action: None for connectors that already work. Re-authorizing a client mints a fresh key; per-client keys are listed and revocable on the API page in the dashboard. If you built on the old instructions and expected a key-paste page, drop that step.

2026-07-21

Editable draft and scheduled posts — publora.com #231

  • Affected surface: REST PUT /update-post/:postGroupId and the MCP update_post tool.
  • Tag: Additive, with new stable conflict codes.
  • Changes:
    • update-post accepts two new optional patch fields: content (replacement base text) and platforms (replacement target set). Omitting a field leaves the stored value unchanged.
    • A content edit rewrites every platform post to its effective text while preserving explicit per-account overrides; editing the text of a Twitter or Threads target clears its derived thread split.
    • A platforms edit replaces the whole target set: dropped IDs have their platform posts deleted, added IDs are validated for ownership and plan entitlement, and adding a target to a scheduled post re-runs scheduling limits plus full content/media validation. [] is accepted only while the post remains a draft.
    • Added stable codes POST_NOT_EDITABLE (400), POST_PUBLISH_IN_PROGRESS (409), and POST_GROUP_VERSION_CONFLICT (409), plus INVALID_CONTENT, INVALID_PLATFORMS, INVALID_PLATFORM_CONNECTION, and INVALID_PLATFORM_ID (400). The pre-existing "Cannot update post: post is currently in {status} status" 400 now also carries code: "POST_NOT_EDITABLE".
    • The success snapshot's postGroup now always includes the effective content and platforms.
    • The "at least one field" error text changed to "At least one of status, scheduledTime, content, platforms, platformSettings, or mediaUrls must be provided".
    • platforms arrays on both create-post and update-post now reject duplicate connection IDs with 400 "Platforms must not contain duplicates".
    • MCP update_post exposes content and platforms, and instructs clients to call list_connections before changing targets.
  • Migration action: No change is required for existing callers. Integrations that previously deleted and recreated a post to fix its text or targets should switch to update-post. Match on the new code values rather than message text, send an Idempotency-Key with content/platform edits, and re-read the post with GET /get-post on a 409 instead of blind-retrying. If you relied on a repeated connection ID in platforms being tolerated, de-duplicate the array.

2026-07-17

LinkedIn mentionable-people directory — publora.com #218

  • Affected surface: REST and MCP.
  • Tag: Additive.
  • Changes: Added GET /linkedin-mentionables and the linkedin_list_mentionables MCP tool (the 15th active tool). Both list the per-user directory of native LinkedIn member ids captured automatically from engagement on connected company pages, with q/limit filtering and a ready-to-paste mention token per person. Paid plans only — free plans receive 403 UPGRADE_REQUIRED.
  • Migration action: None. New integrations that build person mentions should prefer this directory over harvesting actor URNs manually.

2026-07-15

API/MCP correctness release — publora.com #198

  • Affected surface: REST create/update/get-post, webhooks, MCP sessions and tool responses.
  • Tags: Breaking for unknown platformSettings paths; additive/behavioral for the remaining items.
  • Changes:
    • Added Mongo-backed Idempotency-Key handling to create/update.
    • Added the warn-first scheduledTime ramp and structured coercion/rejection codes.
    • Unknown platformSettings paths now return 400 PLATFORM_SETTING_UNKNOWN; they are no longer silently discarded.
    • Added MCP session admission checks and more robust MCP response parsing.
    • Publication identity now flows through get-post and post.published, including platformId, postedId, and nullable permalink.
  • Migration action: Remove unknown settings before retrying; add idempotency keys to retryable create/update workflows; read publication identity from documented fields; audit past-time warnings before strict mode.

LinkedIn repost/reshare — publora.com #194

  • Affected surface: REST, MCP, and scheduled LinkedIn publishing.
  • Tag: Additive.
  • Changes: Added POST /linkedin-reshare, the linkedin_create_reshare MCP tool (the 14th active tool), and platformSettings.linkedin with repostEnabled, repostParentUrn, and repostVisibility as the sixth accepted settings branch.
  • Migration action: No change is required for existing callers. New integrations should use a urn:li:share:* or urn:li:ugcPost:* parent. CONNECTIONS visibility is personal-profile-only; use PUBLIC for company-page reposts.

Correctness-release documentation — publora-api-docs #21

  • Affected surface: Public API, MCP, webhook, and OpenAPI documentation.
  • Tag: Documentation.
  • Change: Published the idempotency, scheduled-time, strict-settings, MCP-draft, and publication-identity contract introduced by the correctness release.
  • Migration action: Compare existing integrations with the updated create/update, get-post, webhook, and MCP reference pages; no separate runtime change was introduced by the docs release.

Deprecation policy

No additional public API deprecations are currently scheduled. Future entries will identify the affected surface, breaking behavior, effective date, and migration action.