YOUR AI TOOLS. YOUR CONTENT WORKFLOW.

Put your content workflow
within reach of your AI tools.

Connect your AI client through MCP. Let it discover stories, help shape drafts, and work across your editorial workflow—with the permissions you choose.

MCP is included in every plan. REST APIs and webhooks come with it.

endata / developer toolkit

$ connect https://app.endata.com/mcp

MCP tools Streamable HTTP
REST API /api/v1
Signed events HMAC-SHA256

// Your editorial rules travel with your content.

await client.listTools()Conceptual illustration · real requests below
Scoped access Full editorial lifecycle Signed event deliveryOpenAPI 3.1

THE STAR OF YOUR TOOLCHAIN

An AI assistant that can
work with your project.

Move beyond copying text between windows. MCP gives a compatible AI client structured tools: read content, inspect drafts, save revisions, and—with an explicit owner grant—queue publication.

Your AI client, connected.
Interactive MCP preview

tools/list 1 sample tools visible

Draft and publishing tools are hidden from this reader key.

YOU ASK

“Show me the latest articles from my publication.”
THE CLIENT CALLSpublished:read
json
{
  "name": "articles_list",
  "arguments": {
    "publicationId": "PUBLICATION_UUID",
    "input": {
      "limit": 3
    }
  }
}

Selected examples only, not the full tool list or exact response schemas. Permission sets are illustrative. Tool names and arguments follow the MCP contract; no API key, AI model, or network request is used here.

CONNECT / STREAMABLE HTTP

Your project. Connected to your tools.

Connect a compatible MCP client using Streamable HTTP at https://app.endata.com/mcp. Tool schemas come from the same operation registry as the HTTP API.

The app calls this a project. API routes and request fields retain publication and publicationId; use those exact names in code.

Permission-aware discoveryTools outside a key’s scopes are omitted. Every call rechecks authorization.

Explicit write requestsPass publicationId, input, and a requestKey for mutations. Owner grants still apply.

Codex

Add this to ~/.codex/config.toml. Make your project API key available as ENDATA_API_KEY in the environment that starts Codex.

javascript
[mcp_servers.endata]
url = "https://app.endata.com/mcp"
bearer_token_env_var = "ENDATA_API_KEY"

Claude Code

Add this to your project’s .mcp.json. Set ENDATA_API_KEY in the environment before starting Claude Code. Keep the key itself out of configuration files and source control.

javascript
{
  "mcpServers": {
    "endata": {
      "type": "http",
      "url": "https://app.endata.com/mcp",
      "headers": { "Authorization": "Bearer ${ENDATA_API_KEY}" }
    }
  }
}

These examples use the clients’ documented HTTP MCP configuration. Connection and available tools depend on your key’s scopes and project permissions.

Build your own MCP client

javascript
// Node.js with @modelcontextprotocol/sdk installed.
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
import { StreamableHTTPClientTransport } from
  "@modelcontextprotocol/sdk/client/streamableHttp.js";

const client = new Client({ name: "my-endata-tool", version: "1.0.0" });
await client.connect(new StreamableHTTPClientTransport(
  new URL("https://app.endata.com/mcp"),
  { requestInit: { headers: {
    Authorization: `Bearer ${process.env.ENDATA_API_KEY}`,
  } } }
));
try {
  const result = await client.callTool({
    name: "articles_list",
    arguments: {
      publicationId: process.env.ENDATA_PUBLICATION_ID,
      input: { limit: 20 },
    },
  });
  if (result.isError) throw new Error(JSON.stringify(result.content));
  console.log(result.content);
} finally {
  await client.close();
}

This uses the official MCP TypeScript SDK, not an Endata-specific SDK. Configure bearer credentials through your client’s secure connection settings. API-key connections are supported; personal OAuth and client registration are deferred.

01 / CONNECT YOUR PROJECT

From key to content.

Create a project API key in Settings → Developer → API Keys. Start with published:read and run your first request on a server or in your build environment.

01Create a key

An organization owner creates a key for one project.

02Set your environment

Store ENDATA_API_KEY and ENDATA_PUBLICATION_ID privately.

03Read an edition

Fetch approved articles and build your reader experience.

