MCP Server API Reference
Connect AI agents to your inseeq organization using the Model Context Protocol (MCP).
Authentication
All requests require a valid API key passed as a Bearer token in the Authorization header.
Generating an API Key
- Log in to the inseeq app at https://app.staging.inseeq.com
- Navigate to Settings in the sidebar
- Click the API Keys tab
- Click Generate API Key
- Copy the key — it will be visible on this page whenever you need it
Each organization can have one API key. You can regenerate or delete it at any time from the same page.
Using Your API Key
Pass the key as a Bearer token in the Authorization header of every request:
Authorization: Bearer inseeq_your_api_key_here
Your API key is scoped to your organization. All operations are automatically filtered to your org's data.
Endpoint
The MCP server accepts JSON-RPC requests over HTTP POST. It follows the Model Context Protocol specification with Streamable HTTP transport.
POST https://api.staging.inseeq.com/mcp Content-Type: application/json Accept: application/json, text/event-stream Authorization: Bearer inseeq_your_api_key_here
The server operates in stateless mode — each request is independent. No session management is required.
Quick Test
Verify your API key works by sending an initialize request:
curl -X POST https://api.staging.inseeq.com/mcp \
-H "Authorization: Bearer INSEEQ_API_KEY" \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-d '{"jsonrpc":"2.0","method":"initialize","params":{"protocolVersion":"2025-03-26","capabilities":{},"clientInfo":{"name":"test","version":"1.0"}},"id":1}'event: message
data: {"result":{"protocolVersion":"2025-03-26","capabilities":{"tools":{"listChanged":true}},"serverInfo":{"name":"inseeq","version":"0.1.0"}},"jsonrpc":"2.0","id":1}Permissions
Your API key grants access to the following capabilities:
| Capability | Access |
|---|---|
| Read organization & brand profile | Allowed |
| List, read, create, update articles | Allowed |
| Change article status | Allowed |
| List, add, resolve comments | Allowed |
| Read, write, sync context (knowledge base) | Allowed |
| Read performance metrics & goals | Allowed |
| Upload images | Allowed |
| Publish to CMS (WordPress, Webflow, etc.) | Allowed |
| Send email notifications | Not available |
Rate Limits
API keys are rate-limited to 60 requests per minute. If you exceed this limit, the server returns a 429 error. Implement exponential backoff: wait 1s, then 2s, then 4s before retrying. Most MCP clients handle this automatically.
Error Responses
All errors follow the standard MCP JSON-RPC error format. The message field contains a human-readable description of what went wrong.
{
"error": {
"code": -32602,
"message": "Article not found"
}
}| HTTP Status | Meaning | Common Causes |
|---|---|---|
| 401 | Unauthorized | Missing or invalid API key |
| 403 | Forbidden | Writing to a frozen or human_only context entry; calling an internal-only tool |
| 404 | Not Found | Article, comment, or context entry does not exist |
| 422 | Validation Error | Invalid parameter type, missing required field, or invalid status transition |
| 429 | Rate Limited | Exceeded 60 requests/minute — back off and retry |
| 500 | Server Error | Internal error — retry once, then contact support |
Article Status Lifecycle
Articles and LinkedIn posts progress through a linear workflow. Use inseeq_update_article_status to transition between states. Calendar events do not use this flow — they have their own two-state toggle (scheduled ↔ done) via inseeq_update_event_status.
draft → ready_to_write → writing → generating → in_review → approved → published
You can move articles forward or backward through this workflow. Some transitions trigger automatic side effects:
| Transition to | Side effect |
|---|---|
| writing | Sets writing_started_at timestamp |
| in_review | Sends review request emails to org reviewers; sets review_requested_at |
| approved | Sets approved_at and approved_by |
| published | Sets published_at and published_by |
To publish to a CMS, first transition to approved, then call inseeq_publish_article.
Article URLs
Every article response includes an app_url field — the direct link to view or edit the article in inseeq. Use this when sharing links with users.
https://app.inseeq.com/review/{article_id}The app_url field is returned on every article object in inseeq_list_articles, inseeq_get_article, and inseeq_create_article.
Tools Reference
Each tool is called via the MCP protocol. Your MCP client handles the JSON-RPC framing — you just need to know the tool names and parameters.
Identity
inseeq_agent_whoami
Returns the authenticated agent's identity and organization binding. Call this first to get your organization_id.
No parameters
Response
{
"agent_id": "a1b2c3d4-...",
"agent_name": "My Agent",
"organization_id": "org-uuid-...",
"key_type": "external"
}Organization
inseeq_get_organization
Get organization details by ID.
| Parameter | Type | Required | Description |
|---|---|---|---|
| organization_id | string (UUID) | Yes | Your organization ID (from inseeq_agent_whoami) |
Response
{
"id": "org-uuid-...",
"name": "Acme Corp",
"slug": "acme-corp",
"settings": { ... },
"created_at": "2025-01-01T00:00:00Z",
"updated_at": "2025-03-15T12:00:00Z"
}inseeq_get_brand_profile
Get the brand profile for an organization — voice, tone, vocabulary rules, and writing guidelines.
| Parameter | Type | Required | Description |
|---|---|---|---|
| organization_id | string (UUID) | Yes | — |
Response
{
"profile": {
"id": "uuid",
"organization_id": "uuid",
"brand_voice": "Professional yet approachable...",
"tone_guidelines": "Use active voice...",
"vocabulary_rules": "Never use 'synergy'..."
}
}inseeq_resolve_slack_channel
Look up which inseeq organization a Slack channel is mapped to.
| Parameter | Type | Required | Description |
|---|---|---|---|
| slack_channel_id | string | Yes | Slack channel ID (e.g. C01234ABCDE) |
| workspace_id | string | No | Slack workspace ID for disambiguation |
Response
{
"organization_id": "org-uuid-...",
"agent_name": "My Agent",
"external_channel_id": "C01234ABCDE",
"external_workspace_id": "T01234ABCDE",
"github_context_repo": "acme/content-repo"
}Articles
Three content types, three tool surfaces
Inseeq manages three content types with separate tool surfaces:
- Articles — long-form blog content with SEO fields, single featured image, CMS publishing. Use the article tools below.
- LinkedIn Posts — social posts with a multi-image carousel (up to 20), no SEO fields, no CMS publishing. See the LinkedIn Posts section.
- Calendar Events — lightweight markers for podcasts, press releases, and custom events. No publishing, no images, no comments. See the Calendar Events section.
The list/get/create/update tools are type-specific and reject IDs of the wrong type with a clear pointer to the correct tool. inseeq_update_article_status handles the article workflow (draft → published); events use the dedicated inseeq_update_event_status (scheduled ↔ done). Comments and image uploads work for articles and LinkedIn posts only — events reject both.
Pagination
inseeq_list_articles returns { articles: [...], total: N }. If total exceeds your limit, paginate by incrementing offset:
- Page 1:
limit=100, offset=0 - Page 2:
limit=100, offset=100 - Continue until
offset >= total
inseeq_list_articles
List long-form articles for your organization, optionally filtered by status. Returns up to 100 items by default. For LinkedIn posts, use inseeq_list_linkedin_posts.
| Parameter | Type | Required | Description |
|---|---|---|---|
| organization_id | string (UUID) | Yes | Your organization ID |
| status | string | No | Filter: draft, ready_to_write, writing, generating, in_review, approved, published |
| limit | number (1–1000) | No | Max items per page (default: 100) |
| offset | number | No | Skip this many items for pagination (default: 0) |
Response
{
"articles": [
{
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"title": "Getting Started with SEO",
"description": "A beginner's guide to search engine optimization",
"meta_description": "Learn the fundamentals of SEO...",
"status": "in_review",
"word_count": 1850,
"language": "English",
"created_at": "2025-01-15T10:30:00Z",
"updated_at": "2025-02-01T14:22:00Z",
"scheduled_publish_date": null,
"target_keyword": "seo basics",
"outline": "## Introduction\n## What is SEO?\n...",
"cluster_topic": "SEO Fundamentals",
"unresolved_comment_count": 2,
"app_url": "https://app.inseeq.com/review/3fa85f64-5717-4562-b3fc-2c963f66afa6"
}
],
"total": 42
}inseeq_get_article
Get a long-form article by ID. The `format` parameter controls body inclusion: 'markdown' (default) returns rendered content, 'structured' returns intro/sections/sources, 'metadata' omits the body entirely. Rejects LinkedIn post IDs with a pointer to inseeq_get_linkedin_post.
| Parameter | Type | Required | Description |
|---|---|---|---|
| article_id | string (UUID) | Yes | — |
| format | 'markdown' | 'structured' | 'metadata' | No | Body format. Default: 'markdown' (metadata + rendered content). 'structured' returns intro/sections/sources instead of content. 'metadata' omits the body. |
Default ('markdown') returns the 28 metadata columns plus the rendered `content` field. 'structured' swaps `content` for `intro`, `sections`, `sources`. 'metadata' returns no body. The `research` and `generation_costs` columns are always excluded.
Response
// Default — format='markdown':
{
"article": {
"id": "uuid",
"title": "Getting Started with SEO",
"content_type": "article",
"status": "in_review",
"word_count": 1850,
"version": 3,
"content": "# Getting Started with SEO\n\nSEO stands for...",
"excerpt": "A beginner's guide...",
"description": "Internal brief for the writing team",
"meta_description": "Learn the fundamentals of SEO...",
"seo_title": "SEO Basics: A Complete Guide",
"focus_keyphrase": "seo basics",
"wordpress_slug": "seo-basics-guide",
"featured_image_url": "https://...",
"featured_image_alt_text": "SEO diagram",
"language": "English",
"language_code": "en",
"target_keyword": "seo basics",
"outline": "## Introduction\n...",
"cluster_topic": "SEO Fundamentals",
"scheduled_publish_date": null,
"wordpress_post_id": null,
"wordpress_post_url": null,
"app_url": "https://app.inseeq.com/review/article-uuid",
"created_at": "2025-01-15T10:30:00Z",
"updated_at": "2025-02-01T14:22:00Z"
}
}
// format='structured':
// Same metadata columns, but swaps the content field for:
// "intro": "SEO stands for Search Engine Optimization...",
// "sections": [ { "heading": "...", "content": "..." }, ... ],
// "sources": "- [Google Search Central](https://...)"
// format='metadata':
// Same structure but content / intro / sections / sources
// are all omitted.inseeq_create_article
Create a new long-form article in draft status. For LinkedIn posts, use inseeq_create_linkedin_post instead.
| Parameter | Type | Required | Description |
|---|---|---|---|
| organization_id | string (UUID) | Yes | — |
| title | string | Yes | Article title (min 1 character) |
| content | string | No | Full markdown content |
| excerpt | string | No | Article summary/excerpt |
| description | string | No | Internal brief |
| meta_description | string | No | SEO meta description |
| seo_title | string | No | SEO title tag |
| slug | string | No | Custom URL slug |
| focus_keyphrase | string | No | SEO focus keyphrase |
| language | string | No | Language name (default: English) |
| language_code | string | No | Language code (default: en) |
| featured_image_url | string | No | Featured image URL |
| featured_image_alt_text | string | No | Image alt text |
| featured_image_title | string | No | Image title attribute |
| featured_image_caption | string | No | Image caption |
| featured_image_description | string | No | Image description |
| scheduled_publish_date | string (ISO 8601) | No | When to publish |
| target_keyword | string | No | Primary target keyword |
| outline | string | No | Article outline/structure |
| cluster_topic | string | No | Topic cluster this belongs to |
Response
{
"article": {
"id": "new-article-uuid",
"organization_id": "org-uuid",
"title": "Getting Started with SEO",
"status": "draft",
"word_count": 0,
"version": 1,
"app_url": "https://app.inseeq.com/review/new-article-uuid",
"created_at": "2025-03-15T10:00:00Z",
"updated_at": "2025-03-15T10:00:00Z"
}
}Article updates are split across four surgical tools
inseeq_update_article kitchen-sink tool has been split into four intent-bearing tools below. Each owns one slice of the article: body, metadata, SEO, or featured image. The body writer is the only tool that accepts content and refuses empty bodies — which makes the "image update accidentally wipes the article body" failure mode structurally impossible. The legacy tool still exists as a deprecated routing shim for backward compatibility (see below) but new integrations should target the surgical tools.inseeq_write_article_body
Write or rewrite the full article body. The only tool that accepts the content field. Refuses empty bodies.
| Parameter | Type | Required | Description |
|---|---|---|---|
| article_id | string (UUID) | Yes | — |
| content | string | Yes | Full markdown body (auto-parsed into intro/sections/sources). Must be non-empty after H1-strip and trim. |
Always pass the complete body — partial-body updates are not supported. If the body is empty after stripping the H1 and surrounding whitespace, the tool refuses with an error and writes nothing. Re-read the draft from disk before calling under context pressure. For LinkedIn posts, use inseeq_update_linkedin_post.
Side effects
- Always creates a version snapshot in gen_article_versions before writing
- Version number is incremented
- Logs an edit_made entry to the article activity audit trail
Response
{
"article": {
"id": "article-uuid",
"title": "Getting Started with SEO",
"version": 4,
"word_count": 1842,
"updated_at": "2025-03-15T14:30:00Z"
}
}inseeq_update_article_metadata
Update non-SEO, non-image metadata fields. Snapshots only on title change.
| Parameter | Type | Required | Description |
|---|---|---|---|
| article_id | string (UUID) | Yes | — |
| title | string | No | Article title (visible to readers) |
| excerpt | string | No | Article summary/excerpt |
| description | string | No | Internal brief / description |
| language | string | No | Language name (default: English) |
| language_code | string | No | Language code (default: en) |
| scheduled_publish_date | string | null | No | ISO 8601 date or null to clear |
| target_keyword | string | No | Primary target keyword |
| outline | string | No | Article outline / structure |
| cluster_topic | string | No | Topic cluster this belongs to |
Does not accept content — for body changes use inseeq_write_article_body. SEO fields (meta description, focus keyphrase, SEO title, slug) live in inseeq_update_article_seo. Image fields live in inseeq_set_featured_image.
Side effects
- Creates a version snapshot only when title is in the params
- Logs an edit_made activity entry only when title is in the params
- No snapshot or log for metadata-only changes (matches prior behavior)
Response
{
"article": {
"id": "article-uuid",
"title": "Updated Title",
"version": 4,
"word_count": 1842,
"updated_at": "2025-03-15T14:30:00Z"
}
}inseeq_update_article_seo
Update SEO-only fields. Never snapshots, never logs.
| Parameter | Type | Required | Description |
|---|---|---|---|
| article_id | string (UUID) | Yes | — |
| meta_description | string | No | SEO meta description |
| focus_keyphrase | string | No | SEO focus keyphrase |
| seo_title | string | No | SEO title tag |
| slug | string | No | Custom URL slug |
At least one SEO field must be provided. Does not touch content, metadata, or image fields.
Response
{
"article": {
"id": "article-uuid",
"version": 4,
"updated_at": "2025-03-15T14:30:00Z"
}
}inseeq_set_featured_image
Set or update featured-image fields. Cannot accept content — image updates can never wipe the body.
| Parameter | Type | Required | Description |
|---|---|---|---|
| article_id | string (UUID) | Yes | — |
| featured_image_url | string | No | Featured image URL |
| featured_image_alt_text | string | No | Image alt text |
| featured_image_title | string | No | Image title attribute |
| featured_image_caption | string | No | Image caption |
| featured_image_description | string | No | Image description |
At least one featured-image field must be provided. The lack of a content parameter is intentional — it makes the historical bug shape (image update with stub content wiping the body) structurally unrepresentable. For body edits, use inseeq_write_article_body.
Response
{
"article": {
"id": "article-uuid",
"version": 4,
"updated_at": "2025-03-15T14:30:00Z"
}
}Deprecated: inseeq_update_article (routing shim)
content with any featured_image_* field — that combination was the failure shape that motivated this split. Cross-category combinations (for example content + meta_description) are split into sequenced internal calls and are not atomic; if the body write succeeds and the SEO write fails, the article is partially updated.inseeq_update_article
[deprecated] Routing shim — dispatches to inseeq_write_article_body / inseeq_update_article_metadata / inseeq_update_article_seo / inseeq_set_featured_image. Refuses content + featured_image_* in the same call.
| Parameter | Type | Required | Description |
|---|---|---|---|
| article_id | string (UUID) | Yes | — |
| title | string | No | — |
| content | string | No | Full markdown body. Forwarded to inseeq_write_article_body. |
| excerpt | string | No | — |
| description | string | No | — |
| language | string | No | — |
| language_code | string | No | — |
| meta_description | string | No | — |
| seo_title | string | No | — |
| slug | string | No | — |
| focus_keyphrase | string | No | — |
| featured_image_url | string | No | — |
| featured_image_alt_text | string | No | — |
| featured_image_title | string | No | — |
| featured_image_caption | string | No | — |
| featured_image_description | string | No | — |
| scheduled_publish_date | string | null | No | — |
| target_keyword | string | No | — |
| outline | string | No | — |
| cluster_topic | string | No | — |
Deprecated. Migrate to the four surgical tools above. Calls combining content with any featured_image_* field are rejected outright; other multi-category calls are split into sequenced internal dispatches and are not atomic. Behavior across categories matches the surgical tools individually (snapshots on body/title; no snapshot for SEO or image). For LinkedIn posts, use inseeq_update_linkedin_post.
Side effects
- Refuses with an error if content and any featured_image_* field are both present
- Forwards body changes to inseeq_write_article_body (with empty-body refusal)
- Per-delegate snapshot/audit semantics inherit from each surgical tool
Response
{
"article": {
"id": "article-uuid",
"version": 4,
"updated_at": "2025-03-15T14:30:00Z"
}
}inseeq_update_article_status
Change an article's workflow status. See Article Status Lifecycle above for the full flow and side effects.
| Parameter | Type | Required | Description |
|---|---|---|---|
| article_id | string (UUID) | Yes | — |
| status | string | Yes | draft, ready_to_write, writing, generating, in_review, approved, published |
| scheduled_publish_date | string (ISO 8601) | No | Set a scheduled publish date |
Side effects
- Transitioning to writing sets writing_started_at
- Transitioning to in_review sends email notifications to org reviewers
- Transitioning to approved/published sets timestamp and actor fields
- All transitions are logged in the article activity audit trail
Response
{
"article": {
"id": "article-uuid",
"status": "in_review",
"updated_at": "2025-03-15T14:30:00Z",
"version": 4
}
}inseeq_publish_article
Publish an approved article to the organization's active CMS platform (WordPress, Webflow, Contentful, or emdash). Articles only — LinkedIn posts cannot be auto-published and must be posted manually (fetch content + post_images, then post on LinkedIn). Article must be in 'approved' status.
| Parameter | Type | Required | Description |
|---|---|---|---|
| article_id | string (UUID) | Yes | — |
| status | string | No | Post status (WordPress only): publish (default), draft, or pending |
| categories | number[] | No | Category IDs (WordPress only) |
| tags | number[] | No | Tag IDs (WordPress only) |
The article must be in 'approved' status. Call inseeq_update_article_status first if needed. The categories and tags parameters only apply to WordPress.
Response
{
"post_id": "12345",
"url": "https://example.com/blog/seo-basics-guide",
"platform": "wordpress",
"published_at": "2025-03-15T15:00:00Z"
}inseeq_create_image_upload
Create a presigned upload URL for an image attached to an article or LinkedIn post. Returns both the upload URL (PUT binary) and the final public URL in one call. After PUTing the file binary, pass public_url into inseeq_set_featured_image for articles, or include it in post_images[] for inseeq_update_linkedin_post.
| Parameter | Type | Required | Description |
|---|---|---|---|
| content_id | string (UUID) | Yes | Article or LinkedIn post ID |
| content_type | string | Yes | image/png, image/jpeg, image/gif, or image/webp |
Single-tool flow: (1) call this; (2) PUT the file binary to upload_url; (3) pass public_url into the relevant update tool. The public URL is deterministic from the storage path, so no separate finalize step is needed. Storage objects live in the article-images bucket at {organization_id}/{content_id}/{uuid}.{ext}.
Response
{
"upload_url": "https://storage.supabase.co/...?token=...",
"public_url": "https://storage.inseeq.com/article-images/org-uuid/content-uuid/abc123.png",
"storage_path": "org-uuid/content-uuid/abc123.png",
"token": "upload-token",
"content_type": "image/png"
}inseeq_delete_article
Delete a long-form article. Irreversible. Deletes the article row and its featured_image_url storage object. External keys can only delete drafts; approved or published articles require an internal/admin key. Rejects LinkedIn post IDs with a pointer to inseeq_delete_linkedin_post.
| Parameter | Type | Required | Description |
|---|---|---|---|
| article_id | string (UUID) | Yes | — |
Move the article back to draft first if an external agent needs to delete an approved/published article, or escalate to an Inseeq admin. Inline body images embedded inside content (if any) are NOT auto-deleted — only the featured image. Comments, versions, and activity rows cascade automatically.
Side effects
- Deletes the storage object at featured_image_url (best-effort)
- Cascade-deletes gen_article_versions, article_comments, and article_review_activity
Response
{
"deleted": true,
"article_id": "uuid"
}LinkedIn Posts
LinkedIn post workflow
LinkedIn posts share the review/approval lifecycle with articles (draft → in_review → approved → published) but differ in content shape:
- Body — flat text (no headings/sections). Up to 3,000 characters recommended (LinkedIn's soft limit).
- Images — ordered array of up to 20
{url, alt_text}objects in thepost_imagesfield. Upload viainseeq_create_image_uploadfirst to obtain URLs. - No SEO fields — meta_description, slug, focus_keyphrase are not applicable.
- Manual publishing — LinkedIn posts cannot be auto-published.
inseeq_publish_articlerejects them. The approved post + images are downloaded by the user and posted manually on LinkedIn. - Image cleanup is automatic — when you remove an image URL from
post_imagesviainseeq_update_linkedin_post, the underlying storage object is deleted as part of the update. Deleting the post viainseeq_delete_linkedin_postalso removes all referenced storage objects. For uploaded-but-never-attached orphans, useinseeq_delete_linkedin_post_image.
inseeq_list_linkedin_posts
List LinkedIn posts for your organization, optionally filtered by status. Returns up to 100 items by default. For long-form articles, use inseeq_list_articles.
| Parameter | Type | Required | Description |
|---|---|---|---|
| organization_id | string (UUID) | Yes | — |
| status | string | No | Filter: draft, ready_to_write, writing, generating, in_review, approved, published |
| limit | number (1–1000) | No | Max items per page (default: 100) |
| offset | number | No | Skip this many items for pagination (default: 0) |
Response
{
"posts": [
{
"id": "4a1b2c3d-...",
"title": "Guess what I just found out...",
"content": "Guess what I just found out?\n\nThe people behind...",
"description": null,
"status": "approved",
"word_count": 42,
"language": "English",
"created_at": "2025-02-10T09:00:00Z",
"updated_at": "2025-02-11T15:30:00Z",
"scheduled_publish_date": "2025-02-15T09:00:00Z",
"post_images": [
{ "url": "https://storage.../01.png", "alt_text": "Diagram" },
{ "url": "https://storage.../02.png", "alt_text": "Chart" }
],
"unresolved_comment_count": 0,
"app_url": "https://app.inseeq.com/review/4a1b2c3d-..."
}
],
"total": 8
}inseeq_get_linkedin_post
Get a LinkedIn post by ID. Returns content, post_images, and metadata. Rejects article IDs with a pointer to inseeq_get_article.
| Parameter | Type | Required | Description |
|---|---|---|---|
| post_id | string (UUID) | Yes | — |
Always returns full content (post bodies are short — no format flag needed).
Response
{
"post": {
"id": "uuid",
"title": "Guess what I just found out...",
"content_type": "linkedin_post",
"content": "Guess what I just found out?\n\nThe people behind...",
"description": null,
"status": "approved",
"word_count": 42,
"version": 2,
"language": "English",
"language_code": "en",
"post_images": [
{ "url": "https://storage.../01.png", "alt_text": "Diagram" },
{ "url": "https://storage.../02.png", "alt_text": "Chart" }
],
"scheduled_publish_date": "2025-02-15T09:00:00Z",
"review_requested_at": "2025-02-10T10:00:00Z",
"approved_at": "2025-02-11T15:30:00Z",
"published_at": null,
"app_url": "https://app.inseeq.com/review/uuid",
"created_at": "2025-02-10T09:00:00Z",
"updated_at": "2025-02-11T15:30:00Z"
}
}inseeq_create_linkedin_post
Create a new LinkedIn post in draft status. Title is auto-derived from the first non-empty line of content if omitted.
| Parameter | Type | Required | Description |
|---|---|---|---|
| organization_id | string (UUID) | Yes | — |
| content | string | No | Post body (plain text or light markdown) |
| title | string | No | Display title. Auto-derived from content if omitted. |
| post_images | array of {url, alt_text} | No | Ordered carousel images, max 20. Upload via inseeq_create_image_upload first. |
| scheduled_publish_date | string (ISO 8601) | No | — |
| description | string | No | Internal brief (not published) |
| language | string | No | Language name (default: English) |
| language_code | string | No | Language code (default: en) |
Response
{
"post": {
"id": "new-post-uuid",
"organization_id": "org-uuid",
"content_type": "linkedin_post",
"title": "Guess what I just found out...",
"content": "Guess what I just found out?\n\nThe people behind...",
"post_images": [
{ "url": "https://storage.../01.png", "alt_text": "Diagram" }
],
"status": "draft",
"version": 1,
"app_url": "https://app.inseeq.com/review/new-post-uuid",
"created_at": "2025-03-15T10:00:00Z",
"updated_at": "2025-03-15T10:00:00Z"
}
}inseeq_update_linkedin_post
Update a LinkedIn post. Creates a version snapshot on content/title changes.
| Parameter | Type | Required | Description |
|---|---|---|---|
| post_id | string (UUID) | Yes | — |
| content | string | No | Post body |
| title | string | No | Explicit title. If omitted and content is updated, title is auto-derived from the first line. |
| post_images | array of {url, alt_text} | No | Replaces the full ordered carousel array. Pass [] to clear all images. |
| scheduled_publish_date | string | null | No | ISO 8601 timestamp, or null to clear |
post_images is a full-array replacement — pass the complete desired ordering. There is no partial add/remove/reorder API; for any change, send the full new array.
Side effects
- Creates a version snapshot before content or title changes
- Version number is incremented on each update
- Title auto-refreshes from content when content is updated and title is not supplied
- Rejects with an error if the provided ID belongs to an article (use inseeq_write_article_body / inseeq_update_article_metadata / inseeq_update_article_seo / inseeq_set_featured_image instead)
- Storage objects for image URLs dropped from post_images are deleted automatically (best-effort)
Response
{
"post": {
"id": "post-uuid",
"version": 3,
"updated_at": "2025-03-15T14:30:00Z"
}
}inseeq_delete_linkedin_post
Delete a LinkedIn post. Irreversible. Deletes the post row and every storage object referenced by its post_images. External keys can only delete drafts; approved or published posts require an internal/admin key. Rejects article IDs with a pointer to inseeq_delete_article.
| Parameter | Type | Required | Description |
|---|---|---|---|
| post_id | string (UUID) | Yes | — |
Move the post back to draft first if an external agent needs to delete an approved/published post, or escalate to an Inseeq admin. Comments, versions, and activity rows cascade automatically.
Side effects
- Deletes all storage objects referenced by post_images (best-effort)
- Cascade-deletes gen_article_versions, article_comments, and article_review_activity
Response
{
"deleted": true,
"post_id": "uuid"
}inseeq_delete_linkedin_post_image
Delete a LinkedIn post image from storage by storage_path. Use this for images uploaded via inseeq_create_image_upload that were never attached to the post (orphans).
| Parameter | Type | Required | Description |
|---|---|---|---|
| post_id | string (UUID) | Yes | — |
| storage_path | string | Yes | Path returned by inseeq_create_image_upload; must start with {organization_id}/{post_id}/ |
If the image is still referenced in the post's post_images array, this tool rejects the call — detach it first via inseeq_update_linkedin_post, which handles cleanup automatically. Use this tool only for uploaded-but-never-attached images.
Response
{
"deleted": true,
"storage_path": "org/post/uuid.png"
}Calendar Events
Lightweight calendar markers
Events are calendar items the team uses to plan around things that aren't written content — podcasts they're recording, press releases they're publishing, webinars they're hosting. Compared to articles and LinkedIn posts, events are intentionally minimal:
- No featured image or carousel
- No SEO fields, no CMS publishing
- No version history, no comments
- Status is a simple two-state toggle:
scheduled↔done
Three subtypes share one tool family because their shape is uniform — only custom_event carries an extra custom_event_label field for the free-text type label (e.g. "Webinar", "Trade Show").
event_type | Display label | Notes |
|---|---|---|
| podcast | Podcast | Fixed label |
| press_release | Press Release | Fixed label |
| custom_event | User-supplied | Requires custom_event_label |
The app_url returned on event responses points to /calendar. Events open in an inline edit modal when their card is clicked, so there's no per-event deep link.
inseeq_list_events
List calendar events for your organization. Filter by event_type and/or status. Returns up to 100 items by default. For articles use inseeq_list_articles; for LinkedIn posts use inseeq_list_linkedin_posts.
| Parameter | Type | Required | Description |
|---|---|---|---|
| organization_id | string (UUID) | Yes | — |
| event_type | 'podcast' | 'press_release' | 'custom_event' | No | Filter to a single subtype |
| status | 'scheduled' | 'done' | No | — |
| limit | number (1–1000) | No | Max items per page (default: 100) |
| offset | number | No | Skip this many items for pagination (default: 0) |
Response
{
"events": [
{
"id": "ev-uuid-1",
"content_type": "podcast",
"title": "Marketing AI on the Acme Show",
"content": "Topics: agent workflows, distribution...",
"status": "scheduled",
"scheduled_publish_date": "2025-04-20T03:00:00Z",
"custom_event_label": null,
"created_at": "2025-03-01T12:00:00Z",
"updated_at": "2025-03-01T12:00:00Z",
"app_url": "https://app.inseeq.com/calendar"
},
{
"id": "ev-uuid-2",
"content_type": "custom_event",
"title": "Spring Trade Show booth",
"content": "",
"status": "scheduled",
"scheduled_publish_date": "2025-05-10T03:00:00Z",
"custom_event_label": "Trade Show",
"created_at": "2025-03-02T09:00:00Z",
"updated_at": "2025-03-02T09:00:00Z",
"app_url": "https://app.inseeq.com/calendar"
}
],
"total": 2
}inseeq_get_event
Get a calendar event by ID. Rejects IDs belonging to articles or LinkedIn posts with a pointer to the correct tool.
| Parameter | Type | Required | Description |
|---|---|---|---|
| event_id | string (UUID) | Yes | — |
Response
{
"event": {
"id": "ev-uuid-1",
"content_type": "podcast",
"title": "Marketing AI on the Acme Show",
"content": "Topics: agent workflows, distribution. Host: Jane Doe.",
"status": "scheduled",
"scheduled_publish_date": "2025-04-20T03:00:00Z",
"custom_event_label": null,
"created_at": "2025-03-01T12:00:00Z",
"updated_at": "2025-03-01T12:00:00Z",
"app_url": "https://app.inseeq.com/calendar"
}
}inseeq_create_event
Create a calendar event. Events start in 'scheduled' status. For custom_event, custom_event_label is required.
| Parameter | Type | Required | Description |
|---|---|---|---|
| organization_id | string (UUID) | Yes | — |
| event_type | 'podcast' | 'press_release' | 'custom_event' | Yes | — |
| title | string | Yes | Event title |
| content | string | No | Free-text notes attached to the event |
| custom_event_label | string | No | Required for custom_event. Must be omitted for other types. |
| scheduled_publish_date | string (ISO 8601) | No | When the event occurs |
Events bypass the article workflow entirely — no credits, no review, no version snapshot.
Response
{
"event": {
"id": "new-event-uuid",
"content_type": "custom_event",
"title": "Spring Trade Show booth",
"content": "",
"status": "scheduled",
"scheduled_publish_date": "2025-05-10T03:00:00Z",
"custom_event_label": "Trade Show",
"created_at": "2025-03-02T09:00:00Z",
"updated_at": "2025-03-02T09:00:00Z",
"app_url": "https://app.inseeq.com/calendar"
}
}inseeq_update_event
Update editable fields on a calendar event: title, content (notes), scheduled date, and (for custom_event only) custom_event_label. Status changes go through inseeq_update_event_status. Rejects non-event IDs.
| Parameter | Type | Required | Description |
|---|---|---|---|
| event_id | string (UUID) | Yes | — |
| title | string | No | — |
| content | string | No | Notes. Pass an empty string to clear. |
| custom_event_label | string | No | Only valid on custom_event rows. Cannot be empty. |
| scheduled_publish_date | string (ISO 8601) | null | No | Pass null to clear. |
Response
{
"event": {
"id": "ev-uuid-1",
"content_type": "podcast",
"title": "Updated podcast title",
"content": "Updated notes.",
"status": "scheduled",
"scheduled_publish_date": "2025-04-21T03:00:00Z",
"custom_event_label": null,
"updated_at": "2025-03-15T16:30:00Z",
"app_url": "https://app.inseeq.com/calendar"
}
}inseeq_update_event_status
Toggle an event between 'scheduled' and 'done'. Rejects article/LinkedIn-post IDs (those use inseeq_update_article_status with the article-status set).
| Parameter | Type | Required | Description |
|---|---|---|---|
| event_id | string (UUID) | Yes | — |
| status | 'scheduled' | 'done' | Yes | — |
Response
{
"event": {
"id": "ev-uuid-1",
"status": "done",
"updated_at": "2025-04-20T18:00:00Z"
}
}inseeq_delete_event
Delete a calendar event. Irreversible. Events have no associated storage objects, version history, or comments to clean up. Both external and internal keys can delete events regardless of status.
| Parameter | Type | Required | Description |
|---|---|---|---|
| event_id | string (UUID) | Yes | — |
Response
{
"deleted": true,
"event_id": "ev-uuid-1"
}Comments
inseeq_list_comments
List all comments for an article, organized into threads with unresolved count.
| Parameter | Type | Required | Description |
|---|---|---|---|
| article_id | string (UUID) | Yes | — |
Response
{
"authors": {
"user-uuid-1": { "email": "alice@example.com", "full_name": "Alice Smith" },
"user-uuid-2": { "email": "bob@example.com", "full_name": null }
},
"comments": [
{
"id": "comment-uuid",
"content": "This intro needs more context about the target audience.",
"author_id": "user-uuid-1",
"parent_id": null,
"is_resolved": false,
"resolved_by": null,
"created_at": "2025-02-10T09:15:00Z",
"selection_start": 0,
"selection_end": 145,
"selected_text": "SEO stands for Search Engine Optimization...",
"replies": [
{
"id": "reply-uuid",
"content": "Good point — I'll add a paragraph about personas.",
"author_id": "user-uuid-2",
"parent_id": "comment-uuid",
"created_at": "2025-02-10T10:30:00Z"
}
]
}
],
"total": 5,
"unresolved_count": 2
}inseeq_poll_new_comments
Poll for new comments on a single article since a given timestamp.
| Parameter | Type | Required | Description |
|---|---|---|---|
| article_id | string (UUID) | Yes | — |
| since | string (ISO 8601) | Yes | Only return comments created after this time |
Use this to check for new feedback on a specific article. For polling across all articles, use inseeq_poll_pending_comments instead.
Response
{
"authors": {
"user-uuid": { "email": "alice@example.com", "full_name": "Alice Smith" }
},
"new_comments": [
{
"id": "comment-uuid",
"content": "Please add a sources section.",
"author_id": "user-uuid",
"parent_id": null,
"is_resolved": false,
"created_at": "2025-02-11T08:00:00Z",
"selection_start": null,
"selection_end": null,
"selected_text": null
}
],
"count": 1,
"polled_since": "2025-02-10T00:00:00Z"
}inseeq_poll_pending_comments
Poll all unresolved top-level comment threads across an organization's articles, grouped by article. Each thread includes its full reply chain so the agent can detect new replies (for example a user accepting a proposal) without fetching threads one at a time.
| Parameter | Type | Required | Description |
|---|---|---|---|
| organization_id | string (UUID) | Yes | — |
| since | string (ISO 8601) | No | Accepted for backwards compatibility but ignored. Filtering by a thread's top-level created_at would hide threads with recent replies — e.g. a user accepting a proposal that was posted earlier. |
Top-level threads authored by the agent itself are skipped — only human-initiated threads are returned. Replies do include agent-authored messages, flagged with is_agent_reply: true (thread context, not new triggers). Each thread's last_activity_at is the max of its reply timestamps and the top-level created_at — use it to detect what changed since the last poll.
Response
{
"authors": {
"user-uuid": { "email": "alice@example.com", "full_name": "Alice Smith" },
"agent-uuid": { "email": "agent@inseeq.com", "full_name": "Inseeq Agent" }
},
"articles": [
{
"article_id": "article-uuid-1",
"title": "Getting Started with SEO",
"status": "in_review",
"comments": [
{
"id": "comment-uuid",
"content": "The H2 structure needs work.",
"author_id": "user-uuid",
"created_at": "2025-02-10T08:00:00Z",
"selection_start": null,
"selection_end": null,
"selected_text": null,
"replies": [
{
"id": "reply-uuid-1",
"content": "Here's a revised outline — H2s reordered.",
"author_id": "agent-uuid",
"created_at": "2025-02-10T08:05:00Z",
"is_agent_reply": true
},
{
"id": "reply-uuid-2",
"content": "Looks good, apply it.",
"author_id": "user-uuid",
"created_at": "2025-02-11T14:22:00Z",
"is_agent_reply": false
}
],
"last_activity_at": "2025-02-11T14:22:00Z"
}
]
}
],
"total_unresolved": 3,
"polled_since": null
}inseeq_add_comment
Add a comment to an article, optionally as a reply to an existing comment. Supports inline text selection.
| Parameter | Type | Required | Description |
|---|---|---|---|
| article_id | string (UUID) | Yes | — |
| content | string | Yes | Comment text (min 1 character) |
| parent_id | string (UUID) | No | Reply to this comment (creates a threaded reply) |
| selection_start | number | No | Character offset where selected text begins |
| selection_end | number | No | Character offset where selected text ends |
| selected_text | string | No | Cached copy of the selected text (for display even if content changes) |
Response
{
"comment": {
"id": "new-comment-uuid",
"content": "Consider adding an FAQ section.",
"parent_id": null,
"created_at": "2025-02-11T09:00:00Z"
}
}inseeq_resolve_comment
Resolve or unresolve a top-level comment thread.
| Parameter | Type | Required | Description |
|---|---|---|---|
| article_id | string (UUID) | Yes | — |
| comment_id | string (UUID) | Yes | — |
| resolved | boolean | No | true to resolve, false to unresolve (default: true) |
Only top-level comments can be resolved. Replies cannot be resolved individually — resolving the parent resolves the entire thread.
Response
{
"comment": {
"id": "comment-uuid",
"is_resolved": true,
"resolved_by": "agent-uuid",
"updated_at": "2025-02-11T10:00:00Z"
}
}Context (Knowledge Base)
Context is your organization's knowledge base — brand guidelines, writing style, product info, and anything else your agent needs to produce on-brand content. It's organized into sections (like folders) containing entries (like files), each identified by a unique key.
| Access Level | Agent Can Read | Agent Can Write | Visible in UI | Use Case |
|---|---|---|---|---|
| all | Yes | Yes | Yes | Default — shared context |
| agent_only | Yes | Yes | No | Agent memory, internal state |
| human_only | No | No | Yes | Private human notes, internal docs |
| frozen | Yes | No | Yes | Immutable reference (brand guidelines, legal) |
Typical workflows
Full sync (easiest — like git pull/push):
- Call
inseeq_pull_contextto download everything as a flat list of{ path, content }pairs - Modify entries locally as needed
- Call
inseeq_push_contextwith the full set — replaces all writable entries, preserves protected ones
Pull output is push input. Entries with human_only or frozen access are never overwritten or deleted.
Surgical read (discover → fetch specific entries):
- Call
inseeq_list_contextto see available sections and entries (metadata only, no content) - Call
inseeq_get_contextwithsection+keyto fetch a single entry
Surgical write (update specific entries without affecting others):
- Call
inseeq_save_contextwith the section slug and entries to create or update - Call
inseeq_delete_contextto remove specific entries by key
inseeq_get_context_index
Get the curated context index (map of content) if one exists. This is a hand-written overview document — for auto-generated structure, use inseeq_list_context instead.
| Parameter | Type | Required | Description |
|---|---|---|---|
| organization_id | string (UUID) | Yes | — |
Response
{
"index": "# Context Map\n\n## Brand\n- voice: Brand voice guidelines\n...",
"message": null
}
// Returns { "index": null, "message": "No index found" }
// if no curated index exists.inseeq_list_context
List context sections and entry metadata without content. Use to discover available context before fetching.
| Parameter | Type | Required | Description |
|---|---|---|---|
| organization_id | string (UUID) | Yes | — |
| section | string | No | Filter by section slug |
Response
{
"sections": [
{
"slug": "brand",
"label": "Brand",
"description": "Brand identity and guidelines",
"entries": [
{
"key": "voice",
"name": "Brand Voice",
"description": "Tone and voice guidelines",
"type": "guideline",
"access": "frozen",
"updated_at": "2025-01-10T00:00:00Z"
},
{
"key": "vocabulary",
"name": "Vocabulary Rules",
"description": null,
"type": null,
"access": "all",
"updated_at": "2025-02-05T00:00:00Z"
}
]
}
]
}inseeq_pull_context
Download all context entries as a flat list. Each entry has a path (section/key), content, and access level. Output format matches inseeq_push_context input.
| Parameter | Type | Required | Description |
|---|---|---|---|
| organization_id | string (UUID) | Yes | — |
Response
{
"context": [
{
"path": "brand/voice",
"content": "# Brand Voice\n\nWe write in a professional...",
"access": "frozen"
},
{
"path": "memory/last-review",
"content": "Reviewed 5 articles on 2025-02-10...",
"access": "agent_only"
}
],
"total": 12
}inseeq_push_context
Upload a full set of context entries, replacing all writable content. Entries with human_only or frozen access are preserved. Entries in the DB but not in your payload are deleted.
| Parameter | Type | Required | Description |
|---|---|---|---|
| organization_id | string (UUID) | Yes | — |
| context | array of {path, content} | Yes | Full context set. path format: "section/key" (e.g. "brand/voice") |
Side effects
- Writable entries in the DB that are NOT in your payload are deleted
- Sections referenced in paths but not yet existing are created automatically
- Entries with human_only or frozen access are preserved (skipped, not overwritten)
Response
{
"pushed": 8,
"deleted": 2,
"skipped": [
{ "path": "brand/voice", "reason": "frozen" },
{ "path": "internal/notes", "reason": "human_only" }
]
}inseeq_get_context
Get context entries with full content. Fetch a single entry by section + key, filter by section, or get everything.
| Parameter | Type | Required | Description |
|---|---|---|---|
| organization_id | string (UUID) | Yes | — |
| section | string | No | Filter by section slug (e.g. "brand", "memory") |
| key | string | No | Fetch a single entry by key (requires section) |
| compact | boolean | No | Return only essential fields per entry (default: false) |
When key is provided, returns a single { entry } object instead of { sections }. Compact mode omits display_order, metadata, last_modified_by, and UI-specific fields.
Response
// Single entry (section + key provided):
{
"entry": {
"key": "voice",
"content": "# Brand Voice\n\nWe write in a professional...",
"name": "Brand Voice",
"description": "Tone and voice guidelines",
"type": "guideline",
"access": "frozen",
"updated_at": "2025-01-10T00:00:00Z"
}
}
// Multiple entries (section only, or no filters):
{
"sections": [
{
"slug": "brand",
"label": "Brand",
"description": "Brand identity and guidelines",
"entries": [
{
"key": "voice",
"content": "# Brand Voice\n...",
"access": "frozen",
"updated_at": "2025-01-10T00:00:00Z"
}
]
}
]
}inseeq_save_context
Create or update context entries within a section. Auto-creates the section if it doesn't exist. Rejects writes to entries marked as human_only or frozen.
| Parameter | Type | Required | Description |
|---|---|---|---|
| organization_id | string (UUID) | Yes | — |
| section | string | Yes | Section slug (e.g. "brand", "memory") |
| entries | array of {key, content, label?, description?, access?} | Yes | Entries to upsert. key is the unique identifier within the section. |
Access values: all (default), agent_only. You cannot set access to human_only or frozen via this tool — those are managed in the inseeq UI. Writes to existing human_only or frozen entries are rejected.
Response
{
"saved": 3,
"section": "memory",
"keys": ["last-review", "article-notes", "pending-tasks"]
}inseeq_delete_context
Delete context entries by key within a section. Rejects deletes on entries marked as human_only or frozen.
| Parameter | Type | Required | Description |
|---|---|---|---|
| organization_id | string (UUID) | Yes | — |
| section | string | Yes | Section slug |
| keys | string[] | Yes | Entry keys to delete |
Response
{
"deleted": 2,
"section": "memory",
"keys": ["old-notes", "stale-cache"]
}inseeq_log_context_read
Log that the agent read a context file, for activity tracking.
| Parameter | Type | Required | Description |
|---|---|---|---|
| organization_id | string (UUID) | Yes | — |
| path | string | Yes | Context file path, e.g. /context/visual/brand-kit.md |
| session_id | string | Yes | Your session/conversation ID |
| platform | string | Yes | "slack" or "web" |
Response
{ "logged": true }Performance
inseeq_get_article_queries
Get stored search query data for an article. Aggregated from daily Search Console ingestion.
| Parameter | Type | Required | Description |
|---|---|---|---|
| article_id | string (UUID) | Yes | — |
| start_date | string (YYYY-MM-DD) | Yes | — |
| end_date | string (YYYY-MM-DD) | Yes | — |
| sort_by | string | No | impressions, clicks, or position (default: impressions) |
| limit | number (1–200) | No | Default: 50 |
Data comes from daily GSC ingestion (top N queries per article). For live GSC queries with custom filters, internal agents can use inseeq_search_console_query.
Response
{
"queries": [
{
"query": "seo basics",
"impressions": 1250,
"clicks": 89,
"avg_position": 4.2,
"ctr": 0.071,
"trend_percent": -12.5
},
{
"query": "what is seo",
"impressions": 980,
"clicks": 45,
"avg_position": 8.7,
"ctr": 0.046,
"trend_percent": 5.3
}
],
"total_queries": 47,
"date_range": {
"start": "2025-01-01",
"end": "2025-01-31"
}
}
// trend_percent: negative = position improved,
// positive = position worsened (vs. prior period)inseeq_get_article_performance
Get GA4 page-level metrics for an article over a date range. Reads from stored daily data.
| Parameter | Type | Required | Description |
|---|---|---|---|
| article_id | string (UUID) | Yes | — |
| start_date | string (YYYY-MM-DD) | Yes | — |
| end_date | string (YYYY-MM-DD) | Yes | — |
| granularity | string | No | daily, weekly, or monthly (default: weekly) |
Response
{
"periods": [
{
"period_start": "2025-01-06",
"pageviews": 320,
"organic_pageviews": 245,
"entrances": 198,
"avg_time_on_page_seconds": 142,
"bounce_rate": 0.38
},
{
"period_start": "2025-01-13",
"pageviews": 410,
"organic_pageviews": 312,
"entrances": 256,
"avg_time_on_page_seconds": 158,
"bounce_rate": 0.35
}
],
"totals": {
"pageviews": 730,
"organic_pageviews": 557,
"organic_share": 0.76,
"avg_bounce_rate": 0.365,
"avg_time_on_page_seconds": 150
}
}inseeq_list_portfolio_performance
Aggregate performance across all published articles for your organization. Returns inseeq-managed articles only.
| Parameter | Type | Required | Description |
|---|---|---|---|
| organization_id | string (UUID) | Yes | — |
| period | string | Yes | last_7d, last_30d, or last_90d |
| sort_by | string | No | pageviews, trend, or position (default: pageviews) |
| limit | number (1–200) | No | Default: 100 |
Response
{
"org_totals": {
"total_pageviews": 15420,
"total_organic_pageviews": 11890,
"organic_share": 0.77,
"trend_percent": 12.3,
"published_article_count": 24,
"traffic_goals": null
},
"articles": [
{
"article_id": "uuid",
"title": "Getting Started with SEO",
"slug": "seo-basics-guide",
"days_since_published": 45,
"pageviews": 2150,
"organic_pageviews": 1820,
"trend_percent": 8.5,
"avg_position": 4.2,
"protected": false,
"has_traffic_goals": true,
"pending_recommendation": false
}
]
}inseeq_get_traffic_goals
Get traffic goals for an article with organization-level defaults as fallback.
| Parameter | Type | Required | Description |
|---|---|---|---|
| organization_id | string (UUID) | Yes | — |
| article_id | string (UUID) | Yes | — |
Response
{
"article_goals": {
"protected": false,
"protect_reason": null,
"target_keyword": "seo basics",
"target_position": 3,
"monthly_traffic_target": 5000,
"notes": "Key landing page for organic funnel"
},
"org_goals": {
"monthly_organic_sessions": 50000,
"monthly_organic_growth_pct": 10,
"notes": null
}
}
// Either field can be null if no goals are configured.inseeq_get_review_settings
Get the review configuration for your organization. Returns all settings with defaults applied.
| Parameter | Type | Required | Description |
|---|---|---|---|
| organization_id | string (UUID) | Yes | — |
Response
{
"review_enabled": true,
"articles_per_cycle": 5,
"review_cadence_days": 7,
"min_article_age_days": 30,
"notification_channel": "slack",
"full_site_insights": false,
"queries_per_article": 50
}inseeq_get_article_recommendations
Get existing optimization recommendations for an article. Use to check for pending recommendations before submitting new ones.
| Parameter | Type | Required | Description |
|---|---|---|---|
| article_id | string (UUID) | Yes | — |
| status | string | No | Filter: pending, accepted, rejected, applied, reverted |
| limit | number (1–50) | No | Default: 10 |
Response
{
"recommendations": [
{
"id": "rec-uuid",
"recommendation_type": "update",
"diagnosis": ["declining_traffic", "keyword_cannibalization"],
"priority": "high",
"status": "pending",
"justification": "Traffic dropped 25% over 30 days...",
"performance_summary": { ... },
"content_changes": null,
"created_at": "2025-02-15T10:00:00Z",
"reviewed_at": null,
"applied_at": null
}
],
"total": 3
}Skills
inseeq_get_enabled_skills
Get the list of enabled skills for your organization.
| Parameter | Type | Required | Description |
|---|---|---|---|
| organization_id | string (UUID) | Yes | — |
Response
{
"skills": [
{
"skill_slug": "article-writer",
"is_active": true,
"activated_at": "2025-01-15T00:00:00Z"
},
{
"skill_slug": "seo-reviewer",
"is_active": true,
"activated_at": "2025-02-01T00:00:00Z"
}
]
}inseeq_get_skill_content
Get enabled skills with full file content for your organization. Returns skill definitions including file paths, content, and metadata.
| Parameter | Type | Required | Description |
|---|---|---|---|
| organization_id | string (UUID) | Yes | — |
Automatically merges global skill definitions with per-organization overrides. Organization-specific overrides take precedence.
Response
{
"skills": [
{
"skill_slug": "article-writer",
"files": [
{
"path": "skills/article-writer/prompt.md",
"content": "# Article Writer\n\nYou are an expert...",
"name": "Article Writer",
"description": "Writes SEO-optimized articles",
"node_type": "skill",
"version": 2
}
]
}
]
}Internal-Only Tools
The following tools appear in the MCP schema but are restricted to internal (first-party) inseeq agents. Calling them with an external API key returns an authorization error.
| Tool | Purpose |
|---|---|
| inseeq_search_console_query | Live proxy to Google Search Console API |
| inseeq_update_heartbeat | Agent liveness heartbeat |
| inseeq_get_agent_deployment | Deployment status and config |
| inseeq_get_agent_config | Per-org agent configuration overrides |
| inseeq_submit_learning_report | Daily learning report submission |
| inseeq_submit_optimization_recommendation | Article optimization recommendations |
| inseeq_send_review_notification | Email notifications for review cycles |
| inseeq_save_chat_message | Persist assistant text and artifact rows to the in-app chat UI |
Workflow Recipes
Common multi-step patterns for working with the inseeq MCP server.
Create and publish an article
1. inseeq_agent_whoami
→ get your organization_id
2. inseeq_create_article
→ { organization_id, title, content, meta_description, ... }
→ returns { article: { id } }
3. inseeq_update_article_status
→ { article_id, status: "in_review" }
→ triggers review emails to org members
4. (Wait for human approval — poll with inseeq_get_article)
5. inseeq_publish_article
→ { article_id }
→ publishes to configured CMSMonitor and respond to feedback
1. inseeq_poll_pending_comments
→ { organization_id }
→ returns unresolved threads (with replies + last_activity_at) grouped by article
→ use last_activity_at per thread to decide what's new since the previous poll
2. inseeq_get_article
→ { article_id }
→ returns metadata + markdown content by default
3. inseeq_add_comment
→ { article_id, content: "response...", parent_id: "original-comment-id" }
→ reply to the feedback
4. inseeq_resolve_comment
→ { article_id, comment_id }
→ mark the thread as resolved once addressedUpload and set the featured image
Three steps: get a presigned URL, PUT the binary, then attach the resulting public URL to the article via inseeq_set_featured_image. The upload tool returns both URLs in one call — no separate finalize step.
1. inseeq_create_image_upload
→ { content_id: <article_id>, content_type: "image/png" }
→ returns { upload_url, public_url, storage_path, token, content_type }
2. HTTP PUT to upload_url with raw binary body
(your MCP client handles this — no base64 through the LLM)
3. inseeq_set_featured_image
→ { article_id, featured_image_url: <public_url>, featured_image_alt_text: "..." }
→ image is now attached to the article. If a previous featured_image_url
existed, its storage object is auto-deleted.Schedule a calendar event
Events are direct one-shot creates — no review, no publish, no images. Useful when an agent wants to place a marker on the team's calendar (an upcoming podcast recording, a press release going out, a trade show).
1. inseeq_create_event
→ { organization_id, event_type: "podcast",
title: "AI on the Acme Show",
content: "Topics: agent workflows, ...",
scheduled_publish_date: "2025-04-20T03:00:00Z" }
→ returns { event: { id } }
2. (Event runs as planned)
3. inseeq_update_event_status
→ { event_id, status: "done" }
→ marks the event complete; calendar dims the cardSync context from an external source
// Full sync (replaces all writable entries):
1. inseeq_pull_context
→ { organization_id }
→ returns all entries as { path, content, access } pairs
2. Modify entries locally (add, update, remove)
3. inseeq_push_context
→ { organization_id, context: [{ path, content }, ...] }
→ returns { pushed, deleted, skipped }
⚠️ Entries NOT in your payload are deleted (except protected ones)
// Surgical update (modify specific entries only):
1. inseeq_save_context
→ { organization_id, section: "memory", entries: [{ key, content }] }
→ creates or updates without affecting other entriesinseeq MCP Server API Reference
Need help? Contact peter@inseeq.com