Native API· Scheduling and publishing

Native APITools

Preview

Scheduling and publishing

Book a post for a time, move many at once, publish now, withdraw one destination, and follow each destination to a live link or a failure.

A post has one or more destinations, such as INSTAGRAM and FACEBOOK. Scheduling and publishing happen per destination: one can be live while another failed. So read the per-destination results, never one flag for the whole post.

Native does not retry a failed publish on its own. A failed destination stays failed until you act.

Before you schedule or publish

#
  • The brand needs a connected channel. get_profile reports it as readiness.canPublish.
  • The post has to pass the approval check. See review and approval.
  • New, in development: you can rehearse the call first with dryRun: true.

Calls that contact platforms can take far longer than others. The gateway waits up to 120 seconds for Native before it answers upstream_timeout, so set your client’s timeout above that, for example 150 seconds. See limits.

The approval check

#

schedule_post and publish_post_now check the post’s approval first. They refuse the whole call with approval_rejected or approval_stale. New, in development: also with approval_required or design_changed_after_approval. Review and approval explains each case.

reschedule_posts checks each post on its own. A refusal shows up in that entry’s moves[].error, and the other entries still run. So a partly refused call is not untouched: do not resend the whole list.

Schedule a post

#
TOOLschedule_postPreview

Books a post, or some of its destinations, for a time. It also moves a post that is already scheduled. That is one call, and the post stays booked throughout.

Input

  • idstringRequired

    The post id.

  • scheduledAtISO 8601 stringRequired

    When to publish, with Z or a numeric offset, such as 2026-11-02T09:00:00+01:00. Native stores it in UTC. A time in the past is refused on a post that is already scheduled.

  • platformsstring[]Optional

    Book only these destinations, as uppercase names such as INSTAGRAM. At least one when sent. Leave it out to book every destination. Destinations you do not name keep their own schedule.

  • dryRunbooleanOptional

    New, in development: true runs every check and books nothing. See rehearse with a dry run.

Native stores the time before it hands the post to the platform. If no platform takes it, the post can carry a future date and still never publish. scheduleConfirmed is how you tell the two apart.

Output

  • postIdstringRequired

    The post id.

  • scheduleConfirmedbooleanRequired

    Report the post as scheduled only when this is true. false means nothing confirms a platform holds the time, or a move did not fully land.

  • scheduleWarningstringOptional

    Why the schedule could not be confirmed. Present when scheduleConfirmed is false.

  • platformResultsobject[]Optional

    One entry per destination, as in get_post_status. May be absent if reading the post back failed.

  • publishedUrlsstring[]Optional

    Live links. Usually empty after scheduling. May be absent if reading the post back failed.

No output schema is declared for the real call. The result also carries the post as it now reads, and the view_post preview fields (authorName, authorSubtitle, authorAvatarUrl, postText, imageUrl, imageUrls, destinations, appOrigin, appUrl), any of which may be absent. Ignore fields you do not use.

Schedule a post

const result = await client.callTool(
  {
    name: "schedule_post",
    arguments: { id: postId, scheduledAt: "2026-11-02T09:00:00+01:00" },
  },
  undefined,
  { timeout: 150_000 },
);

const data = (result.structuredContent as { data: any }).data;
if (result.isError) {
  // data = { ok: false, error: { code, message, retryable, ... } }
  handleScheduleError(data.error);
} else if (data.scheduleConfirmed) {
  // Booked. Poll get_post_status a few minutes after the time.
} else {
  // Not confirmed. Show data.scheduleWarning, then check get_post_status.
}
CodeRetryableMeaningExtra fields
schedule_preflight_failedfalseChecked before any platform was contacted. Nothing was booked and the post is untouched. Fix every destination named, or leave them out with platforms, then call again.blockedDestinations[]
schedule_failedVariesAt least one destination refused at the provider. Others may have booked. See when a destination refuses.refusedDestinations[], scheduledDestinations[]
platform_not_connectedfalseThe post has no destinations, and the brand has no connected channel. Send the client a link from create_connect_link. A destination whose own account is not connected shows up as schedule_failed with channel_not_connected instead.unavailablePlatforms, connectedPlatforms
approval_rejectedfalseThe latest decision is a rejection.None
approval_stalefalseThe content changed after it was approved.None
approval_requiredfalseNew, in development: the workspace requires approval and none is on record.None
design_changed_after_approvalfalseNew, in development: publishing would change the approved pictures. Nothing was sent.None
post_already_published, already_publishedfalseA destination of the post is live, so the post cannot be scheduled. Which of the two codes you get depends on the post’s state, so handle both.publishedPlatforms

