Native API· Overview

Native APIGet started

Preview

Native API

MCP tools that let a partner run Native for its own clients from its backend: onboard, generate, approve and publish. Not live for partners yet.

Rather have your coding agent set it up? Give it the one line on Set up with an agent, and it adds the Native API to your project, ready for when your access opens.

What the Native API is for

#

The Native API is for partners who run Native for their own clients, from their own backend. Your code onboards a client, generates posts, collects the client’s approval in your own interface, publishes, and handles what fails. No language model sits in between: your backend calls the tools directly.

Each client is a Native workspace of its own, with one brand. Every call names the client by its brandId, or by the id of a post that belongs to it. The tools also call a brand a profile: create_profile makes one and get_profile reads it.

Work for one client runs in this order:

  1. Onboard the client from its website, and wait while Native reads it. See Profiles.
  2. Set its look: colours, fonts and logo. See Branding.
  3. Connect its social accounts. The client logs in with each platform. See Social accounts.
  4. Generate posts from a brief, then edit them. See Generating posts and Editing posts.
  5. Collect the client’s approval of the exact content that will go out. See Approval.
  6. Schedule or publish, then track each destination. See Publishing.
  7. Offboard the client when the service ends, or erase it on request. See Profiles.

The flow walks through it call by call.

How it is shaped

#

The Native API is a set of MCP tools, not REST endpoints. Your backend calls them over MCP’s Streamable HTTP transport at https://mcp.native.no/mcp.

  • Every call is an MCP tools/call. Use the official MCP SDK rather than writing JSON-RPC by hand.
  • The gateway is stateless. Each request stands on its own, and nothing is kept between calls.
  • tools/list returns every tool with its inputSchema and outputSchema as JSON Schema. Check these pages against it. When the two disagree, the live list is right.
  • A few tools declare no fields in their output schema, only that the payload is an object. Their pages list what comes back.
  • The output schema of a tool that changes data also allows { "cancelled": true, "changed": false }. That is the answer when a person declines a confirmation in an assistant. Your backend’s calls are never asked for one, so you do not get it.
  • Tools that change data need the mcp:write scope as well as mcp:read. Each tool’s readOnlyHint annotation says which kind it is.
  • Generation runs in the background. create_posts answers with a batchId and regenerate_post_image with a jobId, and you poll until the work is done. There are no webhooks yet.
  • Three tools are for partners only: create_profile, deactivate_profile and delete_profile. They appear in tools/list only for a partner token.

What the badges mean

#
BadgeMeaning
PreviewThe tool is in Native’s MCP gateway today and serves people signed in to Native. A partner can call it once partner sign-in is switched on for them.
In developmentThe tool, field or error code is built but not deployed. These pages show its planned shape, which can change before it ships.

A Preview tool can have fields that are still in development. The description of each such field starts with “New, in development”.

Until partner sign-in ships, both badges mean the same thing for a partner: you can plan against the tool, but you cannot call it.

The tools

#
ToolWhat it doesStatus
create_profileCreates a client’s workspace and brand from its website.In development
get_profileShows a brand’s channels, their health, and whether it can generate and publish.Preview
get_workspacesLists the workspaces the token can reach.Preview
list_brandsLists the brands in one workspace.Preview
deactivate_profileEnds service for a client. Nothing is deleted.In development
delete_profileErases a client for good, for example on a GDPR request.In development
get_brand_styleReads a brand’s colours, fonts and logos.In development
update_brand_styleAdds colours and sets colour roles and fonts.In development
upload_brand_logoSaves a logo and, by default, makes it the one Native uses.In development
create_connect_linkMakes a single-use link the client opens to connect a social account.Preview
get_connect_optionsLists the Pages, organisations, boards or locations a connect can bind to.In development
complete_connectFinishes a connect with the option the client chose.In development
create_postsGenerates 1 to 20 draft posts from one brief, in the background.Preview
get_post_batch_statusReads how a batch is going, and the posts it made.Preview
view_postReads one post exactly as it would publish.Preview
update_postChanges the copy, first comment, time or destinations of a post.Preview
generate_captionRewrites a caption in the brand’s voice.Preview
regenerate_post_imageRenders one picture of a post again, in the background.Preview
get_generation_jobReports how a picture render ended.Preview
approve_postRecords approval of a post’s exact content.Preview
reject_postRecords a rejection with a reason.Preview
schedule_postBooks a post, or some of its destinations, for a time.Preview
reschedule_postsBooks or moves up to 25 posts in one call.Preview
publish_post_nowPublishes now, or retries the destinations that failed.Preview
unschedule_platformWithdraws one destination from its booking.Preview
get_post_statusReports the state of each destination of a post.Preview

