Native APITools
PreviewScheduling 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_profilereports it asreadiness.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
#schedule_postPreviewBooks 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
idstringRequiredThe post id.
scheduledAtISO 8601 stringRequiredWhen to publish, with
Zor a numeric offset, such as2026-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[]OptionalBook 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.dryRunbooleanOptionalNew, in development:
trueruns 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
postIdstringRequiredThe post id.
scheduleConfirmedbooleanRequiredReport the post as scheduled only when this is
true.falsemeans nothing confirms a platform holds the time, or a move did not fully land.scheduleWarningstringOptionalWhy the schedule could not be confirmed. Present when
scheduleConfirmedisfalse.platformResultsobject[]OptionalOne entry per destination, as in
get_post_status. May be absent if reading the post back failed.publishedUrlsstring[]OptionalLive 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.
}| Code | Retryable | Meaning | Extra fields |
|---|---|---|---|
schedule_preflight_failed | false | Checked 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_failed | Varies | At least one destination refused at the provider. Others may have booked. See when a destination refuses. | refusedDestinations[], scheduledDestinations[] |
platform_not_connected | false | The 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_rejected | false | The latest decision is a rejection. | None |
approval_stale | false | The content changed after it was approved. | None |
approval_required | false | New, in development: the workspace requires approval and none is on record. | None |
design_changed_after_approval | false | New, in development: publishing would change the approved pictures. Nothing was sent. | None |
post_already_published, already_published | false | A 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
platformstringRequiredThe blocked destination, such as
INSTAGRAM.codestringRequiredThe 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_longcharacterCountintegerOptionalWith
caption_too_long: the caption’s length.characterLimitintegerOptionalWith
caption_too_long: this destination’s limit.imageCountintegerOptionalWith
too_many_images: how many images the post has.imageLimitintegerOptionalWith
too_many_images: how many this destination takes.
| Code | What it means | What to do |
|---|---|---|
caption_too_long | The 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_required | The destination publishes nothing without a picture, as stories do. | Attach or generate an image. |
image_aspect_invalid | A picture is outside Instagram’s feed band of 0.75 to 1.91. | Replace it with a picture inside the band. |
too_many_images | The post has more images than this destination takes. | Remove images down to imageLimit. |
video_required | The destination takes exactly one video and no pictures, as YouTube does. | Use a post with one video, or leave the destination out. |
video_not_supported | The destination publishes no video. | Leave the destination out, or use pictures. |
post_empty | There is neither a caption nor media. | Add copy or a picture. |
reddit_subreddit_required | A Reddit destination needs a subreddit. | Set one with update_post and reddit. |
reddit_subreddit_not_found | The subreddit does not exist. | Name another subreddit. |
reddit_flair_required | The 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_long | The 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
retryableisfalseif any refusal ismedia_invalid,channel_not_connected,channel_needs_reauthorcontent_rejected, andtrueotherwise.
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
#reschedule_postsPreviewSchedules 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[]RequiredThe posts to book or move. 1 to 25 entries.
postIdstringRequiredThe post id.
scheduledAtISO 8601 stringRequiredWhen to publish this post, with
Zor a numeric offset.platformsstring[]OptionalBook only these destinations of this post. At least one when sent. Leave it out to book them all.
Output
movesobject[]RequiredOne entry per entry you sent, in the same order.
postIdstringRequiredThe post id.
scheduledAtstringRequiredThe time as you sent it.
statusstringRequiredrescheduled: 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: seeerror.One of:
rescheduled,unconfirmed,failedplatformResultsobject[]OptionalThis post’s destinations, as in
get_post_status. Also on a failed entry the provider refused.scheduleWarningstringOptionalWhy an
unconfirmedentry is not confirmed.errorobjectOptionalOn a
failedentry. The codes are the same asschedule_post’s.codestringRequiredBranch on this.
messagestringRequiredA sentence for people. It can change at any time.
retryablebooleanRequiredWhether this entry, unchanged, could succeed.
refusedDestinationsobject[]OptionalWith
schedule_failed: each refusal as{ platform, code }.scheduledDestinationsstring[]OptionalWith
schedule_failed: the destinations that booked anyway.blockedDestinationsobject[]OptionalWith
schedule_preflight_failed: the blocked destinations. Nothing was booked for this post.
rescheduledintegerRequiredHow many entries have
status: "rescheduled".failedintegerRequiredHow 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
#publish_post_nowPreviewPublishes 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
idstringRequiredThe post id.
platformsstring[]OptionalPublish or retry only these destinations. At least one when sent. Naming a destination the post does not have is a
bad_request.dryRunbooleanOptionalNew, in development:
trueruns every check and publishes nothing. See rehearse with a dry run.
Output
postIdstringRequiredThe post id.
publishResultsobject[]RequiredOne entry per destination.
platformstringRequiredThe destination, such as
INSTAGRAM.statusstringRequiredOnly
publishedmeans the destination is live.One of:
published,pending,failed,skippedurlstring | nullRequiredThe live link, once published.
reasonobject | nullRequiredWhy the destination is not published.
nullwhen it is.codestringRequiredBranch on this.
messagestringRequiredA sentence for people. It can change at any time.
retryablebooleanRequiredWhether the same call, unchanged, could succeed.
publishedintegerRequiredHow many destinations are live.
failedintegerRequiredHow many destinations failed.
skippedintegerRequiredHow many destinations were skipped.
pendingintegerOptionalHow many destinations are still finishing. May be absent.
platformResultsobject[]OptionalAs in
get_post_status. May be absent.publishedUrlsstring[]OptionalLive 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
}| Code | Status | What it means | What to do |
|---|---|---|---|
publishing_in_progress | pending | The 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_published | skipped | The destination is already live. | Nothing. Publishing again would duplicate it. |
held_by_provider | skipped | The destination is booked for a scheduled time. | If it should go now, call unschedule_platform on it first. |
stuck_at_provider | skipped | The provider took the destination and never reported back. | Do not retry: it could duplicate the post. Contact Native support. |
story_partially_published | skipped | Some 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_requested | skipped | The destination was not named in platforms. | Nothing. |
| A failure code | failed | The destination did not publish. | Follow retryable. |
| Code | Retryable | Meaning |
|---|---|---|
approval_rejected | false | The latest decision is a rejection. Nothing was published. |
approval_stale | false | The content changed after it was approved. Nothing was published. |
approval_required | false | New, in development: the workspace requires approval and none is on record. |
design_changed_after_approval | false | New, in development: publishing would change the approved pictures. Nothing was sent. |
bad_request | false | The 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
dryRunbooleanRequiredAlways
true.postIdstringRequiredThe post id.
scheduledAtISO 8601 stringRequiredThe time you sent, in UTC.
wouldSchedulestring[]RequiredThe destinations the real call would book.
blockedobject[]RequiredOne entry per destination the real call would not book.
platformstringRequiredThe destination that would not go out, such as
INSTAGRAM.codestringRequiredWhy. Branch on this.
messagestringRequiredA sentence for people, written by Native. It can change at any time.
characterCountintegerOptionalWith
caption_too_long: the caption’s length.characterLimitintegerOptionalWith
caption_too_long: this destination’s limit.imageCountintegerOptionalWith
too_many_images: how many images the post has.imageLimitintegerOptionalWith
too_many_images: how many this destination takes.
publish_post_now output with dryRun
dryRunbooleanRequiredAlways
true.postIdstringRequiredThe post id.
wouldPublishstring[]RequiredThe destinations the real call would dispatch.
blockedobject[]RequiredOne entry per destination the real call would not publish, in the same shape as for
schedule_post.platformstringRequiredThe destination that would not go out, such as
INSTAGRAM.codestringRequiredWhy. Branch on this.
messagestringRequiredA sentence for people, written by Native. It can change at any time.
characterCountintegerOptionalWith
caption_too_long: the caption’s length.characterLimitintegerOptionalWith
caption_too_long: this destination’s limit.imageCountintegerOptionalWith
too_many_images: how many images the post has.imageLimitintegerOptionalWith
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
}
]
}| Code | Call | What it means |
|---|---|---|
| The pre-flight codes | Both | What 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_connected | Both | No account is connected for this platform. |
channel_needs_reauth | Both | The account was disconnected and the client has to reconnect it. |
brand_inactive | Both | The brand is not active, so nothing publishes for it. |
platform_not_supported | Both | This destination cannot publish through the brand’s current provider. |
tiktok_video_not_public | Both | This TikTok account can only post videos as public. |
tiktok_photo_ai_label | Both | This TikTok account cannot post photos marked as AI-generated. |
publishing_in_progress | schedule_post | A booked destination being moved is publishing right now, so it cannot be moved. |
already_published, held_by_provider, stuck_at_provider, story_partially_published | publish_post_now | The 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
#unschedule_platformPreviewWithdraws 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
platformPostIdstringRequiredThe destination’s own id. Get it from
platformResults[].platformPostIdinget_post_status, or from alockedDestinationsentry in an error.
Output
unscheduleConfirmedbooleanRequiredTreat the destination as withdrawn only when this is
true.unscheduleWarningstringOptionalWhy the withdrawal could not be confirmed. Present when
unscheduleConfirmedisfalse.stillScheduledstring[]OptionalThe post’s other destinations that a provider still holds.
keptScheduledAtstring | nullOptionalThe date the withdrawn destination kept.
postIdstringOptionalThe post id.
platformResultsobject[]OptionalAs in
get_post_status.publishedUrlsstring[]OptionalLive links.
No output schema is declared. The result also carries the post as it now reads.
| Code | Retryable | Meaning |
|---|---|---|
already_published | false | A destination of the post is live, even one you did not name. Take it down with unpublish_post instead. |
not_found | false | The platformPostId is wrong, or not yours. |
Follow each destination
#get_post_statusPreviewA status snapshot of a post, per destination. Use it to poll after scheduling or publishing.
Input
idstringRequiredThe post id.
Output
idstringRequiredThe post id.
postIdstringRequiredThe same value as
id.statusstringRequiredA summary for the whole post. Branch on
platformResultsinstead.One of:
Draft,Scheduled,Published,partially_published,errorscheduledAtISO 8601 string | nullRequiredThe earliest time across the destinations.
platformResultsobject[]RequiredOne entry per destination. See results per destination.
publishedUrlsstring[]RequiredLive links.
contentDigeststring | nullOptionalNew, in development: the version an approval locks, the same value
view_postreturns. Until then, read it fromview_post.approvalobject | nullOptionalNew, in development: the latest review decision, as in the approval object. Until then, read it from
view_post.platformVariantsobject[]RequiredThe raw form of each destination. Prefer
platformResults.platformstringRequiredThe destination.
publishingStatusstring | nullRequiredThe raw publishing status.
socialPostUrlstring | nullRequiredThe live link, when there is one.
publishErrorMessagestring | nullRequiredA sentence about a failure. Do not branch on it.
Results per destination
#platformResults[] entry
platformPostIdstringOptionalThe destination’s own id, which
unschedule_platformandmove_platform_to_draftstake. May be absent.platformstringRequiredThe destination, as an uppercase name such as
INSTAGRAM.statestringRequiredWhat this destination is doing. See the table below.
One of:
published,failed,stuck,scheduled,pending,unknownscheduledAtstring | nullOptionalThis destination’s own time. The post’s
scheduledAtis the earliest across destinations, so the two can differ. May be absent.urlstring | nullRequiredThe live link, once published.
errorobject | nullRequiredPresent on a failure. The codes are under failure codes.
codestringRequiredBranch on this.
messagestringRequiredA sentence for people. It can change at any time.
retryablebooleanRequiredWhether the same call, unchanged, could succeed.
| State | What it means | What to do |
|---|---|---|
published | The destination is live. url is the link. | Nothing. |
scheduled | It 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. |
pending | No 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. |
failed | It did not publish. error says why. | Follow error.retryable. Native does not retry on its own. |
stuck | The 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. |
unknown | Native 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.
| Code | Retryable | What to do |
|---|---|---|
channel_not_connected | false | Send the client a link from create_connect_link, then retry. |
channel_needs_reauth | false | The same: the client has to reconnect the account. |
permission_denied | false | The account lacks a permission, such as a page role. Reconnect with the right account. |
rate_limited | true | Retry later, with backoff. |
provider_timeout | true | Retry, but read get_post_status first, because it may have gone through. |
provider_unavailable | true | Retry later. |
media_required | false | Attach or generate media. |
media_invalid | false | Replace the image or video. The aspect ratio, size, format or duration is wrong for the platform. |
content_rejected | false | The platform refused the content, for example for policy or length. Edit it, have it approved again, and retry. |
duplicate_content | false | The platform sees it as a duplicate. Change the content or drop the destination. |
validation_failed | false | Fix what the message names. Do not retry unchanged. |
unknown_error | false | Do not retry blindly: it may have published. Read get_post_status, and contact support if it is unclear. |
How often to poll
#| What | Call | How often | Done when |
|---|---|---|---|
| A scheduled publish | get_post_status | Once 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_now | get_post_status | Every 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 health | get_profile | Daily, 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 whoseretryableisfalsewithout changing something first. publish_post_nowis safe to call again. It retries only failed destinations and never dispatches a published one again.stuck_at_providerandstory_partially_publishedneed support or a new post, not a retry.- After a timeout or a dropped connection on a write, read
get_post_statusbefore 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_postorpublish_post_now. upstream_timeoutmeans 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.