Common codes, such as bad_request and upstream_timeout, are listed under errors.

Checks before any platform is contacted

#

Native checks what each destination will take before it contacts any platform. If any destination fails, the call fails with schedule_preflight_failed and lists every blocked destination in one answer. The same call, unchanged, fails the same way: it is a list of edits to make, not a thing to retry.

structuredContent.data

{
  "ok": false,
  "error": {
    "code": "schedule_preflight_failed",
    "message": "Gateway-written sentence for people",
    "retryable": false,
    "blockedDestinations": [
      {
        "platform": "INSTAGRAM",
        "code": "caption_too_long",
        "characterCount": 2410,
        "characterLimit": 2200
      }
    ]
  }
}

blockedDestinations[] entry

  • platformstringRequired

    The blocked destination, such as INSTAGRAM.

  • codestringRequired

    The rule it breaks. Treat an unknown code as blocking.

    One of: caption_too_long, media_required, image_aspect_invalid, too_many_images, video_required, video_not_supported, post_empty, reddit_subreddit_required, reddit_subreddit_not_found, reddit_flair_required, reddit_title_too_long

  • characterCountintegerOptional

    With caption_too_long: the caption’s length.

  • characterLimitintegerOptional

    With caption_too_long: this destination’s limit.

  • imageCountintegerOptional

    With too_many_images: how many images the post has.

  • imageLimitintegerOptional

    With too_many_images: how many this destination takes.

CodeWhat it meansWhat to do
caption_too_longThe caption is over this destination’s limit.Shorten this destination’s own copy with update_post and platform, or rewrite it with generate_caption.
media_requiredThe destination publishes nothing without a picture, as stories do.Attach or generate an image.
image_aspect_invalidA picture is outside Instagram’s feed band of 0.75 to 1.91.Replace it with a picture inside the band.
too_many_imagesThe post has more images than this destination takes.Remove images down to imageLimit.
video_requiredThe destination takes exactly one video and no pictures, as YouTube does.Use a post with one video, or leave the destination out.
video_not_supportedThe destination publishes no video.Leave the destination out, or use pictures.
post_emptyThere is neither a caption nor media.Add copy or a picture.
reddit_subreddit_requiredA Reddit destination needs a subreddit.Set one with update_post and reddit.
reddit_subreddit_not_foundThe subreddit does not exist.Name another subreddit.
reddit_flair_requiredThe subreddit requires a flair, and the post has none.The flair is chosen in the Native app. Send the client to the post’s appUrl.
reddit_title_too_longThe Reddit title is over Reddit’s limit.Shorten the title.

When a destination refuses

#

schedule_failed means at least one destination refused at the provider. Others may have booked anyway.

  • refusedDestinations[] lists each refusal as { platform, code }, with the codes under failure codes.
  • scheduledDestinations[] lists the destinations that booked.
  • The envelope’s retryable is false if any refusal is media_invalid, channel_not_connected, channel_needs_reauth or content_rejected, and true otherwise.

When retryable is true, call schedule_post again unchanged. It books only the destinations still outstanding. When it is false, fix what the codes name first. If there is no refusedDestinations, read get_post_status for the reason before you change anything.

Schedule several posts

#
TOOLreschedule_postsPreview

Schedules or moves up to 25 posts in one call. Despite the name, it books drafts for the first time too. Entries run in the order you send them, and one failure does not stop the rest.