The gateway has other tools, such as create_draft, delete_post, generate_image and upload_image. These pages do not cover them yet, and tools/list has their schemas.

What is still in development

#

Partners cannot call the API until the first item below ships. The rest follow it. Native has not set dates for any of them, and will tell partners on the waiting list as each one goes live.

FeatureWhat it gives you
Partner sign-inClient credentials for your backend, and the codes partner_only and partner_not_found.
Approval digestscontentDigest and approval on post status, drafts and batch items, and postId and appUrl on review decisions.
Dry runsdryRun on schedule_post and publish_post_now.
A versioned endpoint/mcp/v1, which keeps its contract while newer versions ship.
Connecting accounts from your own appplatform and returnUrl on create_connect_link, plus get_connect_options and complete_connect.
Brand settingsget_brand_style, update_brand_style and upload_brand_logo.
Ending a clientdeactivate_profile and delete_profile.
Approved content stays as approvedThe codes approval_required and design_changed_after_approval.
Text on picturesslideText on each picture, and rendering from design templates.
Alt textaltText on each picture.
Creating clientscreate_profile, onboarding on get_profile, and the code profile_not_ready.

Calling conventions

#

Calling a tool

#

Every call is an MCP tools/call over Streamable HTTP, with a bearer token in the Authorization header. Authentication shows how to get the token. With the token in hand, a call looks like this:

Calling get_profile

import { Client } from '@modelcontextprotocol/sdk/client/index.js'
import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js'

// token comes from accessToken() on /docs/authentication, brandId from create_profile.
const client = new Client({ name: 'partner-backend', version: '1.0.0' })
await client.connect(
  new StreamableHTTPClientTransport(new URL('https://mcp.native.no/mcp'), {
    requestInit: { headers: { Authorization: `Bearer ${token}` } },
  }),
)

const result = await client.callTool(
  { name: 'get_profile', arguments: { brandId } },
  undefined,
  { timeout: 150_000 }, // some writes take up to about 120 s
)

const data = (result.structuredContent as { data: any } | undefined)?.data
if (result.isError) {
  // data is the error envelope: { ok: false, error: { code, message, retryable } }.
  // A call that breaks the input schema has no envelope, only text in result.content.
  console.error(data?.error?.code ?? result.content, data?.error?.retryable)
} else {
  // data is the tool's payload
  console.log(data.readiness)
}

The token is set when the transport is created, so make a new client when you fetch a new token. Raise the SDK’s request timeout as shown. Otherwise a slow write can time out on your side after it has already succeeded on Native’s. Limits has the numbers.

Success

#

On success, result.structuredContent is always { "data": <payload> }, where the payload is the object the tool’s page describes. result.content[0].text holds the same payload as a JSON string, and isError is absent.

structuredContent from get_profile, shortened

{
  "data": {
    "brandId": "3f6b1c2e-8d4a-4f7e-9b1a-2c5d8e0f4a71",
    "brandName": "Harbour Bakery",
    "isActive": true,
    "readiness": {
      "canGenerate": true,
      "canPublish": false,
      "blockers": [
        "no_channel_connected"
      ]
    }
  }
}

Failure

#

When a tool fails, the result has isError: true, and structuredContent is { "data": <envelope> }. Here schedule_post found a caption too long for Instagram before it contacted any platform:

structuredContent from schedule_post, message shortened

{
  "data": {
    "ok": false,
    "error": {
      "code": "schedule_preflight_failed",
      "message": "Nothing was scheduled: at least one destination will not publish this post as it stands.",
      "retryable": false,
      "blockedDestinations": [
        {
          "platform": "INSTAGRAM",
          "code": "caption_too_long",
          "characterCount": 2410,
          "characterLimit": 2200
        }
      ]
    }
  }
}