Server-side example
javascript
// Server-side JavaScript. Keep credentials out of browser bundles.
const base = "https://app.endata.com/api/v1";
const publication = process.env.ENDATA_PUBLICATION_ID;
const headers = { Authorization: `Bearer ${process.env.ENDATA_API_KEY}` };
const response = await fetch(
  `${base}/publications/${publication}/articles?limit=20`,
  { headers, cache: "no-store" }
);
if (!response.ok) throw new Error(`Endata returned ${response.status}`);
const { items, editionId, hasMore } = await response.json();
for (const article of items) {
  console.log(article.title, article.slug);
}
Inspect a sample response JSON ↗

Abbreviated example. UUID placeholders represent real IDs returned by your project.

json
{
  "publication": {
    "id": "PUBLICATION_UUID",
    "name": "Industry Brief",
    "slug": "industry-brief"
  },
  "editionId": "EDITION_UUID",
  "items": [
    {
      "id": "ARTICLE_UUID",
      "slug": "a-new-perspective",
      "title": "A new perspective on your industry",
      "summary": "The developments that matter, with your editorial context.",
      "body": "<p>Reviewed article content.</p>",
      "sources": [
        {
          "title": "Original report",
          "url": "https://example.com/report"
        }
      ],
      "disclosure": "Prepared with AI assistance and reviewed by the editorial team."
    }
  ],
  "hasMore": false
}

Keep bearer keys out of browser bundles, public environment variables, URLs, and source control. These examples never ask you to paste a real key into this page.

02 / ACCESS WITH INTENTION

Give each integration its own boundaries.

Keys belong to one project and act as service accounts. Choose only the scopes your integration needs. Expiration is optional; revocation is checked on every request.

published:read

Published articles, authors, and sections. Start here for a website reader.

editorial:read

Read drafts, revision history, research, media, sharing, and publishing status.

editorial:write

Create and revise stories; manage research, sources, images, and story actions.

editorial:approve

Approve an exact story revision.

publishing:manage

Publish, retry batches, stage removals, and send or cancel social shares.

administration:manage

Configure project details, research automation, domains, integrations, and webhooks.

organization:manage

Manage the organization containing the key’s publication.

Scopes, service roles, and plan limits all apply. Consequential actions such as publishing, scheduling, deletion, and administrative changes also require explicit owner unattended grants. Personal OAuth is not available yet.

Publishing, external sharing, and invitations also require the API key creator’s verified email. A 403 with email_unverified means that person must verify their email in the platform before retrying.

03 / THE BUILDING BLOCKS

Explore by what you’re building.

Explore 51 operations across 10 areas. Every operation uses the same POST route structure. The live catalog contains the exact scopes and input schemas.

Social operations support LinkedIn and Bluesky. Connecting an account first requires interactive consent in Distribution. Newsletter integrations collect subscribers; they do not send email campaigns.

Published content

Read published articles and their authors and sections.

POSTarticles_listpublished:read
POSTarticle_readpublished:read
POSTauthors_listpublished:read
POSTsections_listpublished:read

Editorial

Create drafts, inspect history, approve exact revisions, and manage story state.

POSTpublication_readeditorial:read
POSToverview_readeditorial:read
POSTstories_listeditorial:read
POSTstory_readeditorial:read
POSTstory_historyeditorial:read
POSTstory_createeditorial:write
POSTstory_updateeditorial:write
POSTstory_previeweditorial:read
POSTstory_approveeditorial:approve
POSTstory_actioneditorial:write
POSTactivity_listeditorial:read

Research

Configure research, manage sources, inspect runs, and retry candidates.

POSTresearch_readeditorial:read
POSTresearch_runeditorial:write
POSTresearch_configureadministration:manage
POSTresearch_source_saveeditorial:write
POSTresearch_retryeditorial:write

Publishing

Read batch status with editorial:read; publish, retry, or stage removals with publishing:manage.

POSTpublishing_readeditorial:read
POSTpublication_publishpublishing:manage
POSTpublication_retrypublishing:manage
POSTstory_unpublishpublishing:manage

Media

List, upload, import, and crop images with reuse permission and attribution.

POSTmedia_listeditorial:read
POSTmedia_uploadeditorial:write
POSTmedia_importeditorial:write
POSTmedia_cropeditorial:write

Project settings

Manage authors, coverage sections, project details, and custom domains.

POSTtaxonomy_saveadministration:manage
POSTpublication_updateadministration:manage
POSTdomains_listadministration:manage
POSTdomain_addadministration:manage
POSTdomain_removeadministration:manage

Social distribution

Inspect connections and share stories through LinkedIn and Bluesky. Connect accounts in the platform first.