Each entry gets the same checks as schedule_post, including the pre-flight and the approval check. reschedule_posts has no dryRun.

Input

  • movesobject[]Required

    The posts to book or move. 1 to 25 entries.

    • postIdstringRequired

      The post id.

    • scheduledAtISO 8601 stringRequired

      When to publish this post, with Z or a numeric offset.

    • platformsstring[]Optional

      Book only these destinations of this post. At least one when sent. Leave it out to book them all.

Output

  • movesobject[]Required

    One entry per entry you sent, in the same order.

    • postIdstringRequired

      The post id.

    • scheduledAtstringRequired

      The time as you sent it.

    • statusstringRequired

      rescheduled: a platform holds the time, for a first booking as much as a move. unconfirmed: Native accepted the time, but nothing confirms a platform holds it. failed: see error.

      One of: rescheduled, unconfirmed, failed

    • platformResultsobject[]Optional

      This post’s destinations, as in get_post_status. Also on a failed entry the provider refused.

    • scheduleWarningstringOptional

      Why an unconfirmed entry is not confirmed.

    • errorobjectOptional

      On a failed entry. The codes are the same as schedule_post’s.

      • codestringRequired

        Branch on this.

      • messagestringRequired

        A sentence for people. It can change at any time.

      • retryablebooleanRequired

        Whether this entry, unchanged, could succeed.

      • refusedDestinationsobject[]Optional

        With schedule_failed: each refusal as { platform, code }.

      • scheduledDestinationsstring[]Optional

        With schedule_failed: the destinations that booked anyway.

      • blockedDestinationsobject[]Optional

        With schedule_preflight_failed: the blocked destinations. Nothing was booked for this post.

  • rescheduledintegerRequired

    How many entries have status: "rescheduled".

  • failedintegerRequired

    How many entries have status: "failed".

An unconfirmed entry counts in neither total, so rescheduled plus failed can be less than the number of entries.

structuredContent.data

{
  "moves": [
    {
      "postId": "3f6a2c1e-8b4d-4f7a-9e2b-5c0d1a7e4b93",
      "scheduledAt": "2026-11-02T09:00:00+01:00",
      "status": "rescheduled",
      "platformResults": [
        {
          "platformPostId": "a1c9e7b2-4d6f-4a8e-b3c5-0f2d9e1a7b64",
          "platform": "FACEBOOK",
          "state": "scheduled",
          "scheduledAt": "2026-11-02T08:00:00.000Z",
          "url": null,
          "error": null
        }
      ]
    },
    {
      "postId": "7b0e5d9a-2c4f-4e1b-8a6d-3f9c0b2e5a71",
      "scheduledAt": "2026-11-03T09:00:00+01:00",
      "status": "failed",
      "error": {
        "code": "approval_stale",
        "message": "Gateway-written sentence for people",
        "retryable": false
      }
    }
  ],
  "rescheduled": 1,
  "failed": 1
}

After a partly failed call, fix the failed entries and send only those. Check unconfirmed entries with get_post_status before you send them again.

Publish now

#
TOOLpublish_post_nowPreview

Publishes now. Calling it again retries only the destinations that failed. A destination that already published is never dispatched again.

The post goes live on the client’s real accounts. unpublish_post can take some destinations down again, but Native cannot remove a post from Instagram, TikTok or a story yet. Treat publishing as final.

Input

  • idstringRequired

    The post id.

  • platformsstring[]Optional

    Publish or retry only these destinations. At least one when sent. Naming a destination the post does not have is a bad_request.

  • dryRunbooleanOptional

    New, in development: true runs every check and publishes nothing. See rehearse with a dry run.

