Developer Portal

Build on tchop

Integrate tchop into your AI workflows with the MCP Server, or call the GraphQL API directly.

GraphQL API — Stable MCP Server — Beta

Two ways to build

One set of credentials, two access paths, full feature coverage. Both paths use the same authentication headers, same operations, and the same permission model.

MCP Server

Install the tchop MCP and call operations as natural-language tools from Claude, Cursor, or any MCP-compatible AI assistant. No code required.

Install MCP →

GraphQL API

POST GraphQL queries directly to your org's endpoint. Full schema access, precise field selection, and tight integration into any codebase.

GraphQL quickstart →

Same foundation: Both paths use identical authentication. Each operation in the API Reference shows both MCP and GraphQL usage.

Get Started

Authentication

Every API request — MCP or GraphQL — requires three authentication headers.

Get your credentials

To get started with the tchop API, contact our support team:
support@tchop.io

Our team will provide you with:

  • x-tchop-webapp-organisation — your org subdomain
  • x-tchop-auth-token — your authentication token
  • x-tchop-api-client-id — your API client ID

Once you have these, you're ready to use both the MCP Server and GraphQL API.

Required headers

x-tchop-webapp-organisationStringYour org subdomain REQUIRED
x-tchop-auth-tokenStringYour authentication token. All permissions derive from this token's role. REQUIRED
x-tchop-api-client-idStringAPI client ID paired with your auth token REQUIRED

Security: Treat the auth token like a password. Never commit it to a repo, paste it in Slack, or include it in screenshots.

Get Started

MCP Server

Connect any MCP-compatible AI assistant to your tchop organisation in minutes — no code, no local setup required.

What is MCP?

MCP (Model Context Protocol) is an open standard that lets AI assistants call external tools and APIs safely and reliably. The tchop MCP server exposes every operation as a named, typed tool. Your AI can discover tools automatically, call them with validated inputs, receive structured responses, and chain operations together — all in natural language.

Compatible clients: Claude.ai, Claude Desktop, Claude Code, Cursor, Windsurf, and any assistant that supports remote MCP (Streamable HTTP).

Your credentials

You need three values from tchop. Contact support@tchop.io to get them.

Org URLStringYour org host, e.g. acme.tchop.io
Auth TokenStringYour personal authentication token
API Client IDStringYour API client identifier

Connect via Claude.ai

Works on Free, Pro, Max, Team, and Enterprise plans. No local install needed.

  1. Go to claude.ai → Settings → Connectors → Add custom connector
  2. Fill in the fields:
Remote MCP server URLhttps://mcp.tchop.live
OAuth Client IDacme.tchop.io:YOUR_API_CLIENT_ID — replace acme.tchop.io with your org URL
OAuth Client SecretYour auth token
  1. Click Add — an authorization page opens
  2. Click Allow access
  3. Done. tchop tools appear in every Claude conversation.

.it orgs: use acme.tchop.it:CLIENT_ID as the OAuth Client ID.

Connect via Claude Code

Run once in your terminal:

claude mcp add --transport http tchop https://mcp.tchop.live \
  --header "x-tchop-org-url: acme.tchop.io" \
  --header "x-tchop-auth-token: YOUR_TOKEN" \
  --header "x-tchop-api-client-id: YOUR_CLIENT_ID"

Connect via Cursor

Add to ~/.cursor/mcp.json (global) or .cursor/mcp.json (project):

{
  "mcpServers": {
    "tchop": {
      "url": "https://mcp.tchop.live",
      "headers": {
        "x-tchop-org-url": "acme.tchop.io",
        "x-tchop-auth-token": "YOUR_TOKEN",
        "x-tchop-api-client-id": "YOUR_CLIENT_ID"
      }
    }
  }
}

Connect via Windsurf

Add to ~/.codeium/windsurf/mcp_config.json:

{
  "mcpServers": {
    "tchop": {
      "serverUrl": "https://mcp.tchop.live",
      "headers": {
        "x-tchop-org-url": "acme.tchop.io",
        "x-tchop-auth-token": "YOUR_TOKEN",
        "x-tchop-api-client-id": "YOUR_CLIENT_ID"
      }
    }
  }
}

Updates

The MCP server runs at mcp.tchop.live. No reinstall or config change ever needed.

  • Bug fixes & behavior changes — take effect immediately, no action needed.
  • New or removed tools — reconnect your connector once to refresh the tool list (2 clicks in settings).
Get Started

GraphQL API

Call the tchop GraphQL API directly with three authentication headers and a standard POST request.

Endpoint

POST https://tchop.io/api/graphql/webapp

⚠️ Always use the root domain — never the org subdomain in the URL. The org is identified via the x-tchop-webapp-organisation header only.

Required headers

HeaderDescription
x-tchop-webapp-organisationYour org subdomain
x-tchop-auth-tokenYour authentication token
x-tchop-api-client-idYour API client ID

Example — curl

curl -X POST "https://tchop.io/api/graphql/webapp" \
  -H "content-type: application/json" \
  -H "x-tchop-webapp-organisation: <your-org>" \
  -H "x-tchop-auth-token: <your-token>" \
  -H "x-tchop-api-client-id: <your-client-id>" \
  -d '{"query":"query { organisation { id name subdomain } }"}'

Environments

SuffixEnvironmentExample endpoint
.tchop.ioProductionhttps://tchop.io/api/graphql/webapp
.tchop.itProduction (EU)https://tchop.it/api/graphql/webapp
Concepts & Features

Organisation

An organisation is the root entity of every tchop deployment — one enterprise customer, one subdomain, one set of users.

It contains all channels, content, settings, and user management in a single isolated container.

tchop hierarchy

OrganisationTop-level container
ChannelAudience segment
MixContent feed
CardContent unit

API naming: In the API, a Mix is called a Story and a Card is called a StoryCard.

Subdomain

Each organisation has a globally unique subdomain used in all API requests as the x-tchop-webapp-organisation header value. The subdomain can be changed via the API at any time, but doing so immediately breaks all existing app URLs, deep links, and push notification routes. Plan subdomain changes carefully.

Environments

SuffixEnvironmentGraphQL endpoint
.tchop.ioProductionhttps://<org>.tchop.io/api/graphql/webapp
.tchop.itProduction (EU)https://<org>.tchop.it/api/graphql/webapp

Basic settings

FieldTypeDescription
nameStringOrganisation display name shown in the app and admin console
subdomainStringURL-safe subdomain slug. Breaking Changing immediately breaks all app URLs and deep links
localeIdStringDefault locale (e.g. en, de, fr)

Security

FieldTypeDescription
appLockEnabledBooleanRequire users to set a PIN lock when opening the app
twoFactorForceEnumEnforce 2FA for all org members. Options: DISABLED, OPTIONAL, REQUIRED_EMAIL, REQUIRED_SMS, REQUIRED_APP
sessionExpirationTimeIntWeb session expiry in seconds. 0 = never expire
sessionExpirationTimeAppIntMobile app session expiry in seconds. 0 = never expire

User onboarding

FieldTypeDescription
invitingIntoDefaultChannelsEnabledBooleanAuto-add new users to default channels on registration
defaultChannelIds[Int]Channel IDs that new users are added to automatically
defaultChannelUserRoleIdIntRole assigned to new users in default channels

Content policies

FieldTypeDescription
restrictDuplicateUrlOfCardsEnumPrevent duplicate URLs from being posted within a window. Options: DISABLED, LAST_1H, LAST_6H, LAST_12H, LAST_24H, LAST_2D, LAST_7D, LAST_30D, ALL_TIME

Chat

FieldTypeDescription
chatPromotionEnabledBooleanShow a banner promoting the chat feature to users who have not used it

App tabs

The tchop mobile app has a configurable bottom navigation bar. Each organisation can set custom labels (with multilingual support per BCP 47 language code) and custom icons (1× and 3× resolution PNG URLs) for each tab. 13 tab types are available:

Tab typeDescription
NEWS_FEEDMain newsfeed — mixed content from all channels the user follows
STORIESMixes feed — all mixes from subscribed channels
STORIES_2STORIES_5Additional mixes feeds (for organisations with multiple content streams)
PINNED_STORYA specific pinned mix, set via pinnedStoryId
PINNED_STORY_2PINNED_STORY_5Additional pinned mix tabs
CHATSChat conversations (direct and group)
PROFILEUser profile page