POSTconnections_listadministration:manage
POSTconnection_removeadministration:manage
POSTshare_readeditorial:read
POSTstory_sharepublishing:manage
POSTshare_cancelpublishing:manage

Connected services

Inspect, configure, test, or remove email, analytics, team notification, and other catalog integrations.

POSTintegrations_listadministration:manage
POSTintegration_saveadministration:manage
POSTintegration_testadministration:manage
POSTintegration_removeadministration:manage

Organization

Manage team access, invitations, and projects within plan limits.

POSTteam_readorganization:manage
POSTteam_inviteorganization:manage
POSTinvitation_revokeorganization:manage
POSTmember_updateorganization:manage
POSTpublication_createorganization:manage

Webhooks

Register receivers and manage delivery history and replay.

POSTwebhooks_listadministration:manage
POSTwebhook_createadministration:manage
POSTwebhook_actionadministration:manage
POSTwebhook_replayadministration:manage

API / YOUR PRESENTATION LAYER

Your content is the constant.
The experience is yours.

Use Endata as your editorial backend and build the front end your audience needs. Your website, app, or customer portal can render the same approved articles with its own layout, navigation, and brand.

One response. Your design.
FIELDNOTES / YOUR BRANDTHE INDUSTRY EDITION
01

Perspective

The next chapter for your industry

A closer look at the ideas reshaping how your market works.
Editorial team
02

Industry

Three developments worth watching

The signals your team should have on its radar this week.
Editorial team
03

Research

From emerging research to everyday practice

Putting new findings into context for the people making decisions.
Editorial team

Sample content · Prepared with AI assistance and reviewed by the editorial team.

Same sample article data, three custom layouts. Your application owns the design, routing, and rendering. This preview does not navigate to real stories.

Bring your own stackFetch on your server or at build time. Render article content, sections, authors, and source references with your own components.

Keep your design freedomThe API supplies content, not a prescribed UI. Preserve disclosure and attribution as you shape the reading experience.

04 / CONTENT THAT STAYS CONSISTENT

Read an edition, not a moving target.

Use the article GET routes for content delivery. Pin subsequent pages to the first response’s editionId, then replace your local content only after the entire refresh succeeds.

GET/api/v1/publications/{publicationId}/articles
GET/api/v1/publications/{publicationId}/articles/{slug}

Paginationlimit: 1–100 (default 20). offset: 0–10,000.

Filteringq: up to 200 characters. section: section slug. edition: edition UUID.

javascript
// Pin subsequent pages to the first page's edition.
// Reuse base, publication and headers from the quick start.
const collected = [];
let editionId;
let offset = 0;
while (true) {
  const query = new URLSearchParams({ limit: "100", offset: String(offset) });
  if (editionId) query.set("edition", editionId);
  const response = await fetch(
    `${base}/publications/${publication}/articles?${query}`,
    { headers, cache: "no-store" }
  );
  if (!response.ok) throw new Error(`Refresh failed: ${response.status}`);
  const page = await response.json();
  editionId ??= page.editionId;
  collected.push(...page.items);
  if (!page.hasMore) break;
  offset += page.items.length;
  if (!page.items.length || offset > 10000) {
    throw new Error("Pagination limit reached; preserve the previous export.");
  }
}
// Only replace your previous export after every page succeeds.
// A successful empty edition means the old content should be removed.

Preserve sources, disclosure, and image attribution. Resolve image paths against the project’s public website origin, not app.endata.com. Never send an API key with a media request.

Failed refreshes should preserve your previous export. A successful empty edition should remove old content. Older retained editions can remain readable; staged unpublishing is not historical erasure.

05 / MORE THAN A CONTENT FEED

Build around editorial decisions.

Prepare drafts, save revisions, approve reviewed content, and queue publishing as distinct operations. A write’s request key makes network retries safe without repeating the mutation.

POST/api/v1/publications/{publicationId}/operations/{operationId}
javascript
// Server-side operation helper. Reuse a request key only for identical input.
async function operation(name, input, requestKey) {
  const response = await fetch(
    `${base}/publications/${publication}/operations/${name}`,
    {
      method: "POST",
      headers: {
        ...headers,
        "Content-Type": "application/json",
        "Idempotency-Key": requestKey,
      },
      body: JSON.stringify(input),
    }
  );
  if (!response.ok) throw new Error(`Operation failed: ${response.status}`);
  return response.json();
}
// Use actual IDs from the story you have reviewed.
await operation("story_approve", {
  postId: "STORY_UUID",
  revisionId: "REVIEWED_REVISION_UUID",
}, "approve-story-revision-001");

// Separate, consequential action: requires publishing:manage
// AND an explicit owner unattended grant for the service key.
await operation("publication_publish", {}, "publish-edition-001");
// Publishing is asynchronous. Poll publishing_read or receive events.

This example extends the JavaScript quick start’s base, publication, and headers. Replace UUID placeholders with actual story and revision IDs. A publishing batch includes all eligible approved revisions and staged removals in the publication—not just one story.

Exact revisionsReview the content that will be approved. Handle conflicts by reading and reviewing the latest state.

Asynchronous workResearch and publication return run or batch identifiers. Poll status or consume lifecycle events.

06 / FOLLOW THE EVENT

Press play on the whole flow.

Walk through approval, publishing, and webhook delivery. Switch scenarios to see why a receiver needs retries and duplicate protection.

Local simulation
Approve revisionWaiting
Queue publicationWaiting
Activate editionWaiting
Deliver webhookWaiting
Receive duplicateWaiting
EVENT TRACE0 / 5

Ready. Step through the lifecycle to see how requests become a live edition and a verified event.

Illustrative states, not API response bodies. Assumes the required scopes and owner grants. No network requests. Real deliveries may arrive out of order; retries run on a backoff schedule.

07 / WHEN SOMETHING CHANGES

Verify. Persist. Acknowledge.

Register an HTTPS endpoint under Settings → Developer → Webhooks. Choose events, save the signing secret when it is issued, and verify the exact request body before processing.

Webhook-Id Stable event ID · deduplicate hereWebhook-Delivery-Id Delivery ID · changes on manual replayWebhook-Timestamp Unix seconds · allow five minutesWebhook-Signature v1=hex HMAC-SHA256 digest
javascript
import { createHmac, timingSafeEqual } from "node:crypto";

// Pass the exact, unmodified UTF-8 request body and request headers.
function verifyWebhook(rawBody, headers, secret) {
  const timestamp = headers.get("Webhook-Timestamp");
  const signature = headers.get("Webhook-Signature");
  if (!timestamp || !/^\d+$/.test(timestamp)) return false;
  if (Math.abs(Date.now() / 1000 - Number(timestamp)) > 300) return false;
  if (!/^v1=[a-f0-9]{64}$/.test(signature || "")) return false;

  const expected = createHmac("sha256", secret)
    .update(timestamp + "." + rawBody, "utf8")
    .digest();
  const received = Buffer.from(signature.slice(3), "hex");
  return timingSafeEqual(expected, received);
}
// After verification, parse and validate the event.
// Atomically persist it with a unique constraint on Webhook-Id.
// Return 2xx after persistence; duplicate events should also return 2xx.
// Process asynchronously and reconcile current state through the API.

At least once, not in orderPersist a unique event ID before responding 2xx. Use the API to reconcile current state.

Retries and replayUp to eight attempts over approximately 22 hours. Inspect 30 days of history and replay deliveries.

Events carry resource IDs and transition metadata, not article bodies. “edition.published” signals actual edition activation. Your receiver must trigger its own build or refresh; registering a webhook does not deploy your website.

08 / READY FOR THE REAL WORLD

Make the unhappy path predictable.

Idempotent writes

Use an 8–160 character Idempotency-Key with letters, numbers, periods, underscores, colons, or hyphens. The same operation and input return the original result for seven days. Changed input with the same key returns 409.

Bounded requests

120 integration requests per minute per principal. General JSON requests: 1 MB. HTTP media_upload: up to 14 MB JSON with 10 MB decoded image data. MCP retains the 1 MB limit.

Existing plan limits

Draft allowances, project counts, seats, and approval rules still apply. Integrations are included in every plan; they do not bypass its limits.

Receiver requirements

Use a public HTTPS endpoint on port 443. Redirects are refused and delivery times out after ten seconds. Persist promptly, then process asynchronously.

400Validate input against the operation schema.
401Check that the key is valid and has not expired or been revoked.
403Check scopes, service role, owner grants, and whether the key creator has verified their email.
402An existing plan limit was reached.
409Resolve a changed revision or an idempotency-key conflict.
429Wait until the next minute before retrying.

What will you build
around your content?

Bring your workflow. We’ll help you find the right starting point.

Talk integrations