Output

  • postIdstringRequired

    The post id.

  • publishResultsobject[]Required

    One entry per destination.

    • platformstringRequired

      The destination, such as INSTAGRAM.

    • statusstringRequired

      Only published means the destination is live.

      One of: published, pending, failed, skipped

    • urlstring | nullRequired

      The live link, once published.

    • reasonobject | nullRequired

      Why the destination is not published. null when it is.

      • codestringRequired

        Branch on this.

      • messagestringRequired

        A sentence for people. It can change at any time.

      • retryablebooleanRequired

        Whether the same call, unchanged, could succeed.

  • publishedintegerRequired

    How many destinations are live.

  • failedintegerRequired

    How many destinations failed.

  • skippedintegerRequired

    How many destinations were skipped.

  • pendingintegerOptional

    How many destinations are still finishing. May be absent.

  • platformResultsobject[]Optional

    As in get_post_status. May be absent.

  • publishedUrlsstring[]Optional

    Live links. May be absent.

The result can also carry the view_post preview fields. Any of them may be absent.

structuredContent.data

{
  "postId": "3f6a2c1e-8b4d-4f7a-9e2b-5c0d1a7e4b93",
  "publishResults": [
    {
      "platform": "FACEBOOK",
      "status": "published",
      "url": "https://www.facebook.com/123456789012345/posts/987654321098765",
      "reason": null
    },
    {
      "platform": "INSTAGRAM",
      "status": "pending",
      "url": null,
      "reason": {
        "code": "publishing_in_progress",
        "message": "Sentence for people",
        "retryable": false
      }
    }
  ],
  "published": 1,
  "failed": 0,
  "skipped": 0,
  "pending": 1
}
CodeStatusWhat it meansWhat to do
publishing_in_progresspendingThe platform accepted the post and is finishing it on its own. Instagram does this routinely.Do not retry. Poll get_post_status, where the destination reads scheduled until it lands.
already_publishedskippedThe destination is already live.Nothing. Publishing again would duplicate it.
held_by_providerskippedThe destination is booked for a scheduled time.If it should go now, call unschedule_platform on it first.
stuck_at_providerskippedThe provider took the destination and never reported back.Do not retry: it could duplicate the post. Contact Native support.
story_partially_publishedskippedSome frames of the story are live.Do not retry: it would send every frame again. Make a new post from the frames that did not publish.
not_requestedskippedThe destination was not named in platforms.Nothing.
A failure codefailedThe destination did not publish.Follow retryable.
CodeRetryableMeaning
approval_rejectedfalseThe latest decision is a rejection. Nothing was published.
approval_stalefalseThe content changed after it was approved. Nothing was published.
approval_requiredfalseNew, in development: the workspace requires approval and none is on record.
design_changed_after_approvalfalseNew, in development: publishing would change the approved pictures. Nothing was sent.
bad_requestfalseThe input is invalid, for example a destination the post does not have, or a post with no destinations.

An account that is not connected does not fail the whole call. That destination comes back failed with channel_not_connected or channel_needs_reauth in publishResults, while the others go out.

Rehearse with a dry run

#

schedule_post and publish_post_now will take dryRun: true. Native runs every check the real call runs and stops before its first write. Nothing is booked, published, written or sent to any platform. A dry run needs the same mcp:write scope as the real call.

schedule_post output with dryRun

  • dryRunbooleanRequired

    Always true.

  • postIdstringRequired

    The post id.

  • scheduledAtISO 8601 stringRequired

    The time you sent, in UTC.

  • wouldSchedulestring[]Required

    The destinations the real call would book.

  • blockedobject[]Required

    One entry per destination the real call would not book.

    • platformstringRequired

      The destination that would not go out, such as INSTAGRAM.

    • codestringRequired

      Why. Branch on this.

    • messagestringRequired

      A sentence for people, written by Native. It can change at any time.

    • characterCountintegerOptional

      With caption_too_long: the caption’s length.

    • characterLimitintegerOptional

      With caption_too_long: this destination’s limit.

    • imageCountintegerOptional

      With too_many_images: how many images the post has.

    • imageLimitintegerOptional

      With too_many_images: how many this destination takes.