The error envelope

  • okbooleanRequired

    Always false.

  • errorobjectRequired

    What went wrong.

    • codestringRequired

      What failed, as a stable code. Branch on this.

    • messagestringRequired

      A sentence Native writes for people. It can change at any time, so show it or log it, but never parse it.

    • retryablebooleanRequired

      Whether the same call, unchanged, could succeed. false means something has to change first.

  • Some codes add fields of their own next to code, such as invalidFields, blockedDestinations, connectedPlatforms, resetDate or jobId. Errors lists each one with the code that uses it.
  • A code the gateway does not recognise arrives as upstream_<status>, with the HTTP status Native’s backend answered, for example upstream_409. A 4xx is not retryable. A 429 or a 5xx is.
  • An argument that breaks a tool’s input schema is refused by the MCP SDK before the tool runs. The result has isError: true and the SDK’s validation message as text, with no envelope. This includes an argument the tool does not take: every tool on these pages refuses unknown arguments. Treat it as a bug in your call.
  • A missing, expired or invalid token is refused at the HTTP level, before any tool runs, so it never gets this envelope. See Authentication.

Ids

#

brandId, post ids, batchId, jobId and platformPostId are opaque strings. Store them as you get them, and do not build or parse them. A few tools, create_connect_link among them, also refuse a brandId that is not a UUID.

Tools that act on a post take its id as id (for example view_post, approve_post and schedule_post) or as postId (for example generate_caption and regenerate_post_image). Each tool’s page says which. view_post and get_post_status return the id as both id and postId, with the same value.

Destination names

#

A post’s destinations are uppercase names from Native’s platform list, such as INSTAGRAM, FACEBOOK or INSTAGRAM_STORIES. You see them in platforms, destinations[].platform and platformResults[].platform.

get_profile is the exception. Its channels[].platform and storyDestinations use lowercase channel names, such as instagram and instagram_stories. Stories use the base Instagram or Facebook connection, so they never appear as channels of their own. The platform input of create_connect_link, which is in development, takes lowercase names too.

Times

#

Send times as ISO 8601 with Z or a numeric offset, for example 2026-11-02T09:00:00+01:00. The gateway converts them to UTC.

Retrying writes

#

The gateway attaches an idempotency key to every write it makes to Native, but each tool call gets a fresh one. If you send the same call again, it is a new attempt and is not deduplicated: a repeated create_posts makes a second batch. Idempotency keys of your own are planned, not available.

So after a timeout or a dropped connection on a write, read the current state before you try again, with get_post_status, get_post_batch_status or view_post. A lost response does not prove that nothing changed. Errors has the retry rules.

Fields and codes you do not know

#

Results can carry more fields than these pages list. New output fields, error codes and enum values can also appear at any time. Ignore the fields you do not use, and handle a code you do not know by its retryable flag.

Versions

#

https://mcp.native.no/mcp always serves the latest version of the tools. A pinned endpoint, https://mcp.native.no/mcp/v1, is in development. Once it ships, partners should call it instead. The same token works on both, so switching versions changes only the URL.

With versioning, the initialize result reports the version you are on, as capabilities.experimental["no.native/partner-api"] with version and latestVersion. When latestVersion is greater than version, a newer contract has shipped.

The policy that comes with it: Native announces a breaking change at least two weeks before it ships, and the previous version keeps working for at least four weeks after. The announcement gives the date the old version shuts down. After that date, its path answers HTTP 404.

Breaking changes are: removing or renaming a tool or a field, tightening an input, changing what a field or code means, removing an enum value from output, changing which error code a failure returns, and changing a default. New tools, new optional inputs, new output fields, new enum values in output and new codes for new failures are not breaking, and can ship at any time.

What is not available yet

#
  • Partner sign-in. Machine-to-machine access is in development. Until it ships, a partner can call no tool at all.
  • Webhooks. They are planned for finished generation, approval changes, publish results and disconnected accounts. Until then, poll.
  • Idempotency keys of your own. Resending a write is a new attempt, so read the current state before you retry.
  • Usage per client. A monthly usage report for each client is planned.

Next steps

#

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