Skip to main content

conflict

In v1 this is most often the scheduling pre-flight: POST /v1/posts with scheduledFor, PATCH /v1/posts/{postId} with scheduledFor, and the confirmed second call of the MCP schedule_post all verify that the project’s LinkedIn connection can still publish before they write anything. If it cannot, nothing is created, nothing is changed, and you get this. The other cases share the shape but not the repair:
  • A publish is already in flight for the post, or a publish failed within the last 10 minutes and it is not yet known whether LinkedIn accepted it (MCP publish_post) — check the post on LinkedIn instead of retrying.
  • The workspace requires review and the post has no approved review (MCP publish_post, schedule_post, and create_post_draft / generate_post with scheduledFor; never the REST write paths) — details.reason is review_not_requested / review_pending / review_rejected and details.reviewerUserId names the current reviewer or is null. This one needs the workspace’s plan to include content approvals as well as the flag: below that plan nothing here refuses, so review.requiredByWorkspace: true on a post is not on its own a prediction that you will see this. Approval happens in the dashboard; the states and the repair are in MCP tools §1.3.
  • A knowledge document that is one page of a dashboard sync refused deletion (MCP delete_knowledge_doc) — deleting one page of a website, Notion or synced source sync is pointless because the next sync run restores it; manage the sync in the dashboard instead.
  • A deleted media asset could not be restored (POST /v1/media with the bytes of an asset removed by MCP delete_media_asset) — the row’s record of superseded storage keys is what the row-driven purge follows, so a restore that cannot record the key it replaces is refused rather than overwriting that record. Two states do that, and the message names which: the record is full (that many delete/restore cycles), in which case wait out the rest of the asset’s 30-day retention window — the sweep erases those files and clears the record — then send the bytes again; or the record is unreadable, which the sweep also refuses to act on, so waiting does not clear it and the message asks you to report the asset id to support instead of retrying.
Two sibling 409s have their own codes and their own pages: idempotency_key_conflict and not_cancellable. Unlike those, a conflict is about the resource’s state, not your request’s replay semantics — retrying without changing the underlying state returns the same error.

Body (scheduling pre-flight)

Switch on details.reason, not on the message: details.retryable is always false here. Every one of these needs a human to complete an OAuth flow in a browser; no amount of retrying, backing off, or re-authorizing your API credentials changes the answer. Of the non-pre-flight cases above, only the review-gate refusal carries details (its own reason values, no reconnectUrl); the rest carry none — read message.

What to do

  1. Do not retry. Stop the scheduling attempt.
  2. Tell the user what is wrong and hand them the link. Only they can reconnect. message is written to be shown as-is; details.reconnectUrl is where they go.
  3. After they say they are done, schedule again. Nothing was written, so there is no half-scheduled post to clean up.

Seeing it coming

Two reads answer “will this work?” before you attempt the write:
  • GET /v1/projects/{projectId}linkedIn.canPublish (MCP: get_project) — the stored verdict, cheap, with the same detail and reconnectUrl fields.
  • The MCP schedule_post proposal → linkedIn.canSchedule — the live check, run at phase one so a user is never asked to confirm a schedule that cannot happen. It reports rather than refuses: you still get a confirmationToken, because reconnecting and then confirming is exactly the flow this is meant to enable.

Why an unconnected project is refused rather than queued

A post scheduled against a project with no LinkedIn token cannot ever publish. The publish cron records the failure, retries for 24 hours and gives up; the post keeps saying scheduled and nothing tells the user. That state is visible after the fact as delivery.state: "missed", which is a diagnosis, not a repair. Refusing at the moment of scheduling is the only point at which the person who can fix it is still in the room.