publish_post_now output with dryRun

  • dryRunbooleanRequired

    Always true.

  • postIdstringRequired

    The post id.

  • wouldPublishstring[]Required

    The destinations the real call would dispatch.

  • blockedobject[]Required

    One entry per destination the real call would not publish, in the same shape as for schedule_post.

    • platformstringRequired

      The destination that would not go out, such as INSTAGRAM.

    • codestringRequired

      Why. Branch on this.

    • messagestringRequired

      A sentence for people, written by Native. It can change at any time.

    • characterCountintegerOptional

      With caption_too_long: the caption’s length.

    • characterLimitintegerOptional

      With caption_too_long: this destination’s limit.

    • imageCountintegerOptional

      With too_many_images: how many images the post has.

    • imageLimitintegerOptional

      With too_many_images: how many this destination takes.

schedule_post with dryRun: structuredContent.data

{
  "dryRun": true,
  "postId": "3f6a2c1e-8b4d-4f7a-9e2b-5c0d1a7e4b93",
  "scheduledAt": "2026-11-02T08:00:00.000Z",
  "wouldSchedule": ["FACEBOOK"],
  "blocked": [
    {
      "platform": "INSTAGRAM",
      "code": "caption_too_long",
      "message": "Gateway-written sentence for people",
      "characterCount": 2410,
      "characterLimit": 2200
    }
  ]
}
CodeCallWhat it means
The pre-flight codesBothWhat the destination will not take. The real publish_post_now does not run these checks, so it would only learn them as a failed destination while the rest went live.
channel_not_connectedBothNo account is connected for this platform.
channel_needs_reauthBothThe account was disconnected and the client has to reconnect it.
brand_inactiveBothThe brand is not active, so nothing publishes for it.
platform_not_supportedBothThis destination cannot publish through the brand’s current provider.
tiktok_video_not_publicBothThis TikTok account can only post videos as public.
tiktok_photo_ai_labelBothThis TikTok account cannot post photos marked as AI-generated.
publishing_in_progressschedule_postA booked destination being moved is publishing right now, so it cannot be moved.
already_published, held_by_provider, stuck_at_provider, story_partially_publishedpublish_post_nowThe skip reasons of the real call, described under publish now.

Treat the codes as an open set. A code you do not know still means the destination would not go out.

Refusals of the whole post stay errors, and a dry run fails with them exactly as the real call would. For both tools: the approval check, a post with no destinations, and an unknown platform in platforms. For schedule_post also: a post with a live destination, and moving a booked post into the past. In a publish dry run, destinations you did not name in platforms are left out.

An empty blocked list is not a guarantee. A few checks need the platform itself, so they run only in the real call, and a platform can still refuse at dispatch. A dry run reads the post as it stands at that moment, so run it close to the real call.

Rehearse, then schedule

async function call(name: string, args: Record<string, unknown>) {
  const result = await client.callTool(
    { name, arguments: args },
    undefined,
    { timeout: 150_000 },
  );
  const data = (result.structuredContent as { data: any } | undefined)?.data;
  return { isError: result.isError === true, data };
}

const when = "2026-11-02T09:00:00+01:00";
const rehearsal = await call("schedule_post", {
  id: postId,
  scheduledAt: when,
  dryRun: true,
});

if (rehearsal.isError) {
  handleScheduleError(rehearsal.data.error); // for example approval_stale
} else if (rehearsal.data.blocked.length > 0) {
  // Show rehearsal.data.blocked to the client and fix those destinations.
} else {
  const booked = await call("schedule_post", { id: postId, scheduledAt: when });
  // Check booked.isError and booked.data.scheduleConfirmed as usual.
}

Withdraw a destination

#
TOOLunschedule_platformPreview

Withdraws one destination from the provider. A post with three destinations takes three calls. The destination keeps its date, as a draft. To clear the date as well, call move_platform_to_drafts with the same platformPostId.

Input

  • platformPostIdstringRequired

    The destination’s own id. Get it from platformResults[].platformPostId in get_post_status, or from a lockedDestinations entry in an error.

Output

  • unscheduleConfirmedbooleanRequired

    Treat the destination as withdrawn only when this is true.

  • unscheduleWarningstringOptional

    Why the withdrawal could not be confirmed. Present when unscheduleConfirmed is false.

  • stillScheduledstring[]Optional

    The post’s other destinations that a provider still holds.

  • keptScheduledAtstring | nullOptional

    The date the withdrawn destination kept.

  • postIdstringOptional

    The post id.

  • platformResultsobject[]Optional

    As in get_post_status.

  • publishedUrlsstring[]Optional

    Live links.

No output schema is declared. The result also carries the post as it now reads.

CodeRetryableMeaning
already_publishedfalseA destination of the post is live, even one you did not name. Take it down with unpublish_post instead.
not_foundfalseThe platformPostId is wrong, or not yours.

Follow each destination

#
TOOLget_post_statusPreview

A status snapshot of a post, per destination. Use it to poll after scheduling or publishing.

Input

  • idstringRequired

    The post id.

Output

  • idstringRequired

    The post id.

  • postIdstringRequired

    The same value as id.

  • statusstringRequired

    A summary for the whole post. Branch on platformResults instead.

    One of: Draft, Scheduled, Published, partially_published, error

  • scheduledAtISO 8601 string | nullRequired

    The earliest time across the destinations.

  • platformResultsobject[]Required

    One entry per destination. See results per destination.

  • publishedUrlsstring[]Required

    Live links.

  • contentDigeststring | nullOptional

    New, in development: the version an approval locks, the same value view_post returns. Until then, read it from view_post.

  • approvalobject | nullOptional

    New, in development: the latest review decision, as in the approval object. Until then, read it from view_post.

  • platformVariantsobject[]Required

    The raw form of each destination. Prefer platformResults.

    • platformstringRequired

      The destination.

    • publishingStatusstring | nullRequired

      The raw publishing status.

    • socialPostUrlstring | nullRequired

      The live link, when there is one.

    • publishErrorMessagestring | nullRequired

      A sentence about a failure. Do not branch on it.

Results per destination

#

platformResults[] entry

  • platformPostIdstringOptional

    The destination’s own id, which unschedule_platform and move_platform_to_drafts take. May be absent.

  • platformstringRequired

    The destination, as an uppercase name such as INSTAGRAM.

  • statestringRequired

    What this destination is doing. See the table below.

    One of: published, failed, stuck, scheduled, pending, unknown

  • scheduledAtstring | nullOptional

    This destination’s own time. The post’s scheduledAt is the earliest across destinations, so the two can differ. May be absent.

  • urlstring | nullRequired

    The live link, once published.

  • errorobject | nullRequired

    Present on a failure. The codes are under failure codes.

    • codestringRequired

      Branch on this.

    • messagestringRequired

      A sentence for people. It can change at any time.

    • retryablebooleanRequired

      Whether the same call, unchanged, could succeed.

StateWhat it meansWhat to do
publishedThe destination is live. url is the link.Nothing.
scheduledIt is booked for its time. A destination the platform accepted from publish_post_now and is still finishing also reads scheduled until it lands.Wait, and poll after its time.
pendingNo provider holds it yet: it is not booked, published or failed. A draft destination reads pending, and so does one you withdrew with unschedule_platform.Schedule or publish it if it should go out. It will not go out on its own.
failedIt did not publish. error says why.Follow error.retryable. Native does not retry on its own.
stuckThe provider accepted it, and its time passed with no outcome.Stop polling and contact Native support. Do not publish again: it may have gone out.
unknownNative could not work out the state. The destination may be live.Read the status again later. Do not publish again on this alone.

structuredContent.data

{
  "id": "3f6a2c1e-8b4d-4f7a-9e2b-5c0d1a7e4b93",
  "postId": "3f6a2c1e-8b4d-4f7a-9e2b-5c0d1a7e4b93",
  "status": "partially_published",
  "scheduledAt": "2026-11-02T08:00:00.000Z",
  "platformResults": [
    {
      "platformPostId": "a1c9e7b2-4d6f-4a8e-b3c5-0f2d9e1a7b64",
      "platform": "FACEBOOK",
      "state": "published",
      "scheduledAt": "2026-11-02T08:00:00.000Z",
      "url": "https://www.facebook.com/123456789012345/posts/987654321098765",
      "error": null
    },
    {
      "platformPostId": "d4e8b1f6-9a2c-4c7e-8f0b-6a3d5e9c1b27",
      "platform": "INSTAGRAM",
      "state": "failed",
      "scheduledAt": "2026-11-02T08:00:00.000Z",
      "url": null,
      "error": {
        "code": "media_invalid",
        "message": "Sentence for people",
        "retryable": false
      }
    }
  ],
  "publishedUrls": ["https://www.facebook.com/123456789012345/posts/987654321098765"],
  "platformVariants": [
    {
      "platform": "FACEBOOK",
      "publishingStatus": "SUCCESS",
      "socialPostUrl": "https://www.facebook.com/123456789012345/posts/987654321098765",
      "publishErrorMessage": null
    },
    {
      "platform": "INSTAGRAM",
      "publishingStatus": "FAILED",
      "socialPostUrl": null,
      "publishErrorMessage": "Sentence for people"
    }
  ]
}

Failure codes per destination

#

These codes appear in platformResults[].error.code, publishResults[].reason.code and refusedDestinations[].code. Treat a code you do not know as its retryable flag says.

CodeRetryableWhat to do
channel_not_connectedfalseSend the client a link from create_connect_link, then retry.
channel_needs_reauthfalseThe same: the client has to reconnect the account.
permission_deniedfalseThe account lacks a permission, such as a page role. Reconnect with the right account.
rate_limitedtrueRetry later, with backoff.
provider_timeouttrueRetry, but read get_post_status first, because it may have gone through.
provider_unavailabletrueRetry later.
media_requiredfalseAttach or generate media.
media_invalidfalseReplace the image or video. The aspect ratio, size, format or duration is wrong for the platform.
content_rejectedfalseThe platform refused the content, for example for policy or length. Edit it, have it approved again, and retry.
duplicate_contentfalseThe platform sees it as a duplicate. Change the content or drop the destination.
validation_failedfalseFix what the message names. Do not retry unchanged.
unknown_errorfalseDo not retry blindly: it may have published. Read get_post_status, and contact support if it is unclear.

How often to poll

#
WhatCallHow oftenDone when
A scheduled publishget_post_statusOnce a few minutes after scheduledAt, then every 5 minutes for 30 minutes.Every destination is published or failed. While one still reads scheduled, keep polling. On stuck, stop and contact support.
After publish_post_nowget_post_statusEvery 10 seconds for 2 minutes, then every 60 seconds for 10 minutes.Every destination is published or failed. Only the destinations that came back pending in publishResults need polling. They read scheduled here until they land.
Channel healthget_profileDaily, and before you schedule.An ongoing check. Act on needs_reauth, disconnected and expiring_soon before publishing breaks.

Webhooks are not available, so polling is the only way to learn the outcome. Back off with jitter on errors that are retryable, and stop on ones that are not.

Retry safely

#
  • Respect retryable. Never repeat a call whose retryable is false without changing something first.
  • publish_post_now is safe to call again. It retries only failed destinations and never dispatches a published one again. stuck_at_provider and story_partially_published need support or a new post, not a retry.
  • After a timeout or a dropped connection on a write, read get_post_status before you retry. A lost response does not prove that nothing changed.
  • Sending the same tool call again is a new attempt, and Native does not deduplicate it. Keys you supply yourself are planned, not available. Until then, check the state before you retry schedule_post or publish_post_now.
  • upstream_timeout means Native did not answer within 120 seconds. The work may still be running, so read the state first.

The Native API is not open yet

Partner access is being built now. Tell us what you would build, and we will be in touch when it opens.

Contact sales