Configure tabs via update_organisation or update_channel using the appTabs array. Each entry requires: action.type (tab type enum), iconUrl (valid https:// URLs — x1 PNG 24×24 Android, x3 SVG 72×72 iOS/WebApp), and optional title array. Org update: appTabsDeepUpdate: true (default) merges; false replaces all tabs then pushes to channels. Channel update: API always replaces the full tab list — MCP transparently re-fetches all existing tabs and re-sends them, so you can update a single tab without clobbering others.

Icon auto-pipeline: Use the MCP icon_name param instead of manual URLs. Pass a keyword (e.g. "newspaper") or Iconify ID (e.g. "lucide:newspaper") — MCP searches Lucide/Heroicons/Material/Phosphor, fetches SVG 72×72, converts to PNG 24×24, uploads both to mxz-assets S3, and fills iconUrl.x1 + iconUrl.x3 automatically. Requires AWS_ACCESS_KEY_ID + AWS_SECRET_ACCESS_KEY in the server environment. When only icon_name is provided (no label_en/label_de), the MCP automatically fetches and preserves existing tab titles — no need to re-supply labels. If the upload fails, a clear "Icon pipeline error" message is returned with fix steps.

Supported languages: en (English), de (German).

Permissions (read-only)

FieldDescription
roleThe authenticated user's role in this organisation
uuidImmutable organisation UUID
idImmutable integer organisation ID
createdAtOrganisation creation timestamp

User roles

RoleScopeDescription
StaffCross-orgInternal tchop staff. Can create and delete organisations.
Org OwnerOrganisationFull control of the organisation including all settings and destructive operations.
Org AdminOrganisationManage org settings, channels, and users.
Channel AdminChannelFull control of specific channels they administer.
EditorChannelCreate, edit, and publish content in assigned channels.
Editor LimitedChannelCreate and edit drafts. Cannot publish.
ReaderChannelConsume content. No write operations.
Concepts & Features

Channels

A Channel is a content space within an Organisation — a dedicated section where specific content is published and specific users are given access.

Think of channels as topic areas: "Company News", "HR Updates", "Sales Team", "Product Releases". Users are invited at the channel level — one user can be a Reader in one channel and an Editor in another.

tchop hierarchy

OrganisationTop-level container
ChannelAudience segment
MixContent feed
CardContent unit

Channel URL

Each channel gets a unique URL combining the channel and org subdomains:

https://<channel-subdomain>-<org-subdomain>.tchop.io/webapp

Example: https://company-news-acme.tchop.io/webapp

Note: Channel subdomains can include hyphens (e.g. company-news) — unlike org subdomains which are alphanumeric only.

User roles per channel

Access is always scoped per channel. The same user can have different roles in different channels.

RoleList channelsCreate channelUpdate channelDelete channel
Staff / Org Owner / Org AdminAll channelsAny channel
Channel AdminOwn channelsOwn channels only
Editor / Editor Limited / ReaderOwn channels

Basic settings

nameStringDisplay name visible to all users with access (e.g. "Company News").
subdomainStringURL identifier. Lowercase letters, numbers, and hyphens. ⚠ Changing breaks all existing URLs and deep links immediately.
localeen / deChannel language, independent of the organisation's default. Each channel can have a different language.
imageIdIntOptional cover image ID. Displayed in the app navigation.

Visibility and status

isArchivedBooleanWhen true, hides the channel from users. Content is fully preserved — unarchive at any time. Default: false. Use instead of delete for temporary changes.
isDefaultBooleanNew users are auto-added to default channels when they join the org (requires invitingIntoDefaultChannelsEnabled: true on the org).
positionInt0-indexed position in the org's channel sidebar. Position 0 = first. Other channels shift automatically when position changes.

Content policies

restrictDuplicateUrlOfCardsEnumPrevents editors from posting cards with the same URL twice within a configurable time window. Can differ from the org-level setting.
Options: NEVER · ONE_HOUR · THREE_HOURS · SIX_HOURS · HALF_DAY · ONE_DAY · TWO_DAYS · THREE_DAYS · ONE_WEEK

App tabs (channel-level)

Channels can override the organisation's mobile app tab configuration — different tab labels and icons per channel. Same 13 tab types available: NEWS_FEED, STORIES (×5), PINNED_STORY (×5), CHATS, PROFILE.

Permissions (read-only)

The permissions object returned by list_channels shows what the current user can do in that channel:

storyAllowToCreateBooleanCan create mixes in this channel.
eventAllowToCreateBooleanCan create events.
chatGroupAllowToCreateBooleanCan create group chats.
channelPushNotificationAllowToCreateBooleanCan send push notifications to this channel.
channelInAppMessageAllowToCreateBooleanCan send in-app messages to this channel.
channelUserCuratorManagementAllowToCreateBooleanCan manage editor/admin users.
channelUserReaderManagementAllowToCreateBooleanCan manage reader users.
Concepts & Features

Mixes

A Mix (also called Story, Topic, Section, or Feed) is a content container inside a channel. Every card in tchop lives inside a mix. Mixes organise content into themed sections — for example "Company News", "HR Updates", "Events".

tchop hierarchy

OrganisationTop-level
ChannelAudience
MixContent feed
CardContent unit

Mix types

TypeDescription
STANDARDEditors publish cards directly into this mix.
READONLYConnected mix that automatically pulls cards from a source mix in another channel. Used for content syndication.

Lifecycle

Mixes have two statuses only: UNPUBLISHED (draft — hidden from readers) and PUBLISHED (visible). Scheduling is not supported.

Roles & permissions

RoleListCreateUpdateDeletePost cards
Org Admin / Staff
EditorOwn channelOwn mixesOwn mixes
Editor LimitedOwn channelIf enabled on mix
ReaderOwn channelIf enabled on mix

Display (Output)

Controls where a mix appears in the app. A mix can appear in multiple outputs simultaneously.

Web App News FeedBooleanShows mix in the web app's main news feed.
Mobile App News FeedBooleanShows mix in the mobile app news feed.
Mix List 1–5Boolean ×5Shows mix under one of 5 Mix List tabs in app navigation.
Pinned 1–5EnumPins mix in a dedicated slot. Scope: CHANNEL (visible within channel) or ORGANISATION (org-wide).
Horizontal ModuleInt (position)Shows mix in a horizontal scrollable row. Position ≥1.

Interaction

allowCommentsBooleanUsers can comment on cards. Default: on.
allowCardReactionsBooleanUsers can like/react to cards. Default: on.
allowSharingBooleanUsers can share cards externally. Default: on.
allowCardMentionsBooleanUsers can @mention others in cards. Default: off.
showAllAuthorsBooleanShow all card authors, not just primary. Default: off.
allowAccessToEditorLimitedBooleanEditor Limited role can post cards into this mix.
allowAccessToReaderBooleanReader role can post cards into this mix.

Display style

teaserStyleEnumHow cards look in the feed.
STANDARD · SMALL_WITH_TEXT · SMALL_WITHOUT_TEXT · BIG_WITHOUT_TEXT
backgroundColorHexCustom background color behind card content. E.g. #fef3e2.
highlightingCommentsEnumWhich comment is featured under a card.
MOST_LIKEABLE · FIRST · MANUAL
displayItemUpdatedTimeEnumTimestamp shown on cards.
POSTED_TIME (original publish time) · UPDATED (last edit time)

Content policies

cardOrderEnumSort order for cards in the mix feed.
MANUAL (default) — editor-controlled position · POSTED_TIME — newest first, same as news feed.
Standard mixes only. Read-only mixes always inherit order from source.
cardLimitEnumCap total cards in this mix.
UNLIMITED · 1,000 · 2,000 · 5,000 · 10,000 · 20,000
shouldRestrictDuplicateUrlOfCardsInTimeFrameEnumBlock duplicate card URLs within a window.
NEVER · ONE_HOUR · THREE_HOURS · SIX_HOURS · HALF_DAY · ONE_DAY · TWO_DAYS · THREE_DAYS · ONE_WEEK
shouldSyncItemPositionsOnConnectedStoriesBooleanSync card order with connected read-only mixes. Default: off.
shouldUpdatePostedTimeByCommentsBooleanCard's posted time updates when a new comment is added. Default: off.

Auto push notifications

When enabled, a push notification is sent automatically when a card is published in this mix.

enabledBooleanTurn auto-push on or off. Default: off.
titlePrefixStringPrepended to every push title. E.g. [BREAKING], [UPDATE].
titleDefaultStringFallback push title used when the card has no title.
messageDefaultStringFallback push message used when the card has no body text.

Note: Push settings are saved correctly but return null in the storyCreate GraphQL response. Use storyUpdate or storiesByChannelId to read them back.

Tags

TypeAPI fieldVisible to usersUse case
EXTERNALexternalTagsId✅ YesCategory labels shown in app
INTERNALinternalTagsId❌ NoInternal filtering, automation
EXTERNAL_STATUSexternalStatusTagsId✅ YesEditorial workflow status e.g. "Approved"

Read-only mix sync

A read-only mix connects to a source mix and automatically mirrors its content:

source.storiesId[Int]Source mix IDs to connect to.
source.fetchLastCardsIntPull only the latest N cards from the source (create only).
copyFromSourceBooleanCopy title, subtitle and image from source mix. If source has no image, target image is cleared.

Note: Source mix must be in a different channel than the read-only mix being created. Same-channel source returns connected-story-not-found-error.

Cards cannot be created in read-only mixes. Attempting create_card (or post_card_as_copy / post_card_as_sync) on a read-only mix returns StoryReadOnlyAccessError. Read-only mixes receive content automatically from their source mix — new cards must be posted to the source (standard) mix instead.

ImageFile response type. The image { ... } field on mixes (and cards) returns an ImageFile type with only these fields: id, url, rightholder, statusCopyrightId. The fields imageRightholder and imageStatusCopyright do not exist — querying them causes a server-side schema error. Use rightholder (not imageRightholder).

Concepts & Features

Cards

A Card (also called: content, post, item) is the atomic unit of content in tchop. Every piece of content published in a mix is a card. Cards can contain text, media, links, polls, events, or rich posts.

tchop hierarchy

OrganisationTop-level
ChannelAudience
MixFeed
CardContent unit

Card status lifecycle

DRAFTED ──────────────► PUBLISHED ──► UNPUBLISHED
                            ▲
SCHEDULED ─(auto at time)───┘
StatusMeaning
PUBLISHEDLive and visible to users. Default when creating.
DRAFTEDCreated but never published — working state, only editors see it.
SCHEDULEDWill go live automatically at the scheduled time. Set via scheduledAt in MCP tools (maps to postingTime in API).
UNPUBLISHEDWas live, now taken offline. Different from DRAFTED.

Key distinction: DRAFTED = never published (new working draft). UNPUBLISHED = was published, then taken offline. The MCP treats these as different states.

Mix type requirement

Cards can only be created in STANDARD mixes. READONLY mixes receive cards automatically from a source mix and do not accept direct creation.

Access rules

OperationReaderEditor LimitedEditorOrg Admin+
Read published cards
Create cardMix settingMix setting
Update / Delete own cardAuthorAuthor
Publish / Schedule
Pin card

Card types

11 types available. Click a type to see its full documentation.

Type Description
Text Short text post. Plain text with optional subtitle. No image required. View details
Image Single image or gallery (up to 20). Per-image captions and copyright metadata. View details
Video Video file uploaded to tchop. Optional cover image. View details
Audio Audio file or Apple Podcast episode. Auto-extracts from Apple Podcast RSS. View details
PDF PDF document card. Optional cover image. View details
Article Curated web link. Title, source name, optional teaser image. View details
Social Embeds social media posts. Supports X, Instagram, TikTok, Facebook, LinkedIn, Bluesky. View details
Long post Rich text native article. Editor.js content blocks — write in Markdown, auto-converted. View details
Thread Thread container card. Title only — replies nest inside the thread. View details
Poll Survey or quiz card. 2–12 options, anonymous voting, multiple choice. View details
Event Links to a pre-created tchop event. Displays event details inline in the feed. View details

URL auto-detection (MCP)

When creating a card without specifying cardType, the MCP inspects the url field:

Apple Podcast URLAUDIOURL contains podcasts.apple.com → RSS extraction flow → AUDIO card (fallback: ARTICLE)
Social platform URLQUOTEX/Twitter, Instagram, TikTok, Facebook, Bluesky → QUOTE card. LinkedIn → ARTICLE (storyCardParseUrl returns article fields for LinkedIn).
Any other URLARTICLEFalls back to ARTICLE card automatically

Apple Podcast flow

1

iTunes lookup

Extract podcast ID from URL → fetch itunes.apple.com/lookup → get RSS feed URL

2

RSS parse

Fetch and parse RSS feed → extract episode list with audio URLs and cover images

3

Episode match

Match best episode by GUID, episode ID, or fuzzy title match

4

Upload & post

Upload audio file and cover image to tchop → post AUDIO card

5

Fallback

If audio upload fails → post ARTICLE card linking to Apple Podcast URL

Card settings

Per-card overrides for mix defaults:

allowCommentBooleanEnable/disable comments on this card
allowReactionBooleanEnable/disable reactions (likes)
allowSharingBooleanEnable/disable external sharing
allowDisplayAuthorBooleanShow or hide author name
shouldUpdatePostedTimeByCommentsBooleanBump posted timestamp when new comments are added
backgroundColorHexBackground color behind card content, e.g. #FF5733

Tags on cards

TypeMCP paramVisible to readersLimit
EXTERNALexternalTagIds✅ YesMany per card
INTERNALinternalTagIds❌ NoMany per card (Editor+ only)
EXTERNAL_STATUSstatusTagId✅ YesOne per card

Pin targets

storytargetPins card at the top of the mix feed. Visible to all readers of that mix.
newsfeedtargetPins card at the top of the organisation-wide newsfeed.

Toggle: Calling pin_card on an already-pinned card unpins it.

Card position & sort order

Each card has a position within its mix. Position is controlled by the mix's cardOrder setting:

Mix cardOrderHow order is determinedCan reorder?
MANUAL (default)Editor-controlled. Cards stay where placed via set_card_position.✅ Yes
POSTED_TIMENewest first automatically. Same as a news feed.❌ No — position is read-only

Key rule: set_card_position only works on MANUAL sort mixes. Calling it on a POSTED_TIME mix has no effect — the API normalizes or silently ignores the position. Check cardOrder on the mix before reordering.

The cardOrder setting is configured on the mix — see Mixes → Features → Content policies.

Copy vs Sync

post_card_as_copypost_card_as_sync
Independent from source✅ Yes❌ No
Source edits propagate❌ No✅ Yes
Use caseDistribute content onceKeep content in sync across mixes

Caption vs Copyright

FieldGraphQLRule
Captiongallery[].titlePlain text, no © symbol — backend does not auto-add it here
Copyrightgallery[].image.rightholderCopyright holder name — backend auto-adds © on display

Applies equally to IMAGE, VIDEO, AUDIO, PDF. Do not prefix gallery[].title with ©.

PDF vs AUDIO — media readiness

PDFAUDIO
Available after upload✅ Immediately (status: "OK")❌ Transcoding in progress
Auto-thumbnailpdf.thumb URLN/A
URL populatedpdf.url immediatelyaudio.url = null until done
Skip unprocessedNot neededUse onlyCompletedMedia: true

Type conversion (convert_card)

Convert any existing card to a different type in place — same card ID, no new card created.

FromToWhat happens
Short post ↔ short postEDITORIAL / IMAGE / VIDEO / AUDIO / PDFFully interconvertible. IMAGE auto-fetches full gallery from article URL; other media types need a URL.
ARTICLE → short postIMAGEURL re-parsed via storyCardParseUrlAsNative — gets all images, not just teaser. No extra fields needed.
ARTICLE → short postEDITORIALArticle abstract used as body text automatically. No extra fields needed.
ARTICLE → long postPOSTURL re-parsed via storyCardParseUrlAsPost — full Editor.js content blocks + teaser image. No markdown needed.
POST → short postEDITORIALContent blocks permanently lost — irreversible.

See convert_card in API Reference for full parameter details.

Card Type

Text

API type: EDITORIAL

Short text post. Plain text content with optional subtitle and source attribution. No image or media required. Ideal for announcements, updates, and short commentary.

Required fields

storyIdrequirednumberMix ID (must be a STANDARD mix). Use list_mixes to find IDs.
textrequiredstringBody text. Min 1 char, max 20000 chars.

Optional fields

subHeadlinestringSubtitle line displayed below the main text. Max 360 chars.
sourceNamestringSource attribution (e.g. "Editorial Team"). Max 160 chars.
headlinestringComment text shown above the card in the feed (not the card title). Only set when the user explicitly says "comment". Max 360 chars.
backgroundColorhexBackground color behind card content. Example: #FF5733. Max 7 chars.
statusenumPUBLISHED (default) · DRAFTED · SCHEDULED · UNPUBLISHED
scheduledAtstringISO 8601 datetime. Required when status=SCHEDULED. Example: 2026-07-01T09:00:00Z
allowCommentbooleanEnable/disable comments on this card. Defaults to mix setting.
allowReactionbooleanEnable/disable reactions on this card. Defaults to mix setting.
allowSharingbooleanEnable/disable external sharing. Defaults to mix setting.
allowDisplayAuthorbooleanShow/hide author name. Defaults to mix setting.
shouldUpdatePostedTimeByCommentsbooleanBump posted timestamp when new comments are added.
externalTagIdsnumber[]External (visible) tag IDs to assign.
internalTagIdsnumber[]Internal (hidden) tag IDs to assign. Editor+ only.
statusTagIdnumberStatus tag ID. One per card — for editorial workflow states.
mentionedUserIdsnumber[]User IDs to mention in this card.
categoryIdnumberCard category ID.
localestringCard language: en or de. Defaults to the mix language.

GraphQL fields object

editorialFields: {
  text: "string",            // required
  headline: "string",        // optional — omit if not provided (empty string rejected)
  subHeadline: "string",     // optional
  sourceName: "string",      // optional
  styles: {                   // optional
    backgroundColor: "#RRGGBB"
  }
}

Content type (read back)

StoryCardEditorialContent {
  text
  headline
  sourceName
}

MCP example

{
  "name": "create_card",
  "arguments": {
    "storyId": 61218,
    "cardType": "EDITORIAL",
    "text": "This week's company update is live.",
    "subHeadline": "Internal Communications"
  }
}

Notes

headline must never be an empty string. Omit the field entirely when not provided — the API rejects "".

backgroundColor is behind the card content, not the text. Useful for branded announcement cards.

Card Type

Image

API type: IMAGE

Single image or gallery of up to 20 images. Images are fetched from URLs and uploaded to tchop. Each image supports individual captions, rightholder credits, and copyright status metadata.

Required fields

storyIdrequirednumberMix ID (must be STANDARD).
imageUrlsrequiredstring[]Array of image URLs (1–20). Publicly accessible URLs — fetched and uploaded to tchop. 1 URL = image card. 2–20 URLs = gallery card.

Optional fields

imageCaptionsstring[]Caption per image. Parallel array matched by index to imageUrls. Max 500 chars each. Plain text — do NOT add © prefix; backend auto-adds © on display for rightholders.
imageRightholdersstring[]Rightholder credit per image (copyright holder name, e.g. "Reuters"). Backend automatically prepends © on display. Parallel array. Max 200 chars each.
imageCopyrightStatusesenum[]Copyright status per image. Values: CC · LICENSED · SUBLICENSED · UNKNOWN. Parallel array.
textstringOverall description or caption for the card. Max 20000 chars.
subHeadlinestringSubtitle line. Max 360 chars.
sourceNamestringSource attribution. Max 160 chars.
headlinestringComment text shown above the card. Only set when user explicitly says "comment". Max 360 chars.
backgroundColorhexBackground color, e.g. #FFFFFF.
statusenumPUBLISHED (default) · DRAFTED · SCHEDULED · UNPUBLISHED
scheduledAtstringISO 8601 datetime. Required when status=SCHEDULED.
allowComment / allowReaction / allowSharing / allowDisplayAuthor / shouldUpdatePostedTimeByCommentsbooleanPer-card overrides for mix settings.
externalTagIds / internalTagIds / statusTagIdnumber[]Tag assignment.
mentionedUserIds / categoryIdnumber([])User mentions and category.
localestringCard language: en or de. Defaults to the mix language.

GraphQL fields object

imageFields: {
  gallery: [                    // required, 1–20 items
    {
      image: { id: Int! },       // uploaded image ID
      caption: "string",         // optional
      rightholder: "string",     // optional
      copyrightStatus: "CC"       // CC | LICENSED | SUBLICENSED | UNKNOWN
    }
  ],
  headline: "string",            // optional — omit if not set
  text: "string",                // optional
  subHeadline: "string",         // optional
  sourceName: "string",          // optional
  styles: { backgroundColor: "#RRGGBB" }   // optional
}

Content type (read back)

StoryCardImageContent {
  headline
  sourceName
  text
}

MCP examples

// Single image
{
  "name": "create_card",
  "arguments": {
    "storyId": 61218,
    "cardType": "IMAGE",
    "imageUrls": ["https://example.com/photo.jpg"],
    "imageCaptions": ["Annual conference keynote"],
    "imageRightholders": ["© Company Photography"],
    "imageCopyrightStatuses": ["LICENSED"]
  }
}

// Gallery (3 images)
{
  "name": "create_card",
  "arguments": {
    "storyId": 61218,
    "cardType": "IMAGE",
    "imageUrls": [
      "https://example.com/img1.jpg",
      "https://example.com/img2.jpg",
      "https://example.com/img3.jpg"
    ],
    "imageCaptions": ["Morning session", "Panel discussion", "Networking"]
  }
}

Notes

1 image = image card. 2–20 images = gallery card. The card type in the API response is always IMAGE regardless.

Image resolution: storyCardParseUrl first, upload as fallback. Each URL is first resolved via storyCardParseUrl(url)StoryCardImageParsedUrl.gallery[0].image.id. Only if that returns no image ID does the MCP fall back to POST /api/fs/upload/image. Provide publicly accessible URLs — private or auth-gated URLs fail at both steps.

Parallel arrays. imageCaptions[0] applies to imageUrls[0], etc. Arrays can be shorter than imageUrls — missing entries use empty values.

Updating caption or copyright without changing the image (update_card). Pass imageCaption and/or imageRightholder without a new imageUrl — the MCP fetches the existing image ID from the current card and rebuilds the gallery with the new metadata. Existing caption and rightholder values are preserved unless explicitly overridden.

Card Type

Video

API type: VIDEO

Video file uploaded to tchop. Provide a publicly accessible direct video URL — the MCP fetches it and uploads via multipart form-data. Supports an optional cover/thumbnail image and a description.

Required fields

storyIdrequirednumberMix ID (must be STANDARD).
videoUrlrequiredstring (URL)Direct video file URL (mp4, webm, etc.). Must be publicly accessible.

Optional fields

teaserImageUrlstring (URL)Cover/thumbnail image URL. Uploaded to tchop. If omitted, platform default thumbnail is used.
teaserRightholderstringRightholder for the teaser image. Max 200 chars.
teaserCopyrightStatusenumCopyright status for teaser image: CC · LICENSED · SUBLICENSED · UNKNOWN
useDefaultThumbbooleanUse platform default thumbnail (auto-generated from video). Default: true. Pass false only to suppress when not providing a custom teaserImageUrl. Ignored when teaserImageUrl is provided.
textstringCard body text (shown below headline). Max 20000 chars.
videoTitlestringCaption shown with the video player — stored as gallery[0].title. Plain text, no © symbol.
subHeadlinestringSubtitle. Max 360 chars.
sourceNamestringSource attribution. Max 160 chars.
headlinestringComment text above the card. Only set when user explicitly says "comment". Max 360 chars.
backgroundColorhexBackground color, e.g. #000000.
status / scheduledAtenum / stringCard status and schedule time.
allowComment / allowReaction / allowSharing / allowDisplayAuthorbooleanPer-card overrides for mix settings.
externalTagIds / internalTagIds / statusTagId / mentionedUserIds / categoryIdvariousTags, mentions, category.
localestringCard language: en or de. Defaults to the mix language.

GraphQL fields object

videoFields: {
  gallery: [                           // required, exactly 1 item
    {
      video: { id: Int! },               // uploaded video ID
      image: { id: Int },               // optional teaser image
      title: "string",                  // optional
      useDefaultThumb: Boolean,          // optional
      imageRightholder: "string",       // optional
      imageCopyrightStatus: "LICENSED"   // optional
    }
  ],
  headline: "string",                   // optional
  subHeadline: "string",                // optional
  sourceName: "string",                 // optional
  styles: { backgroundColor: "#RRGGBB" }  // optional
}

gallery is NonNull on update: videoFields.gallery: [StoryCardToCreateVideoGallery!]! — must always be included, even for headline-only updates. Fetch current card first to get existing gallery[0].video.id.

Content type (read back)

StoryCardVideoContent {
  headline
  sourceName
  text
  gallery { video { id } image { id rightholder statusCopyrightId } title }
}

MCP example

{
  "name": "create_card",
  "arguments": {
    "storyId": 61218,
    "cardType": "VIDEO",
    "videoUrl": "https://example.com/keynote.mp4",
    "teaserImageUrl": "https://example.com/keynote-thumb.jpg",
    "text": "Annual Keynote 2026",
    "sourceName": "Company TV"
  }
}

Notes

Upload mechanism: Video file is fetched from the URL and uploaded to tchop via multipart/form-data. The URL must serve a direct video file, not an HTML page.

Default thumbnail: If teaserImageUrl is omitted, useDefaultThumb: true is sent automatically — the platform generates a thumbnail from the video. To use a custom cover image instead, pass teaserImageUrl.

Card Type

Audio

API type: AUDIO

Audio file or podcast episode. Provide a direct audio URL (mp3, wav, aiff) or an Apple Podcast episode URL — the MCP automatically extracts the episode from the RSS feed and uploads it.

Required fields

storyIdrequirednumberMix ID (must be STANDARD).
audioUrlrequiredstring (URL)Direct audio file URL or Apple Podcast episode URL (podcasts.apple.com/...).

Optional fields

teaserImageUrlstring (URL)Cover image URL. For Apple Podcast URLs this is auto-populated from episode/channel artwork.
teaserRightholderstringCopyright holder name for cover image (e.g. "Deutschlandfunk"). Backend auto-adds © on display — do not prefix with ©. Max 200 chars.
teaserCopyrightStatusenumCC · LICENSED · SUBLICENSED · UNKNOWN
useDefaultThumbbooleanUse platform default thumbnail. Default: true. Pass false only to suppress when not providing teaserImageUrl. Ignored when teaserImageUrl is provided.
textstringCard body text (shown below headline). For podcast episodes: use the episode's full description — detailed is good, but never repeat the title/headline here, and drop sponsor or feedback boilerplate lines. Max 20000 chars.
audioTitlestringCaption shown with the audio player — stored as gallery[0].title. Plain text, no © symbol.
subHeadlinestringSubtitle. For podcast episodes: leave empty — podcast name is already shown via sourceName. Max 360 chars.
sourceNamestringSource attribution (e.g. "Spotify Podcasts"). Max 160 chars.
headlinestringComment text above card. Max 360 chars.
status / scheduledAt / allowComment / allowReaction / allowSharing / externalTagIds / etc.variousStandard card settings — same as other types. Also: locale (en/de, defaults to mix language).

GraphQL fields object

audioFields: {
  gallery: [                           // required, exactly 1 item
    {
      audio: { id: Int! },               // uploaded audio ID
      image: { id: Int },               // optional cover image
      title: "string",                  // optional
      useDefaultThumb: Boolean,          // optional
      imageRightholder: "string",       // optional
      imageCopyrightStatus: "LICENSED"   // optional
    }
  ],
  headline: "string",                   // optional
  subHeadline: "string",                // optional (podcast name)
  sourceName: "string",                 // optional
  text: "string"                         // optional (description)
}

Apple Podcast flow

When audioUrl starts with podcasts.apple.com, the MCP runs a multi-step extraction:

1

Extract IDs

Parse podcast ID (/idXXXXXX) and optional episode ID (?i=XXXXXXXXX) from the URL.

2

iTunes lookup

Fetch itunes.apple.com/lookup?id=... to get RSS feed URL and podcast title.

3

RSS parse

Fetch and parse the RSS feed — extract episode list with audio URLs and cover images.

4

Episode match

Match best episode by GUID, episode ID, or fuzzy title match against iTunes metadata.

5

Upload

Upload audio file (and cover image if available) to tchop → post as AUDIO card.

6

Fallback

If audio upload fails → fall back to ARTICLE card linking to the Apple Podcast URL. Response includes _note field.

MCP example

// Direct audio file
{
  "name": "create_card",
  "arguments": {
    "storyId": 61218,
    "cardType": "AUDIO",
    "audioUrl": "https://cdn.example.com/episode-42.mp3",
    "teaserImageUrl": "https://cdn.example.com/podcast-cover.jpg",
    "text": "Episode 42: The Future of Work",
    "subHeadline": "Company Podcast"
  }
}

// Apple Podcast URL (auto-extracts episode)
{
  "name": "create_card",
  "arguments": {
    "storyId": 61218,
    "audioUrl": "https://podcasts.apple.com/podcast/id123456789?i=1000567890"
  }
}

gallery is NonNull on update: audioFields.gallery: [StoryCardToCreateAudioGallery!]! — must always be included, even for headline-only updates. Fetch current card first to get existing gallery[0].audio.id.

Content type (read back)

StoryCardAudioContent {
  headline
  sourceName
  gallery { audio { id } image { id rightholder statusCopyrightId } title }
}
Card Type

PDF

API type: PDF

PDF document card. The PDF is fetched from the URL and uploaded to tchop via multipart form-data. Supports an optional cover image and copyright metadata.

Required fields

storyIdrequirednumberMix ID (must be STANDARD).
pdfUrlrequiredstring (URL)Direct PDF file URL. Must be publicly accessible.

Optional fields

teaserImageUrlstring (URL)Cover image URL for the PDF card.
teaserRightholderstringCopyright holder name for cover image (e.g. "Hamburg.de"). Backend auto-adds © on display. Max 200 chars.
teaserCopyrightStatusenumCC · LICENSED · SUBLICENSED · UNKNOWN
useDefaultThumbbooleanUse platform default thumbnail. Default: true. Pass false only to suppress when not providing teaserImageUrl. Ignored when teaserImageUrl is provided.
textstringCard body text (shown below headline). Max 20000 chars.
pdfTitlestringCaption shown with the PDF — stored as gallery[0].title. Plain text, no © symbol.
subHeadlinestringSubtitle. Max 360 chars.
sourceNamestringSource attribution. Max 160 chars.
headlinestringComment text above the card. Max 360 chars.
status / scheduledAt / allowComment / allowReaction / allowSharing / externalTagIds / etc.variousStandard card settings. Also: locale (en/de, defaults to mix language).

GraphQL fields object

pdfFields: {
  gallery: [                              // required, exactly 1 item
    {
      pdf: {
        id: Int!,                         // uploaded PDF ID
        rightholder: "string",           // write-only — not returned in response
        statusCopyrightId: "LICENSED",   // CC | LICENSED | SUBLICENSED | UNKNOWN
        useDefaultThumb: Boolean          // optional
      },
      image: { id: Int },                // optional cover image
      title: "string"                    // optional caption (no © symbol)
    }
  ],
  headline: "string",                    // optional
  subHeadline: "string",                 // optional
  sourceName: "string",                  // optional
  text: "string"                          // optional description
}

gallery is NonNull on update: pdfFields.gallery: [StoryCardToCreatePdfGallery!]! — must always be included, even for headline-only updates. Fetch current card first to get existing gallery[0].pdf.id. Note: pdf.rightholder is write-only — cannot be read back to preserve.

Content type (read back)

StoryCardPdfContent {
  headline subHeadline sourceName text
  gallery {
    id title
    pdf { id url status thumb originalFilename }  // status=OK immediately; rightholder write-only
    image { id url rightholder }                  // cover image
  }
}

MCP example

{
  "name": "create_card",
  "arguments": {
    "storyId": 61218,
    "cardType": "PDF",
    "pdfUrl": "https://example.com/annual-report-2026.pdf",
    "teaserImageUrl": "https://example.com/report-cover.jpg",
    "text": "Annual Report 2026",
    "sourceName": "Finance Department"
  }
}

Notes

Upload mechanism: PDF is fetched and uploaded via multipart/form-data. Provide a direct PDF file URL, not an HTML page linking to a PDF.

PDF rightholder write-only: pdf.rightholder is accepted on input but not returned in the response. Cover image rightholder (image.rightholder) is returned. To add copyright on the PDF document itself, use pdfFields.gallery[].pdf.rightholder on create/update.

No transcoding delay: Unlike AUDIO cards, PDFs are available immediately — pdf.status = "OK" and pdf.thumb (auto-generated thumbnail) are populated right after upload.

Card Type

Article

API type: ARTICLE

Curated web link card. Share any article or webpage with a title, source name, and optional teaser image. Ideal for linking to external content while keeping users in the app. When using URL auto-detection, any non-social URL is automatically routed to this type.

Required fields

storyIdrequirednumberMix ID (must be STANDARD).
urlrequiredstring (URL)The article URL. Must be a valid URI. When using MCP, title and source are auto-detected from this URL — you only need to provide url.
titlerequired*stringArticle title. Max 160 chars. *Auto-detected via storyCardParseUrl or OG meta tags when using MCP — provide explicitly to override.
source or sourceNamerequired*stringSource publication name (e.g. "The Guardian"). Must not be empty. Max 160 chars. *Auto-detected when using MCP.

Optional fields

descriptionstringArticle abstract or description. Max 700 chars.
imageUrlsstring[]Teaser image URL(s). First URL resolved via storyCardParseUrl(imageUrl)StoryCardImageParsedUrl.gallery[0].image.id. Never calls uploadImage for articles.
imageRightholdersstring[]Copyright holder per image (parallel array to imageUrls). Stored on gallery[0].image.rightholder. Image-tied — cleared when image is replaced. Note: update_card uses singular imageRightholder instead.
authorstringArticle author name. Max 60 chars.
teaserStyleenumTeaser image layout (card-level — preserved even when image changes): STANDARD · BIG_WITHOUT_TEXT · SMALL_WITHOUT_TEXT · SMALL_WITH_TEXT
backgroundColorstringBackground color hex (e.g. #1A1A2E). Independent of teaserStyle — updating one preserves the other.
headlinestringComment text above the card. Max 360 chars.
status / scheduledAt / allowComment / allowReaction / allowSharing / externalTagIds / etc.variousStandard card settings. Also: locale (en/de, defaults to mix language).

GraphQL fields object

articleFields: {
  title: "string",                    // required
  url: "https://...",                 // required — valid URI
  sourceName: "string",               // required — must not be empty
  headline: "string",                 // optional — omit if not provided
  abstract: "string",                 // optional
  gallery: [{ image: { id: Int, rightholder?: String } }], // optional — id from storyCardParseUrl(imageUrl)
  styles: { teaserImageStyle?: "STANDARD", backgroundColor?: "#hex" } // both optional, independent
}

Content type (read back)

StoryCardArticleContent {
  title
  url
  headline
  abstract
  sourceName
  contentAuthor
  gallery { image { id url rightholder statusCopyrightId } }
  styles { teaserImageStyle backgroundColor }
}

MCP examples

// Basic article
{
  "name": "create_card",
  "arguments": {
    "storyId": 61218,
    "url": "https://example.com/article-slug",
    "title": "The Future of Remote Work",
    "source": "Harvard Business Review",
    "description": "New research shows..."
  }
}

// URL auto-detection (no cardType needed)
{
  "name": "create_card",
  "arguments": {
    "storyId": 61218,
    "url": "https://bbc.com/news/story",
    "title": "Article Title",
    "source": "BBC News"
  }
}

Notes

sourceName is required and must not be empty. Providing an empty string causes a StoryCardValidationError.

url must be a valid URI. The API validates the URL format. Invalid URLs cause a StoryCardValidationError.

URL auto-detection: If no cardType is specified and the URL is not a social platform or Apple Podcast URL, the MCP automatically creates an Article card.

MCP auto-parse (url-only): Providing only url is enough — title, source, abstract, author, and teaser image are auto-populated via storyCardParseUrl. Fallback chain: (1) storyCardParseUrl, (2) HTTP OG/meta, (3) AI extraction via web search + URL slug. User-provided fields always override. Note: contentAuthor from parse may be the site name (e.g. "kicker"), not a person.

Image resolution: Teaser images are resolved by calling storyCardParseUrl(imageUrl) on the IMAGE URL (not the article URL) → StoryCardImageParsedUrl.gallery[0].image.id. Applies to both user-provided imageUrls[0] and OG fallback image. Never calls uploadImage.

Partial updates (update_card): Fetches current card first and merges only provided fields. Existing url, abstract, sourceName, rightholder, teaserStyle, and backgroundColor are preserved automatically. Only supply fields you want to change.

rightholder is image-tied: gallery[0].image.rightholder belongs to the image — it is cleared automatically when you replace the image (new imageUrls), but preserved on text-only updates. teaserStyle is card-level and always preserved even when the image changes.

styles are independent: teaserImageStyle and backgroundColor are separate fields. Updating only one always preserves the other.

headline vs title: headline = editorial comment shown above the card, not the article title. User saying "headline" or "title" → title field. Only use headline for explicit "comment text".

Clean auto-parsed titles: storyCardParseUrl often returns the publisher's SEO meta-title suffix (e.g. … | t3n, | Sitename, – Magazin). Always strip these from the title field — the brand is already shown via sourceName. Leave the rest of the title untouched.

Card Type

Social

API type: QUOTE

Social media embed card. Paste a social post URL and the card displays the content inline. Alternatively, provide a quote text with attribution. Supports X/Twitter, Instagram, TikTok, Facebook, LinkedIn, and Bluesky.

Supported platforms

PlatformURL pattern
X / Twitterx.com/... · twitter.com/...
Instagraminstagram.com/p/... · instagram.com/reel/...
TikToktiktok.com/@.../video/...
Facebookfacebook.com/...
LinkedInlinkedin.com/posts/... · linkedin.com/feed/update/...
Blueskybsky.app/profile/.../post/...

Required fields

storyIdrequirednumberMix ID (must be STANDARD).
urlrequiredstring (URL)Social platform post URL. Also accepts any URL — platform detection is advisory.

MCP auto-detection fallback chain

1

storyCardParseUrl

GraphQL parse returns quote, quotePerson, quotePersonHandle, quoteCreated, quotePersonImage, and gallery images. Works reliably for X/Twitter, TikTok, Bluesky. Often blocked (auth walls) for Instagram, Facebook.

2

OG / meta scrape

HTTP fetch + og:image / og:title scrape. Used when step 1 returns no content. May yield a preview image even for platforms that block full parse.

3

URL path extraction

Best-effort username / handle extracted from URL path structure (e.g. x.com/USERNAME/status/…quotePerson=USERNAME, quotePersonHandle=USERNAME). Ensures attribution is always populated even when no metadata is returned.

At least one of quote (text) or gallery (image) must be non-empty. If all three fallback steps fail to produce either, the MCP returns an error — provide text and/or quotePersonImageUrl explicitly.

Required fields

storyIdrequirednumberMix ID (must be STANDARD).
urlrequiredstring (URL)Social post URL. Must match a supported platform domain (X, Instagram, TikTok, Facebook, Bluesky). LinkedIn URLs auto-route to ARTICLE.

Optional fields (create & update)

textstringQuote/post content text. Overrides auto-parsed quote. Either text or an image must be present. Max 2000 chars.
quotePersonstringName of the person / account who posted. Auto-detected from URL; override if wrong.
quotePersonHandlestringHandle / username without @ (e.g. tchopIO). App adds @ on display — storing with @ causes @@username. Auto-detected from URL path.
quotePersonImageUrlstring (URL)Avatar / profile image for the poster. Uploaded to tchop CDN. Both create and update use quotePersonImageUrl.
quoteCreatedstring (ISO 8601)Datetime the original post was created. Auto-detected from parse; override to correct.
headlinestringEditor comment shown above the card. Max 360 chars.
backgroundColorstring (hex)Card background colour (e.g. #1DA1F2 for X blue).
status / scheduledAt / tags / allowComment / etc.variousStandard card settings shared across all card types.

GraphQL fields object

quoteFields: {
  url: "https://x.com/user/status/...",           // required
  quote: "string",                                 // quote/post text
  quotePerson: "string",                           // author name
  quotePersonHandle: "username",                    // author handle — NO @ prefix
  quotePersonImageId: Int,                          // avatar image ID
  quoteCreated: "2026-06-22T10:00:00Z",            // original post datetime
  quoteSource: "X",                                // platform key (auto-set)
  headline: "",                                    // editor comment (pass "" if unused)
  gallery: [{ image: { id: Int } }]                // images
}

Platform source keys

PlatformquoteSource valueURL pattern
X / TwitterXx.com/… · twitter.com/…
InstagramINSTAGRAMinstagram.com/p/… · /reel/…
TikTokTIKTOKtiktok.com/@…/video/…
FacebookFACEBOOKfacebook.com/… · fb.com/…
LinkedInN/A — routes to ARTICLElinkedin.com/… → auto-routed to ARTICLE card
BlueskyBSKYbsky.app/profile/…/post/…

Content type (read back)

StoryCardQuoteContent {
  headline
  quote
  quotePerson
  quotePersonHandle
  quoteCreated
  quoteSource
  url
  styles { backgroundColor }
  quotePersonImage { id url }
  gallery { image { id url rightholder statusCopyrightId } }
}

MCP examples

// X post — auto-detected, all fields populated from URL
{ "name": "create_card", "arguments": {
  "storyId": 61218,
  "url": "https://x.com/tchopio/status/1234567890"
} }

// Instagram post — parse blocked, provide text + avatar manually
{ "name": "create_card", "arguments": {
  "storyId": 61218,
  "url": "https://www.instagram.com/p/ABC123/",
  "text": "Great shot from our event today 📸",
  "quotePerson": "tchop",
  "quotePersonHandle": "tchopio",
  "quotePersonImageUrl": "https://example.com/avatar.jpg"
} }

// Update quote text and person
{ "name": "update_card", "arguments": {
  "storyId": 61218,
  "storyCardId": 99042,
  "cardType": "QUOTE",
  "quote": "Updated post content.",
  "quotePerson": "Jane Smith",
  "quotePersonHandle": "janesmith"
} }

Notes

URL auto-detection: Paste any supported social URL without cardType — MCP auto-routes to QUOTE. LinkedIn URLs auto-route to ARTICLE.

Cannot convert to other card types. QUOTE is not part of the short post family (EDITORIAL/IMAGE/VIDEO/AUDIO/PDF). Passing a different cardType in update_card will fail.

Gallery preservation: Updating a QUOTE card without providing imageUrls preserves the existing gallery — MCP re-sends existing image IDs automatically. Headline and gallery are also preserved through status changes (UNPUBLISHED, SCHEDULED).

Card Type

Long post

API type: POST

Rich text native article. Content is stored as Editor.js blocks. Use the markdown field — the MCP automatically converts it to Editor.js blocks. Supports headings, paragraphs, bullet lists, ordered lists, code blocks, blockquotes, and inline images.

Required fields

storyIdrequirednumberMix ID (must be STANDARD).
titlerequiredstringPost title. Max 160 chars.
markdown or contentBlocksrequiredstring / object[]Body content. Provide markdown (recommended — auto-converted to Editor.js) or raw contentBlocks. One must be non-empty.

Optional fields

descriptionstringPost abstract/summary shown in feed previews. Max 700 chars.
imageUrlsstring[]Teaser/cover image URLs. Uploaded to tchop.
authorstringPost author name. Max 60 chars.
sourceNamestringSource attribution. Max 160 chars.
teaserStyleenumTeaser image layout: STANDARD · BIG_WITHOUT_TEXT · SMALL_WITHOUT_TEXT · SMALL_WITH_TEXT
headlinestringComment text above the card. Max 360 chars.
status / scheduledAt / allowComment / allowReaction / allowSharing / allowDisplayAuthorvariousStandard card settings. Also: locale (en/de, defaults to mix language).
externalTagIds / internalTagIds / statusTagId / mentionedUserIds / categoryIdvariousTags, mentions, category.
localestringCard language: en or de. Defaults to the mix language.

Supported Markdown elements

MarkdownEditor.js block typeNotes
Paragraphsparagraph**bold**, *italic*, ++underline++, ==highlight==, ^sup^, ~sub~, backtick code, ~~strike~~, [link](url)
# H1 through ###### H6headerLevel 1–6. Inline HTML works: <b>, <i>, <mark>, <a href>
- item or * itemlist (unordered)Items support inline HTML formatting
1. itemlist (ordered)Items support inline HTML formatting
- [ ] task / - [x] donechecklistItems support inline HTML formatting
> textquoteText and caption both support inline HTML
> ! textcalloutLeading ! inside blockquote → callout block with ⚠️ icon. Supports inline HTML.
| col | col |tableOptional |---| separator row sets withHeadings: true. Cells support inline HTML. To force no header row in raw blocks: withHeadings: false.
```lang\ncode\n```code
![alt](url)imageImage uploaded to tchop via storyCardParseUrl
[title](url) on its own lineembedlinkStandalone link line → embed block with caption + link metadata
Bare URL on its own lineembedlinkURL alone → embed block
---delimiter

Raw contentBlock types (use contentBlocks param)

These block types are not available via markdown — pass them directly as contentBlocks. All blocks require id: String!.

Block typeRaw data shapeNotes
image (multi){ items: [{ image: { id }, caption }, ...], withBorder, withBackground, stretched }Up to N images in one block. caption supports inline HTML.
audio{ items: [{ audio: { id }, caption }] }Upload audio file first → get ID from upload service. caption is plain text only — no HTML.
video{ items: [{ video: { id }, caption }] }Upload video file first → get ID from upload service. caption is plain text only — no HTML.
pdf{ items: [{ pdf: { id }, caption }] }Upload PDF first → get ID from upload service. caption is plain text only — no HTML.
file{ items: [{ file: { id }, caption }] }Upload arbitrary file first → get ID from upload service. caption is plain text only — no HTML.
card{ storyCard: { id: N, storyId: N } }Embeds a reference to another card. Both id and storyId required.
story{ story: { id: N } }Embeds a mix/story reference. Type name is "story" — NOT "mix".
user{ user: { id: N } }Embeds a user reference.

GraphQL fields object

postFields: {
  title: "string",                          // required
  headline: "",                              // required (NonNull) — pass "" if unused
  sourceName: "",                            // required (NonNull) — pass "" if unused
  abstract: "",                              // required (NonNull) — pass "" if unused
  contentBlocks: [                           // required — array of Editor.js blocks
    { id: "b1", type: "paragraph", data: { text: "Body text with <b>bold</b>" } },
    { id: "b2", type: "header", data: { text: "Section", level: 2 } },
    { id: "b3", type: "audio", data: { items: [{ audio: { id: 58532 }, caption: "Sample" }] } },
    { id: "b4", type: "story", data: { story: { id: 61260 } } }
  ],
  gallery: [{ image: { id: Int } }],        // teaser image — NO "title" field on PostGallery
  styles: { teaserStyle: "STANDARD" }        // optional
}

Content type (read back)

StoryCardPostContent {
  title
  headline
  abstract
  sourceName
  gallery { image { id url rightholder statusCopyrightId } }  // no "title" field on PostGallery
}

MCP example

{
  "name": "create_card",
  "arguments": {
    "storyId": 61218,
    "cardType": "POST",
    "title": "Q2 Product Update",
    "markdown": "## What's new\n\nWe shipped three major features this quarter.\n\n- Feature A\n- Feature B\n- Feature C\n\n## What's next\n\nSee the roadmap.",
    "description": "Quarterly product update",
    "imageUrls": ["https://example.com/product-header.jpg"]
  }
}

Notes

contentBlocks is always required by the API — even for updates. Always provide markdown (preferred) or contentBlocks when creating or updating a Long post card.

All four postFields strings are NonNull: headline, sourceName, abstract, title — all required by the API. Pass "" if not used. Omitting any causes a StoryCardPostContentValidationError.

update_card with markdown wipes media blocks. Markdown conversion only produces text-type blocks. If the post contains audio, video, PDF, card, mix, or user blocks — use contentBlocks (raw array) instead of markdown to avoid losing them.

Use markdown for text-only posts. The MCP converts Markdown to Editor.js blocks automatically. Raw contentBlocks are only needed for media/reference blocks not available via Markdown.

Inline images (![alt](url)) in markdown are resolved via storyCardParseUrl(url)StoryCardImageParsedUrl.gallery[0].image.id and stored as image blocks with format { items: [{ image: { id }, caption }] }. Provide publicly accessible image URLs.

Bare URLs on their own line are converted to embedlink blocks. Standalone [title](url) links also produce embedlink blocks.

Mix/story block type = "story", not "mix". Card reference block = "card" with { storyCard: { id, storyId } }.

Card Type

Thread

API type: THREAD

Thread container card. Creates a named discussion thread in the feed. Thread replies live as nested content inside the thread. The card itself only requires a title — all discussion happens within it.

Required fields

storyIdrequirednumberMix ID (must be STANDARD).
titlerequiredstringThread title. Displayed in the feed. Max 360 chars.

Optional fields

backgroundColorstringHex background color for the card (e.g. #1A1A2E). Only style available on THREAD — no textColor.
status / scheduledAt / allowReaction / allowSharing / externalTagIds / etc.variousStandard card settings. Note: allowComment is ignored — comments always enabled on THREAD.

GraphQL fields object

threadFields: {
  title: String!,                    // required
  styles: { backgroundColor: String } // optional — only field available
}

Content type (read back)

StoryCardThreadContent {
  title
  styles { backgroundColor }
}

MCP example

{
  "name": "create_card",
  "arguments": {
    "storyId": 61218,
    "cardType": "THREAD",
    "title": "Ask Me Anything — Product Team"
  }
}

Notes

Comments always enabled. allowComment: false is silently ignored for THREAD cards. THREAD is designed for discussion — comments cannot be disabled.

Card Type

Poll

API type: POLL

Interactive survey or quiz card. Users vote on a question with 2–12 answer options. Supports anonymous voting, multiple choice, and configurable results visibility. Validated by the API — min 2 options, max 12.

Required fields

storyIdrequirednumberMix ID (must be STANDARD).
questionrequiredstringPoll question text. Max 360 chars.
optionsrequiredstring[]Answer options. Min 2, max 12. Each option max 100 chars. API rejects fewer than 2 or more than 12.

Optional fields

headlinestringCTA title shown on the card. Defaults to question text if not provided. Max 360 chars.
descriptionstringSupporting line displayed beneath the headline (maps to pollFields.text). Omit if not needed — never copy the question here.
multipleVotesbooleanAllow selecting multiple options. Default: false (single choice only).
allowAnonymousbooleanAllow anonymous voting. Default: true.
showVoteResultsbooleanShow vote results to participants. Default: true.
showTotalVotesbooleanShow total vote count. Default: true.
pollDurationnumberVoting duration in hours from now. Sets votingTime (ISO8601 deadline) — poll closes for new votes at that time.
imageUrlsstring[]Gallery image (1 for POLL). Resolved via storyCardParseUrl first, upload as fallback.
imageCaptionsstring[]Caption per image. Parallel to imageUrls. Plain text — no © prefix.
imageRightholdersstring[]Copyright holder per image (parallel array).
imageCopyrightStatusesenum[]Copyright status per image: CC, LICENSED, SUBLICENSED, UNKNOWN.
backgroundColorstringHex background color (e.g. #1a365d).
status / scheduledAt / allowComment / allowReaction / allowSharing / externalTagIds / internalTagIds / statusTagIdvariousStandard card settings.

Update-only fields

headline (POLL)string⚠️ The poll question/headline is not updatable — the API silently ignores headline changes on POLL updates, even before any vote (verified live). Delete and recreate the poll to change the question.
pollOptionsstring[]Replace poll options on update (min 2). Falls back to existing options if not provided. Locked once published or any vote cast.
pollIsAnonymous / pollIsMultipleChoicebooleanUpdate anonymous/multiple-choice settings. Falls back to existing values if not provided.
pollShowVoteResults / pollShowTotalVotesbooleanAlways updatable even after publishing. Falls back to existing values if not provided.
imageUrlstringReplace poll image on update (singular, unlike create which uses imageUrls[]). Locked once published.

API validation rules

RuleValueError if violated
Min options2StoryCardValidationError
Max options12StoryCardValidationError
Max question length360 charsStoryCardValidationError
Max option length100 charsStoryCardValidationError

GraphQL fields object

pollFields: {
  headline: String!,                   // required (NonNull) — the question / CTA title
  text: String,                         // optional — supporting line beneath headline (omit if no separate description)
  options: [                           // required — 2–12 options
    { id: String!, text: String! },    // client-provided IDs (strings)
    ...
  ],
  multipleVotes: Boolean,              // optional, default false
  allowAnonymous: Boolean,             // optional, default true
  showVoteResults: Boolean,            // optional
  showTotalVotes: Boolean,             // optional
  votingTime: String,                  // optional ISO8601 — voting deadline
  styles: { backgroundColor: String }  // optional
}

Content type (read back)

StoryCardPollContent {
  headline       // the question / CTA (prominent title)
  text           // optional supporting line beneath headline
  options { id text }
  multipleVotes
  allowAnonymous
  showVoteResults
  showTotalVotes
  votingTime
  totalVotes     // Int — total votes cast
  selectedVoteIds  // [String] — current user's selections (null if not voted)
  voteResults {  // [{ id, count }] — no text/percent, cross-ref options by ID
    id count
  }
  styles { backgroundColor }
}

MCP example — create with all fields

{
  "name": "create_card",
  "arguments": {
    "storyId": 61277,
    "cardType": "POLL",
    "question": "Which feature should we prioritise in Q3?",
    "options": ["Better search", "Dark mode", "Mobile app", "API improvements"],
    "headline": "Vote now — your input shapes our roadmap",
    "description": "Select the feature you'd like to see shipped first.",
    "multipleVotes": false,
    "allowAnonymous": true,
    "showVoteResults": true,
    "showTotalVotes": true,
    "pollDuration": 72,
    "imageUrls": ["https://example.com/poll-cover.jpg"],
    "imageCaptions": ["Q3 roadmap survey"],
    "imageRightholders": ["Unsplash"],
    "imageCopyrightStatuses": ["CC"],
    "backgroundColor": "#1a365d",
    "externalTagIds": [1027],
    "statusTagId": 1056
  }
}

Notes

Poll content locks when PUBLISHED (even with 0 votes). headline, options, multipleVotes, allowAnonymous, and the gallery image can only be changed while the card is UNPUBLISHED and has 0 votes. Once published, these fields are silently ignored on update. Always updatable regardless: showVoteResults, showTotalVotes, tags, and card settings.

Partial updates fully supported. update_card fetches the current card first — all existing values (headline, options, booleans, image, votingTime) are preserved as fallbacks. Only pass what you want to change.

Vote results are on the card. storyCardPollVote returns only a Boolean. Re-read the card after voting to get updated voteResults and totalVotes. voteResults[].count — no percentage, no option text. Cross-reference with options[] by ID.

pollDuration sets a deadline from now. Pass an integer (hours). Converts to an ISO8601 votingTime (current time + N hours). On update, existing deadline is preserved if pollDuration not passed.

storyCardPollVote input: uses storyCardId: Int! and selectedVoteIds: [String!]! — NOT itemId or selectedOptionIds.

Card Type

Event

API type: EVENT

Event card that links to a pre-created tchop event. Displays event details inline in the feed — date, time, location, RSVP status. The event must exist before creating this card. Use the Events section to create or look up events.

Required fields

storyIdrequirednumberMix ID (must be STANDARD).
eventIdrequirednumberID of an existing tchop event. Use get_events to find available event IDs.

Optional fields

headlinestringComment text shown above the card. Max 360 chars.
status / scheduledAt / allowComment / allowReaction / allowSharing / externalTagIds / etc.variousStandard card settings. Also: locale (en/de, defaults to mix language).

GraphQL fields object

// top-level fields object
{
  eventId: Int!,        // required — links to existing event
  eventFields: {
    headline: String // pass null or a string — key must be present (omitting key → validation error)
  }
}

Content type (read back)

StoryCardEventContent {
  headline  // card overlay text — separate from event data
  styles
}

// Full event data lives on StoryCard.event — NOT in content:
StoryCard {
  event {
    id name startDate endDate
    location { address type virtualUrl }
    participationCount
  }
}

MCP example

{
  "name": "create_card",
  "arguments": {
    "storyId": 61218,
    "cardType": "EVENT",
    "eventId": 9042,
    "headline": "Don't miss this — RSVP now!"
  }
}

Notes

eventFields.headline key is required. Pass headline: null (or a value). Sending eventFields: {} with no headline key triggers a validation error.

Event must exist first. Create the event in the Events section before linking it to a card. An invalid eventId returns EventNotFoundError.

One event can be linked by multiple cards across different mixes. The event data (date, location, RSVP) is always pulled from the source event via StoryCard.event — separate from card content fields.

EVENT cards are fully interconvertible with short post types (EDITORIAL, IMAGE, VIDEO, PDF, AUDIO) via update_card with a different cardType.

Concepts & Features

Users

A User is an org-level account identified by email (unique, never editable). Each user has a customisable profile, one highest role displayed in the app (admin > editor > editor-limited > reader), a per-channel role in every channel they can access, and an activity status.

Roles

RoleScopeMeaning
ORGANISATION_OWNEROrgFull control of the organisation
ORGANISATION_ADMINOrgOrg-wide administration incl. user management
ORGANISATION_TEAM_MEMBEROrgOrg-level team member (staff)
CHANNEL_ADMINChannelManages a channel incl. its users
CHANNEL_EDITORChannelCreates and curates content
CHANNEL_EDITOR_LIMITEDChannelRestricted editing rights
CHANNEL_READERChannelConsumes content, comments, reacts

User statuses

StatusMeaning
ACTIVEHas logged in / is present
ENGAGEDActive and interacting (stricter than ACTIVE)
INACTIVENot active

Onboarding methods

MethodMeaning
INVITATIONInvited by email with a signup link
AUTO_GENERATED_PASSWORDInvited by email with a generated password
COMPANY_IDOnboarded via company ID
DIRECT_LINKJoined via a direct link

Who can see whom

Access is role-gated at the MCP layer — stricter than the raw API: org owner / org admin / staff see the whole organisation; channel admins and editors see only users of channels they manage; readers and editor-limited users see only their own profile.

Profile fields

FieldNotes
AvatarImage; initials placeholder when unset
Name (screenName)Mandatory; defaults to the email's local part
EmailMandatory, unique identifier, never editable
GenderMALE / FEMALE / COUPLES / DIVERSE / NON_BINARY / TRANS — icon next to username when set
Company / Department / PositionFree-text org fields; orgs can rename or hide them
SubscriptionFree-text org field
Bio / Phone / Location / URLLocation supports Google Places-backed text
Social linksFacebook, Instagram, LinkedIn, YouTube, TikTok, X (Twitter), Snapchat
User tagsOrg-wide tags on profiles — structure and filter the user base
Profile feedVisual feed of images/videos attached to the profile

Every field can be renamed, hidden from the public profile, hidden from the edit view, or locked by org configuration. SSO-managed orgs often lock all profile editing — update_user_profile returns an API error in that case.

The user directory — one tool

list_users covers the entire directory: org-wide or per-channel listing, name search, exact email lookup, single user by ID, your own profile (self: true, incl. your referral code), and filter-options discovery (showAvailableFilters: true).

  • Filters: roles, statuses, genders, onboarding methods, user tags, department / position / subscription, location, channels, excludeBanned
  • Sort: name, email, REGISTERED (invited/added date), AUTHORIZED_TIME (last active) — dates are sortable only, the API has no date-range filter
  • Each row carries: all profile fields, role, status, banned flag, onboarding method, added + last-active times, channels with per-channel role, engagement stats, leaderboard badge, referral count

Referral codes are self-only — the API cannot expose another user's code. There is also no mobile-vs-web split for last activity; authorizedAt is platform-agnostic.

User lifecycle

TaskTool + action
Invite by email (signup link)invite_userinvite (bulk supported)
Invite with generated passwordinvite_userinvite_with_generated_password
Invite via company IDinvite_userinvite_by_company_id
Add existing user to a channelinvite_useradd_existing_to_channel
Resend / remind invitationinvite_userresend_invitation / remind_invitation
Change channel roleupdate_user_role — channel role values
Promote to / demote from org adminupdate_user_role — org role values / REMOVE_ORG_ADMIN
Remove from channelremove_userremove_from_channel
Remove from org (= delete user)remove_userremove_from_organisation
Ban / unbanremove_userban / unban

All lifecycle tools are restricted to org owners, admins, and staff. remove_user additionally requires confirm: true and refuses to act on your own account. Invites return a per-user status: OK (done), SKIP (already exists), ACCESS (no permission).

Last-admin protection. Demoting, removing, or banning the only CHANNEL_ADMIN of a channel is blocked — batch-aware — until another user is made admin of that channel (update_user_role), then retry.

Profile feed

The profile feed is a visual story on each profile holding images and videos. list_user_profile_feed reads it (your own feed includes drafts and scheduled cards; other users' feeds are published-only, matching the app). post_card_in_user_profile_story posts to your own feed — published, unpublished, or scheduled.

Concepts & Features

Comments

A Comment is user-generated content posted in response to a card. Comments enable community discussion — readers can reply, react, and engage with content directly.

tchop hierarchy

ChannelAudience
MixFeed
CardContent
CommentDiscussion

Comment types

TypeDescription
Top-level commentPosted directly on a card. Any user with posting rights.
ReplyResponse to a top-level comment. One level deep only — replies cannot be replied to.

Reactions

Users can react to comments and replies with a single reaction at a time. Available reactions are configured per organisation (e.g. like, love). Calling the same reaction again removes it.

MCP behaviour: When a user says "like a comment" and it's already liked, the MCP informs the user instead of accidentally removing the reaction. Use explicit "unlike" to remove.

Access control

Who can post comments depends on mix settings (allowComments, allowEditorLimitedToPost, allowReaderToPost).

Content types

TextStringPlain text with full Unicode support — any emoji can be used in comment or reply text.
Image attachmentimageIdUpload via POST /api/fs/upload/image, then pass the returned ID.
Video attachmentvideoIdUpload via POST /api/fs/upload/video, then pass the returned ID.
Text + attachmentCombinedText and media can be combined in one comment.

Moderation

ActionWho canEffect
HideEditor+Removes from reader view. Reversible — comment still exists.
UnhideEditor+Makes hidden comment visible again.
HighlightEditor+Features the comment prominently. One per card at a time.
DeleteAuthor or Org AdminPermanently removes comment + all replies. Cannot be undone.

Tip: Use hide for temporary moderation. Use delete only when permanent removal is needed.

Comment feed filters

The channel comments feed supports filtering by:

SortEnumNewest first (DESC) or oldest first (ASC).
Date rangeStringCustom from/to or shortcut: 24h, 7d, 30d, 3m, 6m, 12m.
Card typeEnumARTICLE · AUDIO · EVENT · IMAGE · PDF · POLL · POST · QUOTE · THREAD · VIDEO
MixstoryIdFilter by a specific mix.
AuthorauthorIdFilter by user ID.
Show hiddenBooleanInclude or exclude hidden comments.

Pagination: Channel feed max 15/page. Card comments max 25/page. Use fetchAll:true in MCP tools to auto-paginate.

Concepts & Features

Tags

A Tag (also called: label, hashtag, keyword) is a metadata label attached to a card. Tags enable content filtering and organisation across channels.

Tag types

TypeAlso calledVisible to readersWho can assign
EXTERNALVisible tag, label✅ YesAny user with card create rights
INTERNALHidden tag, private label❌ NoEditor+ only
EXTERNAL_STATUSStatus tag, workflow tag✅ YesEditor+ (one per card)

Tag scope

  • Tags are created at org level — only Org Admin+ can create/update/delete
  • Tags are assigned at card level — done when creating or updating cards
  • Mixes support default tags — tags set on a mix auto-apply to new cards in that mix

Tags vs Mixes ("categories")

Both are sometimes called "categories". To distinguish:

TagsMixes
Has only a name
Has title, subtitle, image
Contains cards❌ (attached to cards)
Created at org level

If a user says "create a category" without more context — ask: "Do you mean a tag (just a name/label) or a content section (mix with title, subtitle and display settings)?"

Other tag types: USER tags are managed in the Users section. EVENT tags are managed in the Events section. These are not part of the Tags section tools.

Access rules

OperationReaderEditor LimitedEditor+Org Admin+
List EXTERNAL / STATUS tags
List INTERNAL tags❌ MCP blocked❌ MCP blocked
Get cards by EXTERNAL/STATUS tag
Get cards by INTERNAL tag❌ MCP blocked❌ MCP blocked
Create / Update / Delete tag

Note: The API itself does not restrict readers from INTERNAL tags. The MCP enforces this restriction at tool level.

Status tags

EXTERNAL_STATUS tags are special — one per card. Used for editorial workflow states: Approved, Backlog, Closed, etc. Visible to readers.

Tag feed filters

get_cards_by_tag supports: card type, status, date range (from/to), author.

Concepts & Features

Bookmarks

A Bookmark (also called: saved card, saved post, favourite, starred card) is a personal marker a user places on a card to save it for later reference. Bookmarks are private — only the bookmarking user can see their own bookmarks.

How bookmarks work

User-scopedEach user sees only their own bookmarks. No shared bookmark feeds.
Channel-scopedBookmark feed requires a channelId. A bookmark in one channel doesn't appear in another channel's feed.
Non-destructiveBookmarking doesn't change the card — no visible effect to other users.
IdempotentBookmarking an already-bookmarked card is a no-op. Removing a non-existent bookmark is a no-op.
isBookmarkedField on get_card — returns true only if the currently authenticated user has bookmarked this card.

Bookmark vs Pin vs Reaction

BookmarkPinReaction
Visible to other users❌ Private✅ Public✅ Public
Affects feed order✅ Moves to top
Who can do itAny readerEditor+Any reader

"Save for later", "star this card", "add to favourites"bookmark_card

Access rules

OperationReaderEditor LimitedEditorOrg Admin+
Bookmark a card
Remove a bookmark
List own bookmarks

Bookmark feed filters

list_bookmarks supports: card type, date range (from/to), category, author, full-text search. Size max 15/page.

What can be bookmarked

Only cards can be bookmarked — not channels, mixes, comments, or other objects.

Concepts & Features

Notifications

Two distinct notification types for reaching users: Push Notifications (device OS alerts, even when app is closed) and In-App Messages (modal overlays shown inside the open app).

Notification types

TypeDeliveryAdmin listIndividual targeting
PushDevice OS notification (iOS/Android). Works when app is closed.✅ Channel/org admins see it✅ but never in admin list
In-AppModal overlay. User must be active in app.❌ Never in list✅ via audience conditions

Push notification scope

ScopeWho can sendVisible in admin list
Channel (all users)Channel Admin+✅ Channel admin tab
Org (all users)Org Admin+✅ Org admin tab
Specific users (userIds)Channel Admin+ or Org Admin+❌ Never in list

Roles & permissions

ActionMinimum role
Send push to channel / in-app to channelChannel Admin+
Send push to org / in-app to orgOrg Admin+
Send to specific usersChannel Admin+ (channel) or Org Admin+ (org)
View push notification listChannel Admin+ (channel) or Org Admin+ (org)
Update/delete scheduled pushallowToUpdate / allowToDelete on notification
Delete in-app messageallowToDelete on message

Role hierarchy: reader < editor-limited < editor < admin < org admin < org owner < staff.
Channel Admin+ includes: channel admin, org admin, org owner, staff.
Org Admin+ includes: org admin, org owner, staff.

Push notification fields

titleStringNotification headline (min 4 chars). Required.
messageStringNotification body text (min 1 char). Required.
userIds[Int]Target specific users. When set, notification never appears in admin list.
recipientEnumCHANNEL (default, requires channelId) or ORG (org admin only). Ignored when userIds is set.
linkContentEnumNONE (default) · URL (opens browser/webview) · CARD (deep-links to card, requires storyId + cardId).
deliveryTimeISO 8601Schedule for future delivery. Omit to send immediately. Scheduled pushes can be edited via update_push_notification or removed via delete_push_notification.

In-app message fields

titleStringModal headline (4–120 chars). Required.
messageStringBody text below title (4–300 chars). Required.
imageInt (imageId)Optional image shown above title. Upload via upload_image to get imageId.
bgColor / textColorHexModal background and text color. Defaults: #FFFFFF / #1E1E1E.
buttonTextStringCTA button label (2–20 chars). Default: "OK".
buttonBgColor / buttonTextColorHexButton colors. Defaults: #488ED8 / #FFFFFF.
buttonActionEnumCANCEL (dismiss, default) · OPEN_URL (external browser) · OPEN_IURL (in-app webview) · SHARE (share sheet).
expirationHoursIntHours until message stops showing (12–8760). Default: 72 (3 days).
audienceTypeEnumlogin (real users, default) or demo (demo users).

Auto push notifications (per-mix)

Mixes can be configured to automatically send a push notification when a card is published. Configured via autoNotification in mix settings — separate from manual push. See the Mixes section.

Scheduling & lifecycle

  • Omit deliveryTime → send/show immediately
  • Set deliveryTime → schedule for that UTC time
  • Scheduled push notifications can be updated via update_push_notification or deleted via delete_push_notification before delivery
  • In-app messages cannot be updated after creation — delete via delete_in_app_message and recreate

Known behaviours

  • InAppMessageLayoutEnum only has one value: CARD. Always sent automatically.
  • conditions.types is required by the API. Tool always sends "login" or "demo".
  • Push notifications sent to individual userIds do not appear in the admin list even after sending.
  • In-app expirationHours range: 12–8760 (12 hours to 1 year).
Concepts & Features

Analytics

tchop analytics surfaces aggregated data about how your community uses the platform — who is active, what content performs, how channels compare, and how messaging lands.

Org Admin / Org Owner / Staff token required. Reader and Editor tokens return permission errors on all analytics queries.

  • Date range — every query requires from + to (YYYY-MM-DD). No enforced max window.
  • Platform filter — pass platform: ["WEB"|"IOS"|"ANDROID"] to isolate one platform, or omit for blended totals.
  • Channel filter — pass channelId: [id, ...] to restrict org-wide charts to specific channels.
  • Deduplication — user counts are deduped per user. Don't sum platform splits to get a unique-user total; use blended.
  • Intervals — time-series charts accept interval: AUTO | DAY_OF_WEEK | HOUR_OF_DAY. AUTO lets the server pick the best bucket size for the date range.
  • 13 MCP tools — ranging from one-call overviews (get_analytics_overview) to focused charts (get_analytic_chart) to parallel convenience tools (get_card_type_performance, get_engagement_by_platform). See the Chart types tab for what each chart returns.

Access rules

OperationReaderEditorOrg AdminOrg OwnerStaff
All analytics queries

Time-series charts — used by get_analytic_chart, get_analytics_overview, get_engagement_by_platform, get_user_overview

Return { labels, interval, datasets: [{ label, data }] }. Each dataset is one metric (e.g. "Active users"). Labels are date strings matching the chosen interval.

Chart keyWhat it measuresTypical use
userActiveRegistered users who performed at least one action in the period (DAU / MAU depending on range)Core retention metric — how many real users came back
userActivityAction counts for registered users over time (views, reactions, comments, shares)Trend line for registered-user engagement volume
userAllActivitySame as userActivity but includes anonymous / demo usersTotal platform activity including non-registered visitors
userDemoActivityActivity from anonymous/demo sessions onlyMeasure browse-before-register behaviour; conversion funnel top
userEngagementEngagement rate over time (interactions ÷ active users)Quality signal — are active users actually doing things
userLoginLogoutLogin and logout event countsSession start/end patterns; detect churn signals
userReferralActivitySignups or actions driven by referral codesReferral program effectiveness
userSessionSession count over time (not unique users)How often users return within the period
contentActivityPosts published, views, reactions, comments, and shares over timeContent pipeline health — publishing cadence vs audience response
installUninstallAppApp install and uninstall eventsMobile app growth / churn at the install level

Channel list — used by get_channel_list_analytics

Returns a ranked list of channels ordered by a single metric. Use to compare channel health side-by-side.

Metric keyWhat it counts
viewsTotal card views across the channel
postsCards published in the channel
commentsComments left on channel content
reactionsReactions on cards
sharesShare events
commentReactionsReactions on comments

Content list — used by get_content_list, get_card_type_performance

Top-performing individual cards ranked by one of 26 orderBy metrics. Each item includes the card ID, headline, type, and the metric value. Supports filtering by card type, channel, and text query.

orderBy groupAvailable values
ReachVIEWS · SHARES · BOOKMARKS
InteractionCOMMENTS · REACTIONS · ENGAGEMENT
MediaPOST_VIDEO_PLAYS · POST_AUDIO_PLAYS · POST_LINK_CLICKS

Content analytics charts — used by get_content_analytics

Chart keyWhat it returnsTypical use
contentActivityTime-series — posts, views, reactions, comments, shares per dayOverview of content pipeline and audience response over time
contentInteractionPer-channel breakdown — published cards vs cards that got at least one interactionFind channels where content lands vs goes unnoticed
contentTagListRanked tag list — views, engagement, and published count per tagIdentify which content topics drive the most engagement
commentListTop comments ranked by reaction count with card contextHighlight best community responses; moderation surface

User analytics charts — used by get_user_analytics, get_user_overview

Chart keyWhat it returnsTypical use
userFunnelClassic 3-step funnel: registered → active → engagedQuick conversion health check
userFunnelV2Extended funnel with more granular steps (registered, verified, onboarded, active, engaged)Identify exact drop-off points in onboarding
userFunnelV3Latest funnel with additional retention stepsMost detailed conversion view
userDemoFunnelAnonymous → registered conversion funnelMeasure how well the platform converts visitors to members
userInteractionBreakdown of user action types (views, reactions, comments, shares, bookmarks)Understand what actions users prefer
userReferralListRanked list of users by referrals generatedFind top referrers for rewards / recognition
userRoleContentEngagementEngagement metrics segmented by user roleCompare how admins vs editors vs readers engage
userRoleContentPublishedContent published segmented by user roleContribution breakdown by role
userRoleContentViewViews segmented by user roleConsumption patterns by role

Messaging analytics charts — used by get_messaging_analytics

Chart keyWhat it returnsTypical use
pushTotalAggregate push notification stats — sent, delivered, opened, open rateOverall push campaign health
pushListPer-notification breakdown ranked by sent / open / delivery timeIdentify best-performing notifications
inAppTotalAggregate in-app message stats — sent, viewed, clickedOverall in-app campaign health
inAppListPer-message breakdown ranked by sent / open / delivery timeIdentify best-performing in-app messages

Story analytics charts — used by get_story_analytics

Chart keyWhat it returnsTypical use
storyListPer-story breakdown — views, total watch duration (s), avg duration per viewCompare story performance; find which formats hold attention
storyTotalAggregate story totals across all stories in the rangeOverall story programme health
Concepts & Features

Chat

Coming soon

Detailed documentation for Chat is being written.

Concepts & Features

Events

Beta. Events are not yet enabled for all organisations. These tools return errors on orgs where Events is gated off.

An Event is a first-class content type in tchop for scheduling and managing real-world or virtual gatherings. Events support registration, capacity limits, waitlists, host assignment, and participant management.

Event vs Event Card

EventEvent Card
What it isStandalone event entity (date, location, participants)A card in a mix that links to an existing event
How to createcreate_eventcreate_card with cardType: EVENT + eventId
PurposeManages RSVP, capacity, hosts, participantsSurfaces the event in a channel feed

Create the event first, then create an event card to show it in a mix.

Event statuses

StatusMeaning
PUBLISHEDLive — visible to all channel members
UNPUBLISHEDDraft — only visible to editors/admins
CANCELLEDMarked cancelled, still visible
SCHEDULEDAuto-publishes at scheduledAt
PASTstartAt has passed (filter value only)
UPCOMINGstartAt is in the future (filter value only)

Participant statuses

StatusMeaning
CONFIRMEDRegistered, capacity available
WAITLISTEDEvent full, on the waitlist
HOSTEvent host

Event tags

Event tags are org-level tags with type: EVENT. Managed via Tag tools. Use list_tags with type: EVENT to find tag IDs, then pass via tagIds on create_event.

Location

Events support three location modes — mutually exclusive:

  • In-person: pass address only
  • Virtual: pass virtualUrl only — shown to CONFIRMED participants only
  • None: omit both

Capacity and waitlist

  • Default: unlimited (omit participantsCapacity or set to 0)
  • When full + allowWaitlist: true → new joiners become WAITLISTED
  • When full + allowWaitlist: false → new joiners blocked
  • Use participantsRegistrationDeadlineEndAt to close registration at a specific time
  • Creator auto-joins: event creator is automatically added as CONFIRMED on creation — counts toward capacity

Participant visibility

Who can see?Condition
Event hostsAlways
All membersWhen showParticipants: true
Editors / admins / event authorAlways

Create defaults

FieldDefault
statusPUBLISHED
reminderHoursBeforeStart24
showParticipantsfalse
participantsCapacityunlimited
locationnone

Use update_event to change an existing event. Pass only the fields to change — others are left as-is. tagIds and hostIds replace the full set when provided.

Access rules

OperationReaderEditorOrg Admin+
List / get events
Create / update event
Cancel event
Delete / duplicate event
Join / cancel participation
List participants✅*
Export participants CSV

*Reader only if showParticipants: true or user is a host.

Concepts & Features

Categories

Coming soon

Detailed documentation for Categories (template-based cards) is being written.

Concepts & Features

User Directory

Coming soon

Detailed documentation for User Directory is being written.

Concepts & Features

Leaderboard

Coming soon

Detailed documentation for Leaderboard is being written.

API Reference

Organisation

Four operations for reading and managing your tchop organisation.

Response conventions (apply across all tools):

deeplinkCreate & update operations for cards, channels, mixes and events return a deeplink — the shareable app/web URL for the object, so you can eyeball what was created. Best-effort (omitted if it can't be built).
_tenantEvery mutation (create / update / delete / send / etc.) echoes _tenant: { org, server } — the resolved organisation subdomain and environment the call actually hit. Confirm you targeted the intended tenant.
verbosityLarge reads (get_organisation, list_channels, list_mixes) accept verbosity: "compact" | "full". Default full (unchanged output); compact strips empty/null/default fields to save tokens. list_channels and list_mixes additionally accept verbosity: "summary" (id/name/status essentials only) plus page + pageSize (max 200, default 50) — use both on large organisations where full lists exceed client token limits.
audience / notesend_push_notification and send_in_app_message return the audience reached plus a note on reversibility (an immediate broadcast has no unsend; only scheduled sends can be cancelled).
get_organisation
Query Reader

Fetch full organisation details including all settings, security configuration, and app tab layout.

Parameters
none required
The organisation is derived from your TCHOP_ORG_URL / x-tchop-webapp-organisation header.
Response fields
id
Int
Immutable integer org ID
name
String
Organisation display name
subdomain
String
URL-safe subdomain slug
uuid
String
Immutable UUID
appLockEnabled
Boolean
Whether app PIN lock is required
chatPromotionEnabled
Boolean
Whether chat promo banner is shown
twoFactorForce
String
2FA enforcement level: ALL / ALL_ADMINS / ALL_EDITORS / ORG_ADMINS / NONE
sessionExpirationTime
String
Web session expiry: DAY / WEEK / MONTH / THREE_MONTHS / FOREVER
invitingIntoDefaultChannelsEnabled
Boolean
Auto-add new users to default channels
defaultChannelUserRoleId
Int
Role ID for default channel membership
restrictDuplicateUrlOfCards
String
Duplicate URL restriction window enum
settings.locationPlacesSearchEnabled
Boolean
Location-based places search feature flag
permissions
Object
5 boolean capability flags for the authenticated user
appTabsSettings
Object
Array of app tab configuration objects
Errors
401 Unauthorized
Invalid or expired auth token
404 Not Found
Wrong org header value — organisation not found
Example
// MCP call
{ "name": "get_organisation", "arguments": {} }
update_organisation
Mutation Org Admin

Update organisation settings. All parameters are optional — only supplied fields are modified.

Parameters
nameoptional
String
Organisation display name
subdomainoptional
String
Breaking change — may invalidate existing sessions
localeIdoptional
String
Default locale code, e.g. en, de
appLockEnabledoptional
Boolean
Enable app PIN lock screen
chatPromotionEnabledoptional
Boolean
Show chat promotion banner
invitingIntoDefaultChannelsEnabledoptional
Boolean
Auto-add new users to default channels
defaultChannelIdsoptional
[Int]
Channel IDs for new user auto-join
defaultChannelUserRoleIdoptional
Int
Role ID for default channel membership
twoFactorForceoptional
Enum
ALL / ALL_ADMINS / ALL_EDITORS / ORG_ADMINS / NONE
sessionExpirationTimeoptional
Enum
DAY / WEEK / MONTH / THREE_MONTHS / FOREVER
sessionExpirationTimeAppoptional
Enum
Same enum values as sessionExpirationTime, for mobile app sessions
restrictDuplicateUrlOfCardsoptional
Enum
NEVER / ONE_HOUR / SIX_HOURS / TWELVE_HOURS / ONE_DAY / THREE_DAYS / ONE_WEEK
appTabsoptional
Array
Array of tab config objects with type, enabled, and optional labels
appTabsDeepUpdateoptional
Boolean
true = merge into existing tabs; false = replace all tabs
Errors
SUBDOMAIN_TAKEN
Requested subdomain is already in use
INVALID_LOCALE
Provided localeId not recognised
INVALID_CHANNEL_IDS
One or more defaultChannelIds don't belong to this org
NO_FIELDS_PROVIDED
Empty arguments object — no changes applied
403 Forbidden
Insufficient role — Org Admin or higher required
create_organisation
Mutation Staff only
Restricted operation. Only internal tchop staff tokens can call this. Enterprise customers receive a 403 permission error. Documented for completeness and tchop internal tooling.

Create a new tchop organisation with a unique name and subdomain.

Parameters
namerequired
String
Organisation display name, 3–24 characters
subdomainrequired
String
URL-safe subdomain, 3–24 characters, alphanumeric only. Must be globally unique.
Response
id
Int
New organisation ID
name
String
Organisation name as provided
subdomain
String
Subdomain as provided
url
String
Full org URL, e.g. https://acme.tchop.io
role
Object
Assigned role: { id, name } — creator becomes Org Owner
ownerName
String
Display name of the org owner
Errors
SUBDOMAIN_TAKEN
Subdomain already in use by another organisation
SUBDOMAIN_INVALID
Subdomain contains special characters or spaces
NAME_TOO_SHORT
Name is fewer than 3 characters
NAME_TOO_LONG
Name exceeds 24 characters
403 Forbidden
Non-staff token — permission denied
Example
{ "name": "create_organisation", "arguments": { "name": "Acme Corp", "subdomain": "acmecorp" } }
delete_organisation
Mutation Staff only
Staff only. Performs a soft-delete of the organisation. Requires confirm: true. Contact support to reverse.

Soft-delete the current organisation. The operation is reversible only by tchop staff support. Requires a staff-level token and explicit confirmation.

Parameters
confirmrequired
Boolean
Must be explicitly true. The MCP will prompt for confirmation before executing.
Response
deleted
Boolean
true on success
Errors
CONFIRMATION_REQUIRED
confirm not provided or set to false
403 Forbidden
Non-staff token — operation denied immediately
404 Not Found
Organisation does not exist
MCP call
{ "name": "get_organisation",
  "arguments": {} }
Response
{ "id": 102,
  "name": "Acme Corp",
  "subdomain": "acme",
  "locale": "en-US",
  "appLockEnabled": false,
  "twoFactorForce": "NONE",
  "sessionExpirationTime": "FOREVER",
  "restrictDuplicateUrlOfCards": "NEVER",
  "permissions": { ... },
  "appTabsSettings": { ... }
}
Required headers
x-tchop-webapp-organisation: <your-subdomain>
x-tchop-auth-token: <your-token>
x-tchop-api-client-id: <your-client-id>
Query
query GetOrganisation {
  organisation {
    id name subdomain uuid locale localeId
    appLockEnabled chatPromotionEnabled
    twoFactorForce
    sessionExpirationTime
    sessionExpirationTimeApp
    invitingIntoDefaultChannelsEnabled
    defaultChannelUserRoleId
    restrictDuplicateUrlOfCards
    pinnedStoryId
    role { id }
    settings { locationPlacesSearchEnabled }
    permissions {
      chatGroupAllowToCreate
      chatP2PAllowToCreate
      organisationPushNotificationAllowToCreate
      organisationTagAllowToManage
    }
    appTabsSettings {
      initialAppTabId
      appTabs {
        id
        action { type }
        title { language localization }
        iconUrl { x1 x3 }
      }
    }
  }
}
API Reference

Channels

list_channels
Query Reader

Get all channels in the organisation (sorted by position), or look up a single channel by id or subdomain. Returns full channel details including settings, permissions, and notification preferences.

Parameters
idoptional
Int
Get a specific channel by numeric ID.
subdomainoptional
String
Get a specific channel by subdomain (e.g. company-news).
excludeArchivedoptional
Boolean
Hide archived channels. Default: false.
onlyDefaultoptional
Boolean
Return only channels marked as default.
Errors
ChannelNotFoundError
Subdomain lookup returned no channel
Example
// List all
{ "name": "list_channels", "arguments": {} }
// By name
{ "name": "list_channels", "arguments": { "name": "Company News" } }
create_channel
Mutation Org Admin

Create a new channel. Only name and subdomain required — locale and content settings default from the organisation.

Parameters
namerequired
String
Channel display name.
subdomainrequired
String
Unique subdomain (min 3 chars, lowercase, letters/numbers/hyphens). Channel URL: <subdomain>-<org>.tchop.io
localeoptional
en / de
Channel language. Defaults to org language.
restrictDuplicateUrlOfCardsoptional
Enum
NEVER / ONE_HOUR / THREE_HOURS / SIX_HOURS / HALF_DAY / ONE_DAY / TWO_DAYS / THREE_DAYS / ONE_WEEK. Default: NEVER.
Errors
ChannelDuplicateSubdomainError
Subdomain already taken in this organisation
ChannelSubdomainMalformedError
Invalid subdomain (spaces, uppercase, special chars)
ChannelValidationError
Field-level validation failure (details provided)
Example
{ "name": "create_channel", "arguments": { "name": "Company News", "subdomain": "company-news", "locale": "en" } }
update_channel
Mutation Org Admin / Channel Admin

Update channel settings. All fields optional — only provided fields change. Includes position management (calls channelUpdatePosition automatically when position is provided).

Parameters
channelIdrequired
Int
Channel to update. Get IDs from list_channels.
nameoptional
String
[General] New display name.
subdomainoptional
String
[General] ⚠ Breaking — changes channel URL immediately.
localeoptional
en / de
[General] Channel language.
isArchivedoptional
Boolean
[General] Archive (hide) or unarchive. Content is preserved.
restrictDuplicateUrlOfCardsoptional
Enum
[General] Duplicate URL block window.
positionoptional
Int
[Position] 0-indexed position in org sidebar. 0 = first.
Errors
ChannelNotFoundError
Channel with this ID does not exist
ChannelDuplicateSubdomainError
New subdomain already taken
ChannelSubdomainMalformedError
Invalid subdomain format
delete_channel
Mutation Org Admin
Permanent. Deletes the channel and ALL its mixes and cards. Requires confirm: true. Use update_channel(isArchived: true) for reversible hiding.

Permanently delete a channel and all its content. Cannot be undone.

Parameters
channelIdrequired
Int
Channel to delete.
confirmrequired
true
Must be explicitly true — safety check.
Errors
ChannelNotFoundError
Channel does not exist or already deleted
MCP — list all
{ "name": "list_channels",
  "arguments": {} }
MCP — by name
{ "name": "list_channels",
  "arguments": {
    "name": "Company News"
} }
Response (sample)
{ "channels": [
  { "id": 123,
    "name": "Company News",
    "subdomain": "company-news",
    "position": 0,
    "isArchived": false,
    "readersCount": 45,
    "curatorsCount": 3,
    "createdAt": "2026-01-15T10:00:00.000Z",
    "webappUrl": "https://company-news-acme.tchop.io/webapp"
  }
], "total": 3 }
Required headers
x-tchop-webapp-organisation: <your-subdomain>
x-tchop-auth-token: <your-token>
x-tchop-api-client-id: <your-client-id>
Query
query ListChannels(
  $filters: GetChannelsFiltersArgsType
) {
  channels(filters: $filters) {
    id name subdomain locale localeId
    position isArchived isDefault
    readersCount curatorsCount
    createdAt accessedAt
    webappUrl dashboardUrl
    restrictDuplicateUrlOfCards
    role { id }
    image { id url }
    permissions {
      storyAllowToCreate
      channelPushNotificationAllowToCreate
    }
    appTabsSettings {
      initialAppTabId
      appTabs { action { type } title { language localization } }
    }
  }
}
Variables
// Optional filters
{
  "filters": {
    "excludeArchived": true
  }
}
API Reference

Mixes

list_mixes
Query Reader

List mixes in a channel, get a single mix by ID, filter by name, or fetch all mixes across the organisation. Always returns full settings including display config, push settings, tags, pinned slots.

Parameters
channelIdoptional
Int|String
Channel — numeric ID, name, or subdomain (e.g. 2259, "opium", "company-news").
storyIdoptional
Int|String
Single mix — numeric ID or name. When using name, also provide channelId.
nameoptional
String
Filter by name — case-insensitive partial match.
orgWideoptional
Boolean
Return all mixes across the org, grouped by channel.
statusoptional
Enum[]
PUBLISHED or UNPUBLISHED.
typeoptional
Enum
STANDARD or READONLY.
outputoptional
Enum
Filter by display location: WEB_FEED · MOBILE_FEED · MIX_LIST..5 · PINNED..5 · HORIZONTAL · UNASSIGNED
accessoptional
Enum
EDITOR_LIMITED · READER · UNASSIGNED
onlyPostInoptional
Boolean
Return only mixes where the current user can post cards.
Example
{ "name": "list_mixes", "arguments": { "channelId": 2259 } }
{ "name": "list_mixes", "arguments": { "channelId": 2259, "output": "MIX_LIST" } }
{ "name": "list_mixes", "arguments": { "storyId": 61218 } }
create_mix
Mutation Org Admin / Editor

Create a new standard mix with full settings — display output, interaction flags, push notifications, tags, image and more. Only channelId and title required.

Required parameters
channelIdrequired
Int|String
Channel — numeric ID, name, or subdomain.
titlerequired
String
Mix title.
Key optional parameters
displayInMixTabs
Int[]
Mix tabs to appear in (1–5). Default: [1].
pinInSlot
Int (1–5)
Pin in a pinned slot.
pinScope
Enum
channel or organisation. Default: channel.
imageUrl
URL
Public image URL — auto-resolved to imageId via storyCardParseUrl.
tagIds / hiddenTagIds / statusTagIds
String|Int
Visible, hidden, status tags — pass names or IDs.
autoNotificationEnabled
Boolean
Enable auto push. Always provide titlePrefix, titleDefault, messageDefault when enabling.
cardOrder
Enum
Sort order for cards in the feed. MANUAL (default) — editor position · POSTED_TIME — newest first. Standard mixes only.
allowEditorLimitedToPost / allowReaderToPost
Boolean
Extend posting rights to lower roles.
sourceStoryId
Int|String
[Sync] Source mix to connect to — ID or name. Use when shouldSyncItemPositionsOnConnectedStories is enabled.
copyFromSource
Boolean
[Sync] Copy title, subtitle and image from the source mix.
Notes
pinned
Returns null in create response — saved correctly. Verify via list_mixes.
autoNotification
Returns null in create response — feature works. Verify via list_mixes or update_mix response.
Example
{ "name": "create_mix", "arguments": {
  "channelId": 2259,
  "title": "Breaking News",
  "imageUrl": "https://example.com/img.jpg",
  "displayInMixTabs": [1, 2],
  "autoNotificationEnabled": true,
  "autoNotificationTitlePrefix": "[BREAKING]",
  "autoNotificationTitleDefault": "New story published",
  "autoNotificationMessageDefault": "Check the latest update",
  "tagIds": ["Breaking", "World"]
} }
update_mix
Mutation Org Admin / Editor

Update any mix field — title, status, display settings, image, tags, push, source sync. All fields optional. Only provided fields change.

Required parameters
storyIdrequired
Int|String
Mix to update — numeric ID or name. When using name, provide channelName too.
Key optional parameters
title / subtitle / status
String / Enum
Basic fields.
position
Int
0-indexed position in channel mix list.
includeInWebappNewsFeed / includeInNewsFeed
Boolean
Show in Web App / Mobile App News Feed.
displayInMixTabs
Int[]
Mix tabs to appear in (1–5).
shouldUpdatePostedTimeByComments
Boolean
Update card posted time when a new comment is added.
cardOrder
Enum
Sort order for cards in the feed. MANUAL (default) — editor position · POSTED_TIME — newest first. Standard mixes only.
imageUrl
URL
New cover image URL — auto-resolved.
sourceStoryId
Int|String
Connect this mix to a source mix for syncing — ID or name.
copyFromSource
Boolean
Copy title, subtitle, image from source mix. Clears image if source has none.
tagIds / hiddenTagIds / statusTagIds
String|Int
Replace tags — pass names or IDs.
Example
{ "name": "update_mix", "arguments": {
  "storyId": 61218,
  "title": "Updated Title",
  "status": "PUBLISHED",
  "tagIds": ["Breaking"],
  "hiddenTagIds": ["internal-tag"],
  "statusTagIds": ["Approved"]
} }
delete_mix
Mutation Org Admin

Permanently delete a mix and ALL its cards. Cannot be undone. Requires confirm: true.

Parameters
storyIdoptional
Int
Numeric mix ID. Use this OR name.
nameoptional
String
Mix name (exact match). Requires channelId.
channelIdoptional
Int|String
Channel — numeric ID, name, or subdomain. Required when looking up by name.
confirmrequired
true
Must be explicitly true. Permanent action.
Example
{ "name": "delete_mix", "arguments": { "storyId": 61220, "confirm": true } }
{ "name": "delete_mix", "arguments": { "name": "Old Mix", "channelId": 2259, "confirm": true } }
MCP call
{ "name": "list_mixes",
  "arguments": {
    "channelId": 2259,
    "status": ["PUBLISHED"],
    "output": "MIX_LIST"
} }
Required headers
x-tchop-webapp-organisation: <your-subdomain>
x-tchop-auth-token: <your-token>
x-tchop-api-client-id: <your-client-id>
Query
query ListMixes(
  $channelId: Int!,
  $filter: GetStoriesByChannelIdFilterArgs
) {
  storiesByChannelId(
    channelId: $channelId,
    filter: $filter
  ) {
    items {
      id title subtitle status type
      isReadOnly channelId position cardsCount
      pinned pinned2 pinned3 pinned4 pinned5
      image { id url }
      organisationTags { id name type }
      settings {
        includeInWebappNewsFeed includeInNewsFeed
        includeInMixTab includeInMixTab2
        includeInMixTab3 includeInMixTab4 includeInMixTab5
        allowComments allowCardReactions allowSharing
        highlightingComments cardLimit
        displayItemUpdatedTime cardOrder
        cardStyles { backgroundColor article { teaserStyle } }
        autoNotification { enabled titlePrefix titleDefault messageDefault }
        horizontalDisplayView { position }
      }
    }
  }
}
Variables
{
  "channelId": 2259,
  "filter": {
    "status": ["PUBLISHED"],
    "section": "MIX_LIST"
  }
}
API Reference

Cards

Create, read, update, delete, and manage the full lifecycle of cards in any mix.

create_card
Mutation Editor (or mix setting)

Create a card (also called: content, post, item) in a STANDARD mix. Handles all 11 card types. Smart URL auto-detection: omit cardType and provide a URL — social links → QUOTE, podcasts.apple.com → AUDIO, other URLs → ARTICLE. Read-only mixes are rejected immediately — pass a standard mix ID.

Core parameters
storyIdrequired
Int
Mix ID — must be a STANDARD mix. Use list_mixes to find IDs.
cardTypeoptional
Enum
EDITORIAL · IMAGE · VIDEO · PDF · AUDIO · ARTICLE · QUOTE · POST · THREAD · POLL · EVENT. Omit when providing url for auto-detection.
urloptional
URL
URL for auto-detection (social → QUOTE, Apple Podcast → AUDIO, other → ARTICLE). Also the content URL for ARTICLE and QUOTE cards.
statusoptional
Enum
PUBLISHED (default) · DRAFTED · SCHEDULED · UNPUBLISHED
scheduledAtoptional
ISO 8601
Required when status=SCHEDULED. Example: 2026-06-20T09:00:00Z
Content fields
headlineoptional
String
Comment text shown above card. Only set when user explicitly says "comment text". NOT the card title. Max 360 chars. Never pass empty string.
textoptional
String
Body text. Mandatory for EDITORIAL. Optional description/caption for IMAGE, VIDEO, PDF, AUDIO. Max 20000 chars. For QUOTE: overrides auto-parsed quote text (max 2000 chars).
titleoptional
String
Title for ARTICLE, POST, THREAD cards. Required for POST, THREAD. For ARTICLE: auto-detected via storyCardParseUrl → OG/meta fallback — provide explicitly to override. Max 160 chars.
descriptionoptional
String
Abstract for ARTICLE and POST. Auto-populated from parse for ARTICLE. Max 700 chars.
source / sourceNameoptional
String
Source attribution name. For ARTICLE: auto-detected via storyCardParseUrl → OG/meta fallback — provide explicitly to override. Max 160 chars.
authoroptional
String
Content author name for ARTICLE and POST. Max 60 chars.
subHeadlineoptional
String
Subtitle line for EDITORIAL, IMAGE, VIDEO, PDF, AUDIO. Max 360 chars.
markdownoptional
String
Body content as Markdown for POST — auto-converted to Editor.js blocks. Preferred over contentBlocks.
contentBlocksoptional
Object[]
Raw Editor.js content blocks for POST. Used only when markdown not provided.
Media fields
imageUrlsoptional
URL[]
Required for IMAGE (1–20 URLs). 1 = image card, 2–20 = gallery. Also the teaser image for ARTICLE, POST.
imageCaptionsoptional
String[]
Caption per image (parallel array matched by index). Max 500 chars each.
imageRightholdersoptional
String[]
Rightholder per image (parallel array). Max 200 chars each.
imageCopyrightStatusesoptional
Enum[]
CC · LICENSED · SUBLICENSED · UNKNOWN (per image, parallel array)
videoUrloptional
URL
Required for VIDEO. Direct video file URL — uploaded to tchop.
pdfUrloptional
URL
Required for PDF. Direct PDF file URL — uploaded to tchop.
audioUrloptional
URL
Required for AUDIO. Direct audio URL or Apple Podcast episode URL. Apple Podcast URLs trigger RSS extraction.
teaserImageUrloptional
URL
Cover/teaser image for VIDEO, PDF, AUDIO cards.
teaserStyleoptional
Enum
Teaser layout for ARTICLE and POST: STANDARD · BIG_WITHOUT_TEXT · SMALL_WITHOUT_TEXT · SMALL_WITH_TEXT
Poll fields
questionoptional
String
Required for POLL. Poll question text. Max 360 chars.
optionsoptional
String[]
Required for POLL. Answer options (min 2, max 12, each max 100 chars).
multipleVotesoptional
Boolean
Allow multiple option selections. Default: false.
allowAnonymousoptional
Boolean
Allow anonymous voting. Default: true.
showVoteResultsoptional
Boolean
Show results to voters. Default: true.
showTotalVotesoptional
Boolean
Show total vote count. Default: true.
Card settings & tags
eventIdoptional
Int
Required for EVENT. Existing event ID to link. Use get_events to find IDs.
allowComment / allowReaction / allowSharingoptional
Boolean
Per-card overrides for mix settings.
allowDisplayAuthoroptional
Boolean
Show/hide author on this card.
backgroundColoroptional
Hex
Background color for this card (e.g. #FF5733). Max 7 chars.
externalTagIdsoptional
Int[]
External (visible) tag IDs to assign.
internalTagIdsoptional
Int[]
Internal (hidden) tag IDs. Editor+ only.
statusTagIdoptional
Int
Status tag ID. One per card (editorial workflow state).
mentionedUserIdsoptional
Int[]
User IDs to mention in this card.
categoryIdoptional
Int
Card category ID.
localeoptional
String
Card language: en or de. Defaults to the mix language.
Errors
StoryReadOnlyAccessError
Mix is read-only — card creation blocked. Caught post-mutation. Use a STANDARD mix. Check isReadOnly field via list_mixes.
status=SCHEDULED + no scheduledAt
MCP rejects immediately: "status=SCHEDULED requires 'scheduledAt' … Use status=DRAFTED to save without scheduling." API would silently create UNPUBLISHED draft without this guard.
StoryCardPostContentValidationError
Required content fields missing or invalid (e.g. <2 poll options, empty POST title)
Example
{ "name": "create_card", "arguments": {
  "storyId": 61218,
  "cardType": "EDITORIAL",
  "text": "Breaking news update!",
  "status": "PUBLISHED"
} }
get_card
Query Reader

Get a single card, list all cards in a mix, or search cards in a channel. Mode depends on which inputs are provided. Supports rich filtering by status, type, date, author, section, and tags. Access: Reader and Editor Limited can only see PUBLISHED cards — status filter is locked to PUBLISHED; fetching an unpublished card by ID returns Access denied.

Parameters
storyIdoptional
Int
Mix ID. Required for single card or list mode.
storyCardIdoptional
Int
Card ID. Provide with storyId for single card mode.
channelIdoptional
Int
Channel ID. Required for search mode.
queryoptional
String
Search query. Triggers search mode (requires channelId). Must be non-empty — empty string returns 0 results.
page / sizeoptional
Int
Pagination. Default: 1 / 10. Max size: 15 (API hard cap).
orderDirectionoptional
Enum
ASC or DESC. Default: DESC. List mode only.
statusoptional
Enum[]
Filter by status: PUBLISHED · UNPUBLISHED · SCHEDULED. Note: DRAFTED filter has no effect — returns all cards.
storyCardTypeoptional
Enum[]
Filter by type: EDITORIAL · IMAGE · VIDEO · PDF · AUDIO · ARTICLE · QUOTE · POST · THREAD · POLL · EVENT
from / tooptional
ISO 8601
Date range filter. Matches postedAt field. Both are optional and can be combined.
authorIdoptional
Int[]
Filter by author user ID(s).
sectionoptional
Enum[]
Filter by newsfeed placement: WEB_FEED · MOBILE_FEED · MIX_LIST
externalTagIds / internalTagIdsoptional
Int[]
Filter by tag IDs.
onlyCompletedMediaoptional
Boolean
Skip cards with unprocessed media attachments.
Modes
Single card
storyId + storyCardId → calls storyCardById. Returns full gallery for IMAGE, VIDEO (video+image), AUDIO (audio+image), PDF (pdf+image+thumb), POLL (image), ARTICLE (image).
List cards
storyId only → calls storyCardsFeed (supports all filters above). Returns summary fields only — no gallery.
Search
channelId + non-empty query → calls storyCardsChannelSearch
Example
{ "name": "get_card", "arguments": { "storyId": 61218, "storyCardId": 99042 } }
{ "name": "get_card", "arguments": { "storyId": 61218 } }
{ "name": "get_card", "arguments": { "storyId": 61218, "status": ["PUBLISHED"], "storyCardType": ["EDITORIAL"] } }
{ "name": "get_card", "arguments": { "storyId": 61218, "from": "2026-06-15T00:00:00Z", "to": "2026-06-15T23:59:59Z" } }
{ "name": "get_card", "arguments": { "channelId": 2259, "query": "annual report" } }
set_card_status
Mutation Editor

Change a card's status. Synonyms: publish card, unpublish card, draft card, schedule card. SCHEDULED requires scheduledAt. To cancel a schedule, pass status: UNPUBLISHED + scheduledAt: null.

Parameters
storyIdrequired
Int
Mix containing the card.
storyCardIdrequired
Int
Card to update.
statusrequired
Enum
PUBLISHED · DRAFTED · SCHEDULED · UNPUBLISHED
scheduledAtoptional
ISO 8601
Required when status=SCHEDULED. Must be ≥30 seconds in future.
Errors
StoryCardNotFoundError
Card does not exist in specified mix
StoryReadOnlyAccessError
Mix is read-only — status updates blocked
Cannot change SCHEDULED card
Use update_card with scheduledAt: null + status: UNPUBLISHED to cancel
Example
{ "name": "set_card_status", "arguments": { "storyId": 61218, "storyCardId": 99042, "status": "PUBLISHED" } }
{ "name": "set_card_status", "arguments": { "storyId": 61218, "storyCardId": 99042, "status": "SCHEDULED", "scheduledAt": "2026-06-20T09:00:00Z" } }
delete_card
Mutation Editor+

Permanently delete a card from a mix. This action is irreversible. Requires confirm: true.

Irreversible. Permanently removes the card. Cannot be undone. The MCP requires confirm: true as a safety check.
Parameters
storyIdrequired
Int
Mix containing the card.
storyCardIdrequired
Int
Card to delete.
confirmrequired
true
Must be explicitly true. Prevents accidental deletion.
Response
deleted
Boolean
true on success
storyCardId
Int
ID of the deleted card
Errors
CONFIRMATION_REQUIRED
confirm not true
StoryCardNotFoundError
Card not found in specified mix
StoryReadOnlyAccessError
Mix is read-only — deletion blocked
Example
{ "name": "delete_card", "arguments": { "storyId": 61218, "storyCardId": 99042, "confirm": true } }
update_card
Mutation Editor

Update any field on an existing card. For short post types (EDITORIAL/IMAGE/VIDEO/PDF/AUDIO), the MCP fetches the current card first and merges only the fields you provide — unset fields are preserved automatically. Provide storyId + storyCardId + cardType plus whichever fields to change. Read-only mixes are rejected immediately.

Parameters
storyIdrequired
Int
Mix containing the card.
storyCardIdrequired
Int
ID of the card to update.
cardTyperequired
Enum
Must match the card's current type (or a new type to convert). EDITORIAL · IMAGE · VIDEO · PDF · AUDIO · ARTICLE · QUOTE · POLL · THREAD · EVENT · POST
(all create_card fields)optional
Same field set as create_card. For short post types: existing values are preserved for any field not supplied. For POST (Long post): markdown or contentBlocks required — API requires contentBlocks even on updates.
imageUrls[]optional
string[]
IMAGE only. 1–20 URLs to replace the entire gallery. Single imageUrl replaces only slot 0, preserving remaining images.
imageCaptions[] / imageRightholders[] / imageCopyrightStatuses[]optional
string[]
IMAGE gallery. Parallel arrays matched by index to imageUrls[].
videoTitle / pdfTitle / audioTitleoptional
string
Caption shown with the media player — stored as gallery[0].title. Use instead of text for caption. text = card body field.
audioUseDefaultThumboptional
boolean
AUDIO only. Pass true to remove an existing cover image and revert to platform default.
quoteoptional
string
QUOTE only. Post/quote content text. Max 2000 chars.
Notes
Field preservation
Short post types (EDITORIAL/IMAGE/VIDEO/PDF/AUDIO): MCP fetches current card first — unset params fall back to existing values. Only supply what you want to change.
POST card update
Always provide markdown or contentBlocks — API requires contentBlocks even for updates
Type change
Updating with a different cardType converts the card in-place (same ID). Short post types are fully interconvertible.
Example
{ "name": "update_card", "arguments": {
  "storyId": 61218,
  "storyCardId": 99042,
  "text": "Updated announcement text."
} }
pin_card
Mutation Editor

Pin or unpin a card. Calling again on an already-pinned card unpins it (toggle). Two targets: story pins to top of the mix feed; newsfeed pins to the org-wide newsfeed.

Parameters
storyIdrequired
Int
Mix containing the card.
storyCardIdrequired
Int
Card to pin/unpin.
targetrequired
Enum
story = pin in mix/story feed. newsfeed = pin in org newsfeed.
Response
pinned
Boolean
Current pin state after toggle (read-back from API).
storyCardId
Int
Card ID.
target
String
The target used.
Errors
StoryCardNotFoundError
Card does not exist in specified mix
StoryCardNewsFeedPinnedStatusError
Pin operation failed
Example
{ "name": "pin_card", "arguments": { "storyId": 61218, "storyCardId": 99042, "target": "story" } }
{ "name": "pin_card", "arguments": { "storyId": 61218, "storyCardId": 99042, "target": "newsfeed" } }
move_card
Mutation Editor

Move a card from one mix to another. The card is removed from the source mix and placed in the target mix. Source and target must be different mixes.

Parameters
sourceStoryIdrequired
Int
Mix the card currently lives in.
sourceStoryCardIdrequired
Int
Card to move.
targetStoryIdrequired
Int
Destination mix (must be STANDARD and different from source).
Errors
SourceAndTargetStoryAreEqualErrorType
Source and target mix are the same — use a different targetStoryId
StoryReadOnlyAccessError
Target mix is read-only
Example
{ "name": "move_card", "arguments": {
  "sourceStoryId": 61218,
  "sourceStoryCardId": 99042,
  "targetStoryId": 61220
} }
set_card_position
Mutation Editor

Reorder a card within its mix feed. Position is 0-indexed (0 = top of feed). Passing a position beyond the feed length moves the card to the end. Only works on mixes with cardOrder = MANUAL — on POSTED_TIME mixes, position is read-only (sorted by newest-first). The API normalizes the requested position to the nearest valid slot.

Parameters
storyIdrequired
Int
Mix containing the card.
storyCardIdrequired
Int
Card to reposition.
positionrequired
Int
Target position (0-indexed). 0 = top. Values beyond feed length move card to end.
Errors
StoryCardPositionOutOfBoundsError
Position is negative
StoryCardPositionNotChangedError
Card already at requested position
StoryCardNotFoundError
Card not found in mix
Example
{ "name": "set_card_position", "arguments": { "storyId": 61218, "storyCardId": 99042, "position": 0 } }
set_card_newsfeed_visibility
Mutation Editor

Control whether a card appears in the mobile app newsfeed or web app newsfeed. Independent of card status — a PUBLISHED card can be hidden from newsfeeds while still visible in its mix. Read back current visibility via the section field on the card: WEB_FEED = in webapp newsfeed, MOBILE_FEED = in mobile newsfeed, MIX_LIST = mix only.

Parameters
storyIdrequired
Int
Mix containing the card.
storyCardIdrequired
Int
Card to update.
includeInNewsFeedoptional
Boolean
Show/hide in the mobile app newsfeed.
includeInWebappNewsFeedoptional
Boolean
Show/hide in the web app newsfeed.
Example
{ "name": "set_card_newsfeed_visibility", "arguments": {
  "storyId": 61218,
  "storyCardId": 99042,
  "includeInNewsFeed": false,
  "includeInWebappNewsFeed": false
} }
assign_tags_to_card
Mutation Editor

Assign or replace tags on a card. Sends all three tag arrays in one call — each provided array replaces the current tags of that type. Omit an array to leave that tag type unchanged.

Parameters
storyIdrequired
Int
Mix containing the card.
storyCardIdrequired
Int
Card to tag.
externalTagIdsoptional
Int[]
External (visible) tag IDs. Replaces all current external tags. Pass [] to clear.
internalTagIdsoptional
Int[]
Internal (hidden) tag IDs. Editor+ only. Replaces all current internal tags.
statusTagIdoptional
Int
Status tag ID (one per card). Pass null to clear the status tag.
Errors
OrganisationTagNotFoundError
One or more tag IDs not found in org tag list
Example
{ "name": "assign_tags_to_card", "arguments": {
  "storyId": 61218,
  "storyCardId": 99042,
  "externalTagIds": [173, 190],
  "statusTagId": 205
} }
react_to_card
Mutation Reader+

Add or remove a reaction on a card. Calling with the same reaction twice toggles it off. Reaction values must be lowercaselike, love, haha, wow, sad, angry. One reaction per user per card.

Parameters
storyIdrequired
Int
Mix containing the card.
storyCardIdrequired
Int
Card to react to.
reactionrequired
Enum
Must be one of: like, love, haha, wow, sad, angry (all lowercase). Uppercase is rejected.
Response
storyCardId
Int
Card ID
reaction
String
The reaction used
myReaction
String
Current user's active reaction (null if removed)
reactions
Array
Updated reaction counts: [{ name, count }]
Errors
Invalid reaction
Reaction value not in accepted list or is uppercase — must be like/love/haha/wow/sad/angry
Example
{ "name": "react_to_card", "arguments": { "storyId": 61218, "storyCardId": 99042, "reaction": "like" } }
repost_card
Mutation Reader+

Repost a card in its current mix. Creates a new card entry in the same mix that references the original. Useful for re-surfacing older content. Works even on DRAFTED cards.

Parameters
storyIdrequired
Int
Mix containing the card.
storyCardIdrequired
Int
Card to repost.
Response
id
Int
New card ID (the repost)
type
String
Card type of the reposted card
storyId
Int
Mix ID
Example
{ "name": "repost_card", "arguments": { "storyId": 61218, "storyCardId": 99042 } }
post_card_as_copy
Mutation Editor

Post an independent copy of a card into a different mix. The copy is not linked to the original — edits to one do not affect the other. Source and target must be different mixes.

Parameters
sourceStoryIdrequired
Int
Mix containing the original card.
sourceStoryCardIdrequired
Int
Card to copy.
targetStoryIdrequired
Int
Destination mix (must be different from source).
Errors
SAME_STORY
MCP guard: source and target mix are the same — API allows it but MCP blocks it
SourceStoryCardNotFoundError
Source card not found
StoryCardUrlUniquenessConflictError
URL uniqueness enforced at org level (org setting)
Example
{ "name": "post_card_as_copy", "arguments": {
  "sourceStoryId": 61218,
  "sourceStoryCardId": 99042,
  "targetStoryId": 61225
} }
post_card_as_sync
Mutation Editor

Post a live-linked sync of a card into a different mix. Changes to the source card automatically propagate to all synced copies. Source and target must be different mixes. Use post_card_as_copy for an independent clone.

Parameters
sourceStoryIdrequired
Int
Mix containing the original card.
sourceStoryCardIdrequired
Int
Card to sync.
targetStoryIdrequired
Int
Destination mix (must be different from source).
Errors
SourceAndTargetStoryAreEqualErrorType
Source and target mix are the same
SourceStoryCardNotFoundError
Source card not found
Example
{ "name": "post_card_as_sync", "arguments": {
  "sourceStoryId": 61218,
  "sourceStoryCardId": 99042,
  "targetStoryId": 61230
} }
convert_card
Mutation Editor

Convert an existing card to a different type — e.g. article to long post, article to image card, short post interconversion. Fetches the current card first and preserves all metadata. For ARTICLE→POST, re-parses the URL for full content blocks. For ARTICLE→IMAGE, re-parses the URL for the full image gallery. Conversion happens in place — same card ID.

Parameters
storyIdrequired
Int
Mix containing the card.
storyCardIdrequired
Int
Card to convert.
targetTyperequired
Enum
POST (long post), EDITORIAL (short text post), IMAGE, VIDEO, AUDIO, PDF, ARTICLE.
markdownoptional
String
Body for POST target — auto-converted to Editor.js blocks. For ARTICLE→POST, omit to auto-populate from article URL.
contentBlocksoptional
Object[]
Raw Editor.js blocks for POST target.
urloptional
String (URL)
Article URL — required for ARTICLE target.
imageUrloptional
String (URL)
Image to upload for IMAGE target. For ARTICLE→IMAGE, omit to auto-populate all images from the article URL.
videoUrloptional
String (URL)
Video to upload — required for VIDEO target.
audioUrloptional
String (URL)
Audio to upload — required for AUDIO target. Apple Podcast URLs not supported.
pdfUrloptional
String (URL)
PDF to upload — required for PDF target.
headline / title / abstract / sourceNameoptional
String
Override metadata. If omitted, values are preserved from the current card.
editorialTextoptional
String
Body text for EDITORIAL target. Falls back to current card's text, then abstract. Required only if neither exists.
localeoptional
String
en or de. Defaults to mix locale.
Conversion notes
ARTICLE → POST
No fields required — URL re-parsed automatically for full content blocks + teaser image
ARTICLE → EDITORIAL
No fields required — abstract used as body text auto-fallback
ARTICLE → IMAGE
No fields required — URL re-parsed for full image gallery. Provide imageUrl to override.
ARTICLE → VIDEO / AUDIO / PDF
Provide corresponding media URL
POST → EDITORIAL
Content blocks lost — irreversible
Short post ↔ short post
Fully interconvertible — IMAGE auto-fetches gallery; other types need media URL
Example
{ "name": "convert_card", "arguments": {
  "storyId": 61218,
  "storyCardId": 22140184,
  "targetType": "POST",
  "markdown": "## Title\n\nConverted from article."
} }
MCP — EDITORIAL card
{ "name": "create_card",
  "arguments": {
    "storyId": 61218,
    "cardType": "EDITORIAL",
    "text": "Quick update for the team.",
    "status": "PUBLISHED"
} }
MCP — URL auto-detect
{ "name": "create_card",
  "arguments": {
    "storyId": 61218,
    "url": "https://bbc.com/article/xyz",
    "title": "Breaking: Budget announced",
    "source": "BBC News"
} }
MCP — Poll
{ "name": "create_card",
  "arguments": {
    "storyId": 61218,
    "cardType": "POLL",
    "question": "What's your preferred work style?",
    "options": ["Office", "Remote", "Hybrid"]
} }
MCP — IMAGE card
{ "name": "create_card",
  "arguments": {
    "storyId": 61218,
    "cardType": "IMAGE",
    "imageUrls": ["https://cdn.example.com/photo.jpg"],
    "headline": "Photo from the event",
    "status": "PUBLISHED"
} }
MCP — VIDEO card
{ "name": "create_card",
  "arguments": {
    "storyId": 61218,
    "cardType": "VIDEO",
    "videoUrl": "https://cdn.example.com/video.mp4",
    "headline": "Product demo"
} }
MCP — AUDIO card
{ "name": "create_card",
  "arguments": {
    "storyId": 61218,
    "cardType": "AUDIO",
    "audioUrl": "https://cdn.example.com/episode.mp3",
    "title": "Episode 12",
    "subtitle": "My Podcast"
} }
MCP — PDF card
{ "name": "create_card",
  "arguments": {
    "storyId": 61218,
    "cardType": "PDF",
    "pdfUrl": "https://cdn.example.com/report.pdf",
    "headline": "Q3 Report"
} }
MCP — ARTICLE card (explicit)
{ "name": "create_card",
  "arguments": {
    "storyId": 61218,
    "cardType": "ARTICLE",
    "url": "https://bbc.com/article/xyz",
    "title": "Breaking: Budget announced",
    "source": "BBC News"
} }
MCP — QUOTE (Social) card
{ "name": "create_card",
  "arguments": {
    "storyId": 61218,
    "cardType": "QUOTE",
    "url": "https://x.com/user/status/123456"
} }
MCP — POST (Long post) card
{ "name": "create_card",
  "arguments": {
    "storyId": 61218,
    "cardType": "POST",
    "title": "Monthly update",
    "markdown": "## Summary\n\nKey highlights from this month..."
} }
MCP — THREAD card
{ "name": "create_card",
  "arguments": {
    "storyId": 61218,
    "cardType": "THREAD",
    "title": "AMA: Ask me anything"
} }
MCP — EVENT card
{ "name": "create_card",
  "arguments": {
    "storyId": 61218,
    "cardType": "EVENT",
    "eventId": 55012
} }
Required headers
x-tchop-webapp-organisation: <subdomain>
x-tchop-auth-token: <token>
x-tchop-api-client-id: <client-id>
Mutation
mutation PostCard($input: StoryCardPostInStoryInput!) {
  storyCardPostInStory(input: $input) {
    error { ... on UnknownError { kind message } }
    payload {
      id type status storyId postedAt
      content { __typename }
      author { id screenName }
      organisationTags { id name type }
    }
  }
}
Variables — EDITORIAL
{
  "input": {
    "storyId": 61218,
    "fields": {
      "editorialFields": {
        "text": "Quick update for the team."
      },
      "status": "PUBLISHED"
    }
  }
}
Variables — ARTICLE
{
  "input": {
    "storyId": 61218,
    "fields": {
      "articleFields": {
        "title": "Breaking: Budget announced",
        "url": "https://bbc.com/article/xyz",
        "sourceName": "BBC News",
        "abstract": "The chancellor presented..."
      },
      "status": "PUBLISHED"
    }
  }
}
API Reference

Comments

get_comments
QueryReader

Get comments on a card, replies to a comment, or a single comment by ID. Auto-paginates with fetchAll. cardId and storyId accept names or numeric IDs.

Parameters
cardIdoptional
Int|String
Card — numeric ID or headline. Required with storyId.
storyIdoptional
Int|String
Mix — numeric ID or name.
commentIdoptional
Int
Get a single comment by ID.
parentIdoptional
Int
Get replies to this top-level comment.
showHiddenoptional
Boolean
Include hidden comments. Default: false.
orderDirectionoptional
Enum
DESC (newest, default) or ASC (oldest).
fetchAlloptional
Boolean
Auto-paginate all pages (max 25/page). Default: false.
list_comments_feed
QueryReader

Channel or org-wide comment activity feed with full filters: date range, card type, mix, author, hidden. Auto-paginate with fetchAll (max 15/page).

Parameters
channelIdoptional
Int|String
Channel — ID, name, or subdomain.
orgWideoptional
Boolean
Loop all channels.
dateRangeoptional
Enum
24h · 7d · 30d · 3m · 6m · 12m
from / tooptional
String
Custom date range ISO 8601.
cardTypeoptional
Enum
ARTICLE · AUDIO · EVENT · IMAGE · PDF · POLL · POST · QUOTE · THREAD · VIDEO
storyIdoptional
Int|String
Filter by mix — ID or name.
authorIdoptional
Int
Filter by user ID.
showHiddenoptional
Boolean
Include hidden comments.
fetchAlloptional
Boolean
Auto-paginate (15/page API limit).
post_comment
MutationReader+

Post a comment to a card, or reply to a top-level comment. Supports text, image, video, or text + attachment. Replies are one level deep only.

Parameters
storyIdoptional
Int|String
Mix — optional, auto-resolved from cardId.
cardIdrequired
Int|String
Card — ID or headline.
contentoptional
String
Comment text. Emoji supported.
parentIdoptional
Int
Top-level comment ID to reply to. Cannot reply to a reply.
imageIdoptional
Int
Uploaded image attachment.
videoIdoptional
Int
Uploaded video attachment.
Error types
CommentMultilevelForbiddenError
Attempted reply to a reply
CommentUnderStoryCardForbiddenError
Comments disabled on this mix
update_comment
MutationAuthor / Org Admin

Edit a comment or reply's text or attachments. Only the original author can edit their own — Org Admin cannot edit others.

Parameters
commentIdrequired
Int
Comment to edit.
storyIdoptional
Int|String
Mix for precision.
contentoptional
String
New text.
imageId / videoIdoptional
Int
Replace attachment.
delete_comment
MutationAuthor / Org Admin

⚠️ Permanently delete a comment and all its replies. Cannot be undone. Org Admin can delete any comment.

Parameters
commentIdrequired
Int
Comment to delete.
confirmrequired
true
Must be explicitly true.
hide_comment
MutationEditor+

Toggle hide/unhide a comment. Hidden comments are excluded from default reader view. Non-destructive — comment still exists and can be unhidden.

Parameters
commentIdrequired
Int
Comment to hide or unhide.
storyIdoptional
Int|String
Mix for precision.
highlight_comment
MutationEditor+

Toggle featured/highlighted state on a comment. Highlighted comments are shown prominently. One per card at a time.

Parameters
commentIdrequired
Int
Comment to feature or unfeature.
storyIdoptional
Int|String
Mix for precision.
react_to_comment
MutationReader+

Like or unlike a comment or reply. Always reacts with like. storyId auto-resolved from comment — just provide commentId. MCP prevents accidental unlike.

Parameters
commentIdrequired
Int
Comment to react to.
storyIdoptional
Int|String
Mix — optional, auto-resolved from cardId.
reactionrequired
String
Reaction name (e.g. like). Org-configured.
removeoptional
Boolean
Set true to explicitly remove the reaction.
Behaviour
Like (not liked)
Adds reaction
Like (already liked)
Returns alreadyLiked — does NOT remove
remove:true (liked)
Removes reaction
remove:true (not liked)
Returns alreadyUnliked
MCP call
{ "name": "get_comments",
  "arguments": {
    "cardId": 22138518,
    "storyId": 61040,
    "fetchAll": true
} }
Required headers
x-tchop-webapp-organisation: <subdomain>
x-tchop-auth-token: <token>
x-tchop-api-client-id: <client-id>
Query
query {
  storyCardComments(
    storyId: 61040, itemId: 22138518,
    page: 1, size: 25,
    orderDirection: DESC
  ) {
    payload {
      items {
        id content parentId repliesCount
        author { id screenName }
        createdAt isHidden isHighlighted
        myReaction reactions { name count }
        attachments { imageId videoId }
        permissions { allowToDelete allowToEditContent
          allowToHide allowToHighlight }
      }
      pageInfo { totalItems hasNextPage }
    }
  }
}
API Reference

Tags

list_tags
QueryReader

List organisation tags. Filter by type (EXTERNAL/INTERNAL/EXTERNAL_STATUS) or search by name. Reader and Editor Limited cannot access INTERNAL tags — MCP enforces this.

Parameters
typeoptional
Enum
EXTERNAL · INTERNAL · EXTERNAL_STATUS. Omit for all accessible types.
queryoptional
String
Search by name — partial match.
page / pageSizeoptional
Int
Default: 1 / 100. Max pageSize: 200.
create_tag
MutationOrg Admin+

Create a new organisation tag. Tags are created at org level — only Org Admin, Org Owner, or Staff can create.

Parameters
namerequired
String
Tag name (unique within org).
typerequired
Enum
EXTERNAL · INTERNAL · EXTERNAL_STATUS
update_tag
MutationOrg Admin+

Update a tag's name or type. tagId accepts numeric ID or exact tag name.

Parameters
tagIdrequired
Int|String
Tag — numeric ID or exact name.
nameoptional
String
New name.
typeoptional
Enum
New type.
delete_tag
MutationOrg Admin+

⚠️ Permanently delete a tag. Removes it from all cards it was assigned to. tagId accepts numeric ID or name.

Parameters
tagIdrequired
Int|String
Tag — numeric ID or exact name.
confirmrequired
true
Must be explicitly true.
get_cards_by_tag
QueryReader

Get all cards assigned to a specific tag in a channel. Reader/Editor Limited cannot query INTERNAL tags. Access: Reader and Editor Limited: status filter locked to PUBLISHED — unpublished/scheduled cards never returned.

Parameters
tagIdrequired
Int|String
Tag — numeric ID or name.
channelIdrequired
Int|String
Channel — ID, name, or subdomain.
cardTypeoptional
Enum
ARTICLE · AUDIO · EDITORIAL · EVENT · IMAGE · PDF · POLL · POST · QUOTE · THREAD · VIDEO
statusoptional
Enum
PUBLISHED · UNPUBLISHED · SCHEDULED · DRAFTED
from / tooptional
String
Date range ISO 8601.
dateRangeoptional
Enum
Shortcut: 24h · 7d · 30d · 3m · 6m · 12m. Overrides from.
authorIdoptional
Int|String
Filter by author — numeric userId, username, or email.
filterByVisibleTagIdoptional
Int
Only cards that also have this EXTERNAL tag.
filterByHiddenTagIdoptional
Int
Only cards with this INTERNAL tag. Editor+ only.
filterByStatusTagIdoptional
Int
Only cards with this STATUS tag.
MCP call
{ "name": "list_tags",
  "arguments": { "type": "EXTERNAL" } }
Search by name
{ "name": "list_tags",
  "arguments": { "query": "approved" } }
Required headers
x-tchop-webapp-organisation: <subdomain>
x-tchop-auth-token: <token>
x-tchop-api-client-id: <client-id>
Query
query {
  organisationTags(page: 1, size: 100, filters: {
    types: EXTERNAL
  }) {
    items { id name type }
    pageInfo { totalItems totalPages }
  }
}
API Reference

Bookmarks

bookmark_card
MutationReader

Bookmark a card for the current user. Bookmarked cards appear in the user's personal bookmark feed. Bookmarking an already-bookmarked card is a no-op.

Parameters
storyIdrequired
Int
Mix/story containing the card.
storyCardIdrequired
Int
Card to bookmark.
unbookmark_card
MutationReader

Remove a bookmark from a card. Removing a non-existent bookmark is a no-op.

Parameters
storyIdrequired
Int
Mix/story containing the card.
storyCardIdrequired
Int
Card to remove from bookmarks.
list_bookmarks
QueryReader

List the current user's bookmarked cards in a channel. Bookmarks are user-scoped and channel-scoped. Supports filtering and pagination. Access: Reader and Editor Limited: only PUBLISHED bookmarks returned — bookmarks of unpublished cards are silently excluded.

Parameters
channelIdrequired
Int
Channel to list bookmarks from. Bookmarks are scoped per channel.
queryoptional
String
Full-text search filter.
storyCardTypeoptional
Enum[]
Filter by type: ARTICLE · EDITORIAL · IMAGE · VIDEO · AUDIO · PDF · POST · QUOTE · POLL · THREAD · EVENT
from / tooptional
String
ISO 8601 date range filter on postedAt.
categoryIdoptional
Int[]
Filter by category IDs.
authorIdoptional
Int[]
Filter by author user IDs.
orderDirectionoptional
Enum
DESC (default) · ASC
page / sizeoptional
Int
Page is 1-indexed. Size max 15. Default: page=1, size=15.
Response
items
Array of StoryCard objects with content, author, reactions
pageInfo
{ page, perPage, totalItems, totalPages, hasNextPage }
API Reference

Notifications

send_push_notification
MutationChannel Admin+

Send a push notification to all channel users, all org users, or specific users by ID. User-targeted pushes never appear in the admin list.

Parameters
titlerequired
String
Notification headline. Min 4 chars.
messagerequired
String
Notification body text.
userIds
[Int]
Target specific user IDs. Never appears in admin list. With channelId = channel-scoped; without = org-scoped.
recipient
Enum
CHANNEL (default) or ORG. Ignored when userIds is set. CHANNEL requires channelId. ORG is org admin only.
channelId
Int
Required when recipient=CHANNEL or targeting users in a channel.
linkContent
Enum
NONE (default), URL (opens url on tap), CARD (deep-links to card, needs storyId+cardId).
url
String
Required when linkContent=URL.
storyId
Int
Required when linkContent=CARD.
cardId
Int
Required when linkContent=CARD. Mapped to itemId in GraphQL.
deliveryTime
String
ISO 8601 schedule time. Omit to send immediately.
list_push_notifications
QueryChannel Admin+

List push notifications, or get a single notification by ID. Individual user pushes never appear in the list.

Parameters
notificationId
Int
Get single notification by ID with full detail. Ignores list filters when provided.
channelId
Int
Filter by channel. Omit for all.
excludeOrganisationNotifications
Boolean
Exclude org-level pushes from list.
query
String
Search by title (partial match).
orderDirection
Enum
ASC or DESC (default).
page / pageSize
Int
Pagination. pageSize max 50, default 20.
update_push_notification
MutationChannel Admin+

Edit a scheduled push notification — title, message, url, card link, or delivery time. Only works on notifications that have not yet been sent. At least one update field is required.

Parameters
notificationIdrequired
Int
Push notification to update. Get IDs from list_push_notifications.
title
String
New title (min 4 chars).
message
String
New body message.
url
String
New link URL.
storyId / cardId
Int
Update card deeplink. cardId mapped to itemId in GraphQL.
deliveryTime
String
Reschedule to new ISO 8601 time.
channelId
Int
Channel ID context. Usually omit.
delete_push_notification
MutationChannel Admin+

⚠️ Delete a scheduled push notification. Only works on notifications that have not yet been sent — a sent notification cannot be recalled.

Parameters
notificationIdrequired
Int
Push notification to delete. Get IDs from list_push_notifications.
confirmrequired
true
Must be explicitly true.
send_in_app_message
MutationChannel Admin+

Send an in-app modal message to all channel users, all org users, or filtered by audience. Never appears in admin list. Cannot be updated after creation.

Parameters
titlerequired
String
Modal headline (4–120 chars).
messagerequired
String
Body text (4–300 chars).
recipient
Enum
CHANNEL (default, requires channelId) or ORG.
channelId
Int
Required when recipient=CHANNEL.
buttonText
String
CTA button label (2–20 chars). Default: "OK".
buttonAction
Enum
CANCEL (default), OPEN_URL, OPEN_IURL, SHARE.
buttonUrl
String
URL for OPEN_URL / OPEN_IURL / SHARE actions.
bgColor / textColor
Hex
Modal background and text color. Defaults: #FFFFFF / #1E1E1E.
buttonBgColor / buttonTextColor
Hex
Button colors. Defaults: #488ED8 / #FFFFFF.
imageId
Int
Image above title (optional). Use upload_image to get imageId.
expirationHours
Int
Hours until message expires (12–8760). Default: 72.
audienceType
Enum
login (real users, default) or demo.
userIds
[Int]
Filter to specific user IDs.
deliveryTime
String
ISO 8601 schedule time. Omit to show immediately.
list_in_app_messages
QueryChannel Admin+

List in-app messages, or get a single message by ID with full detail.

Parameters
inAppMessageId
Int
Get single message by ID with full detail. Ignores list filters when provided.
channelId
Int
Filter by channel. Omit for all.
excludeOrganisationMessages
Boolean
Exclude org-level messages from list.
query
String
Search by title or body (partial match).
orderDirection
Enum
ASC or DESC (default).
page / pageSize
Int
Pagination. pageSize max 50, default 20.
delete_in_app_message
MutationChannel Admin+

⚠️ Delete an in-app message. Removes it immediately. In-app messages cannot be updated after creation — delete and recreate if changes are needed.

Parameters
inAppMessageIdrequired
Int
In-app message to delete. Get IDs from list_in_app_messages.
confirmrequired
true
Must be explicitly true.
API Reference

Analytics

Org Admin / Org Owner / Staff token required. All analytics tools require at minimum an Organisation Admin auth token.

Platform deduplication. User counts are deduped per user. Summing platform splits (WEB + IOS + ANDROID) double-counts multi-platform users — use blended totals for accurate unique counts.

get_analytic_chart
QueryOrg Admin

Fetch one of 10 time-series charts. Returns labelled datasets with data points per interval. Use interval to group by day-of-week or hour-of-day.

Parameters
chartrequired
Enum
userActive · userActivity · userAllActivity · userDemoActivity · userEngagement · userLoginLogout · userReferralActivity · userSession · contentActivity · installUninstallApp
from / torequired
String
Date range YYYY-MM-DD.
intervaloptional
Enum
AUTO · DAY_OF_WEEK · HOUR_OF_DAY. Default: AUTO.
channelIdoptional
Int[]
Restrict to channels. Omit for org-wide.
platformoptional
Enum[]
WEB · IOS · ANDROID. Omit for blended.
categoryIdoptional
Int[]
Category filter — contentActivity only.
get_channel_list_analytics
QueryOrg Admin

Rank all channels by a chosen metric for the date range. Returns channels sorted by value descending.

Parameters
metricrequired
Enum
views · posts · comments · reactions · shares · commentReactions
from / torequired
String
Date range YYYY-MM-DD.
channelIdoptional
Int[]
Restrict to channels. Omit for all.
platformoptional
Enum[]
WEB · IOS · ANDROID.
page / pageSizeoptional
Int
Default: 1 / 10. Max: 100.
get_content_list
QueryOrg Admin

Top-performing cards ranked by any of 26 metrics. Returns card IDs, types, and full metrics per card.

Parameters
from / torequired
String
Date range YYYY-MM-DD.
orderByoptional
Enum
Default: VIEWS. Options: VIEWS · COMMENTS · REACTIONS · ENGAGEMENT · SHARES · NOTIFICATIONS · PLAYS · PLAY_DURATION · PLAY_DURATION_AVG · POST_VIDEO_PLAYS · POST_VIDEO_PLAY_DURATION · POST_VIDEO_PLAY_DURATION_AVG · POST_AUDIO_PLAYS · POST_AUDIO_PLAY_DURATION · POST_AUDIO_PLAY_DURATION_AVG · POST_LINK_CLICKS · POSTED_TIME · REACTIONS_POSITIVE · REACTIONS_NEGATIVE · REACTION_LIKE · REACTION_LOVE · REACTION_WOW · REACTION_HAHA · REACTION_SAD · REACTION_ANGRY · COMMENT_REACTIONS
orderDirectionoptional
Enum
ASC · DESC. Default: DESC.
channelIdoptional
Int[]
Filter to channels.
categoryIdoptional
Int[]
Filter by category.
storyCardTypeoptional
Enum[]
ARTICLE · IMAGE · VIDEO · EDITORIAL · POST · THREAD · QUOTE · AUDIO · POLL · PDF
platformoptional
Enum[]
WEB · IOS · ANDROID.
queryoptional
String
Search by card title.
page / pageSizeoptional
Int
Default: 1 / 10. Max: 100.
get_content_analytics
QueryOrg Admin

Content activity over time, interaction rates, tag performance, or top comments. Chart selector controls which data is returned.

Parameters
chartrequired
Enum
contentActivity — time-series of posts/views/reactions/comments/shares · contentInteraction — published vs interacted breakdown · contentTagList — tags ranked by views/engagement/published · commentList — top comments by reactions
from / torequired
String
Date range YYYY-MM-DD.
channelIdoptional
Int[]
Restrict to channels.
categoryIdoptional
Int[]
Category filter. Not applicable to commentList.
storyCardTypeoptional
Enum[]
Card type filter — contentTagList only.
platformoptional
Enum[]
WEB · IOS · ANDROID.
intervaloptional
Enum
AUTO · DAY_OF_WEEK · HOUR_OF_DAYcontentActivity only.
orderByoptional
Enum
VIEWS · ENGAGEMENT · PUBLISHEDcontentTagList only. Default: VIEWS.
orderDirectionoptional
Enum
ASC · DESCcontentTagList + commentList. Default: DESC.
page / pageSizeoptional
Int
Default: 1 / 10. Max: 100.
get_user_analytics
QueryOrg Admin

User conversion funnels, interaction breakdowns, referral rankings, and content breakdown by user role.

Parameters
chartrequired
Enum
userFunnel / userFunnelV2 / userFunnelV3 — conversion funnel steps · userDemoFunnel — anonymous user funnel · userInteraction — interaction breakdown · userReferralList — top referrers · userRoleContentEngagement / userRoleContentEngagementAvg — engagement by role · userRoleContentPublished — published by role · userRoleContentView / userRoleContentViewAvg — views by role
from / torequired
String
Date range YYYY-MM-DD.
channelIdoptional
Int[]
Restrict to channels.
platformoptional
Enum[]
WEB · IOS · ANDROID.
orderDirectionoptional
Enum
ASC · DESC. Not applicable to funnel charts or userInteraction. Default: DESC.
page / pageSizeoptional
Int
Default: 1 / 10. Max: 100.
get_messaging_analytics
QueryOrg Admin

Push notification and in-app message analytics. Aggregate totals or per-message breakdown ranked by sent, open rate, or delivery time.

Parameters
chartrequired
Enum
pushTotal — aggregate push totals · pushList — per-notification breakdown · inAppTotal — aggregate in-app totals · inAppList — per-message breakdown
from / torequired
String
Date range YYYY-MM-DD.
channelIdoptional
Int[]
Restrict to channels.
platformoptional
Enum[]
WEB · IOS · ANDROID.
queryoptional
String
Search by title — list charts only.
orderByoptional
Enum
SENT · OPEN · DELIVERY_TIME — list charts only. Default: SENT.
orderDirectionoptional
Enum
ASC · DESC. Default: DESC.
page / pageSizeoptional
Int
Default: 1 / 10. Max: 100.
get_story_analytics
QueryOrg Admin

Story performance breakdown or aggregate story totals. Duration values are in seconds.

Parameters
chartrequired
Enum
storyList — per-story breakdown · storyTotal — aggregate totals
from / torequired
String
Date range YYYY-MM-DD.
channelIdoptional
Int[]
Restrict to channels.
platformoptional
Enum[]
WEB · IOS · ANDROID.
orderByoptional
Enum
VIEWS · DURATION · DURATION_AVGstoryList only. Default: VIEWS.
orderDirectionoptional
Enum
ASC · DESC. Default: DESC.
page / pageSizeoptional
Int
Default: 1 / 10. Max: 100.
get_user_activity
QueryOrg Admin

Get content activity stats for a specific user in a channel: published, draft, scheduled, and unpublished card counts.

Parameters
channelIdrequired
Int
Channel ID.
userIdrequired
Int
User ID.

Returns { published, drafts, scheduled, unpublished } counts.

get_analytics_overview
QueryOrg Admin

One-call analytics overview: active users, sessions, engagement, content activity, and user funnel for a date range. Returns six charts in a single response — ideal for dashboard summaries.

Parameters
from / torequired
String
Date range YYYY-MM-DD.
channelIdoptional
Int[]
Restrict to channels. Omit for org-wide.
platformoptional
Enum[]
WEB · IOS · ANDROID. Omit for blended.

Returns userActive, userActivity, userSession, userEngagement, contentActivity, and userFunnelV2 in one response.

get_user_cards
QueryOrg Admin

Get a paginated list of cards created by a specific user. Returns headline, type, status, posted date, and storyId for deeplinks. Filter by status, card type, or date range.

Parameters
userIdrequired
Int
User ID to retrieve cards for.
from / tooptional
String
Filter by posted date YYYY-MM-DD.
statusoptional
Enum
PUBLISHED · UNPUBLISHED · SCHEDULED · DRAFT
storyCardTypeoptional
Enum
ARTICLE · IMAGE · VIDEO · EDITORIAL · POST · THREAD · QUOTE · AUDIO · POLL · PDF
orderDirectionoptional
Enum
ASC · DESC. Default: DESC.
page / pageSizeoptional
Int
Default: 1 / 15. Max pageSize: 50.
get_user_overview
QueryOrg Admin

User activity overview in one call: registered active users, anonymous/demo users, and conversion funnel. Equivalent to combining userActive, userDemoActivity, and userFunnelV2.

Parameters
from / torequired
String
Date range YYYY-MM-DD.
channelIdoptional
Int[]
Restrict to channels. Omit for org-wide.
platformoptional
Enum[]
WEB · IOS · ANDROID. Omit for blended.

Returns userActive (registered), userDemoActivity (anonymous), and userFunnelV2 (conversion steps).

get_engagement_by_platform
QueryOrg Admin

Engagement time-series broken down by platform (WEB, IOS, ANDROID) with blended total — all in one call. Runs four parallel queries and bundles results.

Parameters
from / torequired
String
Date range YYYY-MM-DD.
channelIdoptional
Int[]
Restrict to channels. Omit for org-wide.

Returns { blended, WEB, IOS, ANDROID } — each is a userEngagement time-series.

get_card_type_performance
QueryOrg Admin

Top N cards per card type in one call — runs parallel queries for each type and bundles results side-by-side. Ideal for comparing performance across content formats.

Parameters
from / torequired
String
Date range YYYY-MM-DD.
orderByoptional
Enum
VIEWS · COMMENTS · REACTIONS · ENGAGEMENT · SHARES · POST_VIDEO_PLAYS · POST_AUDIO_PLAYS · POST_LINK_CLICKS · POSTED_TIME. Default: VIEWS.
topNoptional
Int
Cards per type (max 20). Default: 10.
typesoptional
Enum[]
Subset of types to query. Omit for all 10: ARTICLE · IMAGE · VIDEO · EDITORIAL · POST · THREAD · QUOTE · AUDIO · POLL · PDF
channelIdoptional
Int[]
Restrict to channels.
platformoptional
Enum[]
WEB · IOS · ANDROID.

Response is keyed by type: { byType: { ARTICLE: { items, totalItems }, VIDEO: { ... }, ... } }

MCP call
{ "name": "get_analytics_overview",
  "arguments": {
    "from": "2025-06-01",
    "to": "2025-07-01"
} }
Response
{
  "userActive": { "labels": [...], "interval": "DAY", "datasets": [{ "label": "DAU", "data": [...] }] },
  "userActivity": { ... },
  "userSession": { ... },
  "userEngagement": { ... },
  "contentActivity": { ... },
  "userFunnelV2": { "items": [{ "label": "Registered", "value": 120 }, ...] }
}
Required headers
x-tchop-webapp-organisation: <your-subdomain>
x-tchop-auth-token: <your-token>
x-tchop-api-client-id: <your-client-id>
Query
query GetAnalyticsOverview(
  $filter: GetAnalyticFilterArgs!
  $contentFilter: GetAnalyticContentFilterArgs!
) {
  analyticCharts {
    userActive(filter: $filter) { labels interval datasets { label data } }
    userActivity(filter: $filter) { labels interval datasets { label data } }
    userSession(filter: $filter) { labels interval datasets { label data } }
    userEngagement(filter: $filter) { labels interval datasets { label data } }
    contentActivity(filter: $contentFilter) { labels interval datasets { label data } }
    userFunnelV2(filter: $filter, page: 1, size: 10) { items { label value } }
  }
}
Variables
{
  "filter": { "from": "2025-06-01", "to": "2025-07-01" },
  "contentFilter": { "from": "2025-06-01", "to": "2025-07-01" }
}
API Reference

Chat

Coming soon

Chat API operations are being documented.

API Reference

Events

Beta. Events are not yet enabled for all organisations. Tools return errors on orgs where Events is gated off.

get_events
QueryReader

List events in a channel, or get full detail for a single event by ID or exact name. Omit eventId/eventName to list with filters; pass one for full detail including description, location, image, tags, and permissions.

Parameters
channelIdrequired
Int
Channel to list events in.
eventIdoptional
Int
Omit to list; pass to fetch full detail for one event.
eventNameoptional
String
Fetch one event by exact name in the channel (read-only; errors if ambiguous). Destructive tools require eventId.
from / tooptional
String
ISO 8601 date range filter (list mode only).
statusesoptional
Enum[]
PUBLISHED · UNPUBLISHED · CANCELLED · SCHEDULED · PAST · UPCOMING
tagIdsoptional
Int[]
Filter by event tag IDs. Use list_tags with type: EVENT.
hostIdsoptional
Int[]
Filter by host user IDs.
participantStatusesoptional
Enum[]
CONFIRMED · HOST · WAITLISTED
orderByoptional
Enum
Default: START_AT. Options: CREATED_AT · END_AT · PUBLISHED_AT · SCHEDULED_AT · START_AT · UPDATED_AT
orderDirectionoptional
Enum
ASC · DESC. Default: ASC
page / pageSizeoptional
Int
Default: 1 / 15. Max pageSize: 15.
create_event
MutationEditor

Create an event in a channel. Required: name, description, startAt, endAt. Defaults: status PUBLISHED, reminder 24h before start, showParticipants false, no capacity limit. Pass address and/or virtualUrl for location. tagIds = event tag IDs (use list_tags with type: EVENT).

Parameters
channelIdrequired
Int
Channel to create the event in.
namerequired
String
Event title.
descriptionrequired
String
Event description. Markdown supported.
startAtrequired
String
ISO 8601 start date/time.
endAtrequired
String
ISO 8601 end date/time. Required — API enforces this even though schema marks it optional.
statusoptional
Enum
PUBLISHED · UNPUBLISHED · CANCELLED. Default: PUBLISHED.
participantsCapacityoptional
Int
Max participants. 0 or omit = unlimited.
allowWaitlistoptional
Boolean
Enable waitlist after capacity full.
showParticipantsoptional
Boolean
Show participant list to all readers. Default: false.
reminderHoursBeforeStartoptional
Int
Reminder email hours before start. Default: 24. Set 0 to disable.
addressoptional
String
Physical address for in-person events.
virtualUrloptional
String
Virtual meeting URL. Shown to CONFIRMED participants only.
imageUrloptional
String
Cover image URL — uploaded automatically. No separate upload step needed.
hostIdsoptional
Int[]
User IDs of event hosts.
channelIdsoptional
Int[]
Additional channels to cross-post into.
tagIdsoptional
Int[]
Event tag IDs. Use list_tags with type: EVENT.
update_event
MutationEditor

Update an existing event. Pass only the fields to change — others are left as-is. tagIds and hostIds replace the full existing set when provided. imageUrl is uploaded automatically.

Parameters
eventIdrequired
Int
ID of the event to update.
channelIdrequired
Int
Channel the event belongs to.
nameoptional
String
Event title.
descriptionoptional
String
Event description. Markdown: bold **, italic *, underline ++, hyperlink [text](url).
startAt / endAtoptional
String
ISO 8601 start/end date-time.
statusoptional
Enum
PUBLISHED · UNPUBLISHED · CANCELLED
participantsCapacityoptional
Int
Max participants. 0 = unlimited.
allowWaitlistoptional
Boolean
Enable waitlist after capacity full.
showParticipantsoptional
Boolean
Show participant list to all readers.
reminderHoursBeforeStartoptional
Int
Reminder email hours before start. 0 = disable.
addressoptional
String
Physical address.
virtualUrloptional
String
Virtual meeting URL. Shown to CONFIRMED participants only.
imageUrloptional
String
Cover image URL — uploaded automatically.
hostIdsoptional
Int[]
User IDs of event hosts. Replaces existing hosts entirely.
channelIdsoptional
Int[]
Additional channels to cross-post into.
tagIdsoptional
Int[]
Event tag IDs. Replaces existing tags entirely. Use list_tags with type: EVENT.
delete_event
MutationEditor

⚠️ Permanently delete an event. This cannot be undone.

Parameters
channelIdrequired
Int
Channel the event belongs to.
eventIdrequired
Int
Event to delete.
confirmrequired
true
Must be explicitly true.
duplicate_event
MutationEditor

Copy an existing event into a target channel. Useful for recurring events or templates. The copy is independent — changes to the source do not affect the copy.

Parameters
channelIdrequired
Int
Target channel for the new event.
sourceChannelIdrequired
Int
Channel the source event lives in.
sourceEventIdrequired
Int
ID of the event to copy.
join_event
MutationReader

Join an event as the authenticated user (RSVP). Returns the resulting participant status: CONFIRMED (capacity available), WAITLISTED (event full, waitlist enabled), or HOST (user is a host).

Parameters
channelIdrequired
Int
Channel the event belongs to.
eventIdrequired
Int
Event to join.
cancel_event_participation
MutationReader

Cancel the authenticated user's participation in an event. Removes CONFIRMED or WAITLISTED status.

Parameters
channelIdrequired
Int
Channel the event belongs to.
eventIdrequired
Int
Event to cancel participation for.
cancel_event
MutationEditor

Cancel an event — sets status to CANCELLED. The event stays visible to members but is marked as cancelled. Does not delete it. Use delete_event to remove entirely.

Parameters
channelIdrequired
Int
Channel the event belongs to.
eventIdrequired
Int
Event to cancel.
Returns
id
Int
Event ID
name
String
Event name
status
String
CANCELLED
get_event_participants
QueryEditor*

List participants for an event, or pass exportCsv: true to trigger an async CSV export delivered via email. Visibility: hosts always; all members if showParticipants: true; otherwise editors/admins/event author only.

Parameters
channelIdrequired
Int
Channel the event belongs to.
eventIdrequired
Int
Event ID.
exportCsvoptional
Boolean
true → async CSV export via email. Omit to list inline.
participantStatusesoptional
Enum[]
CONFIRMED · HOST · WAITLISTED
queryoptional
String
Search by name or email.
page / pageSizeoptional
Int
Default: 1 / 15. Max: 15.
List events
{ "name": "get_events",
  "arguments": { "channelId": 2385 } }
Filter upcoming
{ "name": "get_events",
  "arguments": {
    "channelId": 2385,
    "statuses": ["UPCOMING"]
} }
Get single event
{ "name": "get_events",
  "arguments": {
    "channelId": 2385,
    "eventId": 42
} }
Required headers
x-tchop-webapp-organisation: <subdomain>
x-tchop-auth-token: <token>
x-tchop-api-client-id: <client-id>
Query
query {
  events(channelId: 2385, orderBy: START_AT,
    orderDirection: ASC, page: 1, size: 20
  ) {
    items { id name status startAt endAt participantStatus }
    pageInfo { totalItems hasNextPage }
  }
}
API Reference

Users

List, search, and inspect users; manage invitations, roles, and removals; and work with the calling user's own profile and profile feed. All seven tools accept the shared targeting parameters server, domain, and organisation (see each table's last row).

Access model (enforced at the MCP layer). Org owner / org admin / org staff → all users in the organisation. Channel admin / channel editor → only users of channels they manage. Reader / editor limited → own profile only. Admin tools (invite_user, update_user_role, remove_user) are restricted to org owners, admins, and staff.

list_users
QueryReader*

List, search, and inspect users of the organisation or a channel — one consolidated tool. Each row is the full user projection: name, email, avatar, role, status, gender, company fields, location, bio, phone, socials, user tags, onboarding method, registered/last-active times, channels with per-channel role, engagement stats, and referral count.

Role-gated results. Org owner/admin/staff see the whole org (organisationUsers). Channel admins/editors see only their managed channels (channelUsers, merged and de-duplicated). Readers and editor-limited users get their own profile only — any other request is denied at the MCP layer, even where the raw API would answer.

Four modes: default list (filterable), self: true (own full profile — the only mode that returns your referralCode; the API cannot expose other users' referral codes), userId (one specific user), and showAvailableFilters: true (discover the org's configured position / department / subscription values plus all static filter enums before filtering).

Parameters
selfoptional
Boolean
true → the calling user's own full profile, including their referral code.
userIdoptional
Int
Look up one specific user by ID. Subject to the role-based access model.
showAvailableFiltersoptional
Boolean
true → return configured filter values (positions, departments, subscriptions) plus static enums. No user data — safe for all roles.
channelIdsoptional
Int[] | String[]
Restrict to users of these channels (ID, name, or subdomain). Omit for the whole organisation.
queryoptional
String
Search users by name or email.
emailoptional
String
Exact email lookup — returns the user whose email matches exactly. Matched at the MCP layer (server-side search narrows first).
rolesoptional
Enum[]
ORGANISATION_OWNER · ORGANISATION_ADMIN · ORGANISATION_TEAM_MEMBER · CHANNEL_ADMIN · CHANNEL_EDITOR · CHANNEL_EDITOR_LIMITED · CHANNEL_READER. Filter by user role. Narrowed server-side via the numeric rolesId filter (mapping live-verified), then matched on the user's highest role at the MCP layer — the app's highest-role semantics.
queryLocationoptional
String
Search users by profile location text.
usersIdoptional
Int[]
Filter to specific user IDs.
gendersoptional
Enum[]
MALE · FEMALE · COUPLES · DIVERSE · NON_BINARY · TRANS
statusesoptional
Enum[]
ACTIVE · ENGAGED · INACTIVE. ENGAGED = active AND interacting.
onboardingMethodsoptional
Enum[]
INVITATION · DIRECT_LINK · COMPANY_ID · AUTO_GENERATED_PASSWORD
userTagIdsoptional
Int[]
Filter by organisation user-tag IDs. Use list_tags to discover IDs.
departmentsoptional
String[]
Filter by department values. Use showAvailableFilters for valid values.
positionsoptional
String[]
Filter by position values. Use showAvailableFilters for valid values.
subscriptionsoptional
String[]
Filter by subscription values. Use showAvailableFilters for valid values.
excludeBannedoptional
Boolean
true → exclude banned users.
orderByoptional
Enum
SCREEN_NAME · EMAIL · REGISTERED (= invited/added date) · AUTHORIZED_TIME (= last active). Default: SCREEN_NAME. Invited date and last active are sortable only — the API has no date-range filter for them.
orderDirectionoptional
Enum
ASC · DESC. Default: ASC.
page / pageSizeoptional
Int
Default: 1 / 20. Max pageSize: 30.
server / domain / organisationoptional
Enum / String
Shared targeting: server staging/io/it; domain from the app URL (overrides server); organisation subdomain (staff token). Omit for defaults.
list_user_profile_feed
QueryReader

List the cards on a user's profile feed — the visual feed attached to their profile. Profile feeds hold images and videos only. Omit userId to list your own feed (includes unpublished and scheduled cards). Other users' feeds return published cards only, matching what the app shows.

Parameters
userIdoptional
Int
User whose profile feed to list. Omit for your own feed.
statusoptional
Enum[]
PUBLISHED · UNPUBLISHED · SCHEDULED. Only your own feed shows non-published cards; other users' feeds are forced to PUBLISHED.
storyCardTypeoptional
Enum[]
IMAGE · VIDEO
from / tooptional
String
ISO 8601 posted-date range filter.
orderDirectionoptional
Enum
ASC · DESC by posted time. Default: DESC (newest first).
page / pageSizeoptional
Int
Default: 1 / 15. Max pageSize: 15.
server / domain / organisationoptional
Enum / String
Shared targeting params — see list_users.
post_card_in_user_profile_story
MutationReader

Self-only. Post an image or video card to your own profile feed. Profile feeds support images and videos only. Provide imageUrls or videoUrl, not both (one card per call). Published cards are visible to anyone viewing your profile; unpublished cards only to you. Media is ingested automatically from the URL — no separate upload step.

Parameters
imageUrlsoptional
String[]
Image URLs. Each becomes a gallery item on one IMAGE card.
videoUrloptional
String
Video URL for one VIDEO card. Mutually exclusive with imageUrls.
headlineoptional
String
Card headline.
textoptional
String
Card text / caption.
captionoptional
String
Gallery item caption (media title).
statusoptional
Enum
PUBLISHED · UNPUBLISHED. Default: PUBLISHED.
postingTimeoptional
String
ISO 8601 datetime to schedule instead of publishing now.
server / domain / organisationoptional
Enum / String
Shared targeting params — see list_users.
invite_user
MutationOrg admin

Invite users to the organisation (bulk supported — pass many users entries), add existing users to channels, and manage pending invitations. Restricted to organisation owners, admins, and staff. Seven actions: invite (default — invitation email), invite_with_generated_password (email invite with auto-generated password instead of self-signup), invite_by_company_id (invite via company ID instead of email — each user needs companyId; notifyExistingUsers not supported on this input), add_existing_to_channel (grant EXISTING users channel access — userIds + role + channelIds required; notifyExistingUsers defaults true), resend_invitation, remind_invitation, and regenerate_password (the last three take userIds). Per-user result status: OK = done, SKIP = already exists/invited, ACCESS = no permission for that user.

Parameters
actionoptional
Enum
invite · invite_with_generated_password · invite_by_company_id · add_existing_to_channel · resend_invitation · remind_invitation · regenerate_password. Default: invite.
usersoptional
Object[]
Users to invite (bulk supported) — { email?, companyId?, screenName, department?, position? }. screenName is always required; email invites need email, invite_by_company_id needs companyId. Required for the three invite actions.
roleoptional
Enum
CHANNEL_ADMIN · CHANNEL_EDITOR · CHANNEL_EDITOR_LIMITED · CHANNEL_READER. Required for invite actions and add_existing_to_channel.
channelIdsoptional
Int[] | String[]
Channels to grant access to (ID, name, or subdomain). Omit to invite at organisation level. Required for add_existing_to_channel.
notifyExistingUsersoptional
Boolean
Notify users who already exist about newly granted channel access. Not sent for invite_by_company_id (the API input has no such field); defaults to true for add_existing_to_channel.
userIdsoptional
Int[]
Target user IDs. Required for add_existing_to_channel / resend_invitation / remind_invitation / regenerate_password.
server / domain / organisationoptional
Enum / String
Shared targeting params — see list_users.
update_user_role
MutationOrg admin

Change users' role — inside channels (admin / editor / editor limited / reader via organisationUserUpdateChannelRole) or at organisation level (promote to org admin / owner / team member via organisationAdminUpdate, or REMOVE_ORG_ADMIN to demote via organisationAdminRemove). Restricted to organisation owners, admins, and staff. Per-user result status: OK = changed, SKIP = unchanged, ACCESS = no permission.

Last-admin protection. Demoting the only CHANNEL_ADMIN of a channel is blocked with an error — promote another user to CHANNEL_ADMIN there first, then retry. Enforced at the MCP layer (the backend does not consistently enforce it).

Parameters
userIdsrequired
Int[]
Users whose role to change. Use list_users to find IDs.
rolerequired
Enum
Channel roles: CHANNEL_ADMIN · CHANNEL_EDITOR · CHANNEL_EDITOR_LIMITED · CHANNEL_READER. Org roles: ORGANISATION_ADMIN · ORGANISATION_OWNER · ORGANISATION_TEAM_MEMBER. REMOVE_ORG_ADMIN demotes from org admin back to regular member.
channelIdsoptional
Int[] | String[]
For channel roles: channels to apply the role in (ID, name, or subdomain). Omit to apply across the users' channels. Ignored for org roles.
server / domain / organisationoptional
Enum / String
Shared targeting params — see list_users.
remove_user
MutationOrg admin

⚠️ Remove users from the organisation or from specific channels, or ban / unban them. Restricted to organisation owners, admins, and staff, and requires confirm: true. Four actions: remove_from_organisation (revokes all access — this is also how you delete a user: the API has no separate account deletion), remove_from_channel (specific channels only — channelIds required), ban (user stays listed but loses access; reversible), unban. The tool refuses to act on your own account. Per-user result status: OK · SKIP · ACCESS.

Last-admin protection. Removing or banning the only CHANNEL_ADMIN of a channel (remove_from_organisation, remove_from_channel, ban — not unban) is blocked with an error until another user is made admin of that channel via update_user_role.

Parameters
actionrequired
Enum
remove_from_organisation · remove_from_channel · ban · unban
userIdsrequired
Int[]
User IDs to act on. Must not include your own ID.
channelIdsoptional
Int[] | String[]
Channels to remove the users from. Required for remove_from_channel.
confirmrequired
true
Must be explicitly true. Confirms this access-revoking action.
server / domain / organisationoptional
Enum / String
Shared targeting params — see list_users.
update_user_profile
MutationReader

Self-only. Update your own user profile: name, bio, gender, phone, company fields, location, URL, social links, user tags, and avatar. Only touches the fields you pass. Email cannot be changed (it is the account's unique identifier). Note: SSO-managed organisations lock profile editing — the API rejects edits in that case.

Parameters
screenNameoptional
String
New display name.
biooptional
String
Short bio / tagline.
genderoptional
Enum
MALE · FEMALE · COUPLES · DIVERSE · NON_BINARY · TRANS. Icon shown next to username when set.
phoneoptional
String
Phone number.
position / department / subscriptionoptional
String
Company profile fields.
locationoptional
String
Location text.
urloptional
String
Personal website / portfolio URL shown on the profile.
avatarUrloptional
String
Image URL to set as the new avatar — uploaded automatically.
userTagIdsoptional
Int[]
Organisation user-tag IDs to set (replaces the current set).
linksoptional
Object
Social links: facebook, instagram, linkedin, youtube, tiktok, twitter, snapchat.
server / domain / organisationoptional
Enum / String
Shared targeting params — see list_users.
List org users
{ "name": "list_users",
  "arguments": {} }
Filtered
{ "name": "list_users",
  "arguments": {
    "statuses": ["INACTIVE"],
    "departments": ["Marketing"],
    "excludeBanned": true,
    "orderBy": "AUTHORIZED_TIME"
} }
Exact email / role filter (MCP-layer)
{ "name": "list_users",
  "arguments": {
    "email": "jane@example.com"
} }
{ "name": "list_users",
  "arguments": {
    "roles": ["CHANNEL_ADMIN"]
} }
Own profile + referral code
{ "name": "list_users",
  "arguments": { "self": true } }
Discover filter values
{ "name": "list_users",
  "arguments": { "showAvailableFilters": true } }
Required headers
x-tchop-webapp-organisation: <subdomain>
x-tchop-auth-token: <token>
x-tchop-api-client-id: <client-id>
Query — org scope
query {
  organisationUsers(
    filters: { statuses: [INACTIVE], excludeBanned: true },
    orderBy: SCREEN_NAME, orderDirection: ASC,
    page: 1, size: 20
  ) {
    items { id screenName email roleId status
      department position registeredAt authorizedAt }
    pageInfo { totalItems hasNextPage }
  }
}
Query — channel scope
query {
  channelUsers(channelId: 2385, page: 1, size: 20) {
    items { id screenName email roleId status }
    pageInfo { totalItems hasNextPage }
  }
}
Query — self (only source of the referral code)
query {
  me { id screenName email roleId }
  meReferralCode
}