{
  "openapi": "3.1.0",
  "info": {
    "title": "Contentio API",
    "version": "1.0.0",
    "summary": "Draft, schedule, publish and analyse LinkedIn and X content in a Contentio account.",
    "description": "The Contentio REST API exposes the same actions as the Contentio MCP server: search content, read the user's voice and analytics, generate posts and tweets in their voice, manage drafts and series, schedule and publish.\n\nEvery operation acts as the signed-in user, inside their own account, on a paid Contentio plan. Generation uses the account's normal AI credits.\n\nAuthentication: OAuth 2.1 authorization code flow with PKCE (dynamic client registration supported). Send the access token as `Authorization: Bearer <token>`.\n\nErrors use one JSON shape: `{\"error\": {\"code\", \"message\", \"hint\", \"docs\"}}`. Guide: https://contentio.io/developers",
    "contact": {
      "name": "Contentio support",
      "email": "support@contentio.io",
      "url": "https://contentio.io/support"
    },
    "termsOfService": "https://contentio.io/terms-of-service"
  },
  "externalDocs": {
    "description": "Developer guide",
    "url": "https://contentio.io/developers"
  },
  "servers": [
    {
      "url": "https://contentio.io/api/v1",
      "description": "Production"
    }
  ],
  "security": [
    {
      "oauth2": [
        "email"
      ]
    }
  ],
  "tags": [
    {
      "name": "content",
      "description": "Drafts, generation, search and publishing."
    },
    {
      "name": "scheduling",
      "description": "The publishing calendar."
    },
    {
      "name": "series",
      "description": "Multi-part content series."
    },
    {
      "name": "profile",
      "description": "The account's voice and connections."
    },
    {
      "name": "analytics",
      "description": "Growth and consistency analytics."
    }
  ],
  "paths": {
    "/get_analytics": {
      "post": {
        "operationId": "get_analytics",
        "summary": "Get content analytics",
        "description": "Returns a compact numeric summary of the user's own Contentio activity: the idea-to-published pipeline, posting streaks and active days, content pillar mix, AI credit usage week over week, weekly activity, profile score trend and community participation. Use it when the user asks how they are doing, whether they are consistent, what they write about most, how many credits they have burned, or when you need real numbers to ground a recommendation instead of guessing. Do NOT use it for LinkedIn or X engagement data: these are Contentio-side counts of the user's own drafts and actions, with no likes, impressions, followers or reach, and no per-post performance. It also returns no individual drafts or ids, so pair it with contentio_search_content when the user asks which specific post something refers to.",
        "tags": [
          "analytics"
        ],
        "x-contentio-permissions": [
          "analytics:read"
        ],
        "x-contentio-requires-confirmation": false,
        "x-contentio-annotations": {
          "readOnlyHint": true,
          "destructiveHint": false,
          "idempotentHint": true,
          "openWorldHint": false
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "area": {
                    "default": "overview",
                    "description": "\"overview\" returns everything; the others are cheaper single-topic reads.",
                    "type": "string",
                    "enum": [
                      "overview",
                      "funnel",
                      "consistency",
                      "pillars",
                      "credits"
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Success.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "area": {
                      "type": "string",
                      "enum": [
                        "overview",
                        "funnel",
                        "consistency",
                        "pillars",
                        "credits"
                      ]
                    },
                    "generated_at": {
                      "type": "string"
                    },
                    "pipeline": {
                      "type": "object",
                      "properties": {
                        "ideas": {
                          "type": "number"
                        },
                        "drafts": {
                          "type": "number"
                        },
                        "scheduled": {
                          "type": "number"
                        },
                        "published": {
                          "type": "number"
                        }
                      },
                      "required": [
                        "ideas",
                        "drafts",
                        "scheduled",
                        "published"
                      ],
                      "additionalProperties": false
                    },
                    "consistency": {
                      "type": "object",
                      "properties": {
                        "current_streak_days": {
                          "type": "number"
                        },
                        "longest_streak_days": {
                          "type": "number"
                        },
                        "active_days_this_week": {
                          "type": "number"
                        },
                        "active_days_this_month": {
                          "type": "number"
                        },
                        "missed_scheduled_posts": {
                          "type": "number"
                        },
                        "backlog_count": {
                          "type": "number"
                        }
                      },
                      "required": [
                        "current_streak_days",
                        "longest_streak_days",
                        "active_days_this_week",
                        "active_days_this_month",
                        "missed_scheduled_posts",
                        "backlog_count"
                      ],
                      "additionalProperties": false
                    },
                    "pillars": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "name": {
                            "type": "string"
                          },
                          "count": {
                            "type": "number"
                          },
                          "percentage": {
                            "type": "number"
                          }
                        },
                        "required": [
                          "name",
                          "count",
                          "percentage"
                        ],
                        "additionalProperties": false
                      }
                    },
                    "credits": {
                      "type": "object",
                      "properties": {
                        "this_week_total": {
                          "type": "number"
                        },
                        "last_week_total": {
                          "type": "number"
                        },
                        "change_percent": {
                          "anyOf": [
                            {
                              "type": "number"
                            },
                            {
                              "type": "null"
                            }
                          ]
                        },
                        "busiest_day": {
                          "anyOf": [
                            {
                              "type": "string"
                            },
                            {
                              "type": "null"
                            }
                          ]
                        }
                      },
                      "required": [
                        "this_week_total",
                        "last_week_total",
                        "change_percent",
                        "busiest_day"
                      ],
                      "additionalProperties": false
                    },
                    "activity": {
                      "type": "object",
                      "properties": {
                        "last_7_days_total": {
                          "type": "number"
                        },
                        "by_day": {
                          "type": "array",
                          "items": {
                            "type": "object",
                            "properties": {
                              "day": {
                                "type": "string"
                              },
                              "count": {
                                "type": "number"
                              }
                            },
                            "required": [
                              "day",
                              "count"
                            ],
                            "additionalProperties": false
                          }
                        }
                      },
                      "required": [
                        "last_7_days_total",
                        "by_day"
                      ],
                      "additionalProperties": false
                    },
                    "profile_score": {
                      "type": "object",
                      "properties": {
                        "score": {
                          "type": "number"
                        },
                        "trend": {
                          "type": "string"
                        }
                      },
                      "required": [
                        "score",
                        "trend"
                      ],
                      "additionalProperties": false
                    },
                    "forum": {
                      "type": "object",
                      "properties": {
                        "topics_created": {
                          "type": "number"
                        },
                        "replies_posted": {
                          "type": "number"
                        },
                        "votes_cast": {
                          "type": "number"
                        },
                        "total_contributions": {
                          "type": "number"
                        }
                      },
                      "required": [
                        "topics_created",
                        "replies_posted",
                        "votes_cast",
                        "total_contributions"
                      ],
                      "additionalProperties": false
                    },
                    "sources": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "source": {
                            "type": "string"
                          },
                          "target": {
                            "type": "string"
                          },
                          "value": {
                            "type": "number"
                          }
                        },
                        "required": [
                          "source",
                          "target",
                          "value"
                        ],
                        "additionalProperties": false
                      }
                    },
                    "notes": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    }
                  },
                  "required": [
                    "area",
                    "generated_at"
                  ],
                  "additionalProperties": false
                }
              }
            }
          },
          "400": {
            "description": "The body is not valid JSON (invalid_json) or does not match the schema (invalid_request).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing, invalid or expired access token (unauthorized).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The account has no active plan (plan_required), is not set up yet (account_incomplete), or has not allowed connected apps to publish, schedule or delete (publishing_not_allowed).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "The action could not be completed; the message says why and what to do (action_failed).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (rate_limited). Honour the Retry-After header.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Unexpected error (internal_error). Safe to retry reads.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Temporarily unavailable (temporarily_unavailable). Retry with backoff.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/create_draft": {
      "post": {
        "operationId": "create_draft",
        "summary": "Create a draft",
        "description": "Saves a brand-new draft into the user's Contentio library: a LinkedIn post draft when type is 'post', or an X (Twitter) draft when type is 'tweet'. Use it once the user has agreed on wording they want kept, or when they ask you to write something and save it for later; the draft is private and nothing is posted anywhere. Do NOT use it to change an existing draft (use contentio_update_draft), to put a draft on the calendar (use contentio_schedule_content), or to post immediately (use contentio_publish_now). Because Contentio always stores a tweet against a parent LinkedIn draft, creating a tweet without post_draft_id also creates that parent draft and returns it as linked_post_id, so tell the user both exist. It returns the new draft id and a short preview, not the full stored row, and it never generates or attaches images by itself.",
        "tags": [
          "content"
        ],
        "x-contentio-permissions": [
          "content:write"
        ],
        "x-contentio-requires-confirmation": false,
        "x-contentio-annotations": {
          "readOnlyHint": false,
          "destructiveHint": false,
          "idempotentHint": false,
          "openWorldHint": false
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "enum": [
                      "post",
                      "tweet"
                    ],
                    "description": "'post' creates a LinkedIn draft (post_drafts), 'tweet' creates an X draft (tweet_drafts)."
                  },
                  "title": {
                    "description": "Short internal title shown on the draft card. Posts only; derived from the first line when omitted.",
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 200
                  },
                  "body": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 3000,
                    "description": "The full draft text: the LinkedIn post body when type is 'post' (max 3000 characters), or the tweet text when type is 'tweet' (max 280 characters)."
                  },
                  "image_url": {
                    "description": "Optional https URL of an image already hosted by Contentio.",
                    "type": "string",
                    "format": "uri"
                  },
                  "post_draft_id": {
                    "description": "Tweets only: the parent LinkedIn draft this tweet belongs to. When omitted, a matching LinkedIn draft is created automatically because Contentio always stores a tweet against a parent draft.",
                    "type": "string",
                    "format": "uuid",
                    "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
                  }
                },
                "required": [
                  "type",
                  "body"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Success.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "type": {
                      "type": "string",
                      "enum": [
                        "post",
                        "tweet"
                      ]
                    },
                    "title": {
                      "type": "string"
                    },
                    "preview": {
                      "type": "string"
                    },
                    "linked_post_id": {
                      "anyOf": [
                        {
                          "type": "string"
                        },
                        {
                          "type": "null"
                        }
                      ]
                    }
                  },
                  "required": [
                    "id",
                    "type",
                    "title",
                    "preview",
                    "linked_post_id"
                  ],
                  "additionalProperties": false
                }
              }
            }
          },
          "400": {
            "description": "The body is not valid JSON (invalid_json) or does not match the schema (invalid_request).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing, invalid or expired access token (unauthorized).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The account has no active plan (plan_required), is not set up yet (account_incomplete), or has not allowed connected apps to publish, schedule or delete (publishing_not_allowed).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "The action could not be completed; the message says why and what to do (action_failed).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (rate_limited). Honour the Retry-After header.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Unexpected error (internal_error). Safe to retry reads.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Temporarily unavailable (temporarily_unavailable). Retry with backoff.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/delete_draft": {
      "post": {
        "operationId": "delete_draft",
        "summary": "Delete a draft",
        "description": "Permanently deletes a draft from the library and cancels any pending schedule for it, so it can never be auto-published later. Use it only when the user clearly asks for a draft to be thrown away; there is no undo and the text cannot be recovered afterwards. Do NOT use it to clear a schedule while keeping the draft (use contentio_unschedule_content), to tidy up drafts the user did not name, or as a way to \"replace\" a draft (edit it with contentio_update_draft instead). Deleting a LinkedIn post draft also removes the tweet drafts stored against it. It returns only the deleted id, so confirm the exact draft with the user before calling it.\n\nRequires the user's confirmation: this affects the user's real LinkedIn or X account, or removes their work. Only call it after the user explicitly approved this exact action.",
        "tags": [
          "content"
        ],
        "x-contentio-permissions": [
          "content:write"
        ],
        "x-contentio-requires-confirmation": true,
        "x-contentio-annotations": {
          "readOnlyHint": false,
          "destructiveHint": true,
          "idempotentHint": true,
          "openWorldHint": false
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "enum": [
                      "post",
                      "tweet"
                    ]
                  },
                  "id": {
                    "type": "string",
                    "format": "uuid",
                    "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
                    "description": "Id of the draft to delete permanently."
                  }
                },
                "required": [
                  "type",
                  "id"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Success.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "type": {
                      "type": "string",
                      "enum": [
                        "post",
                        "tweet"
                      ]
                    },
                    "deleted": {
                      "type": "boolean"
                    },
                    "cancelled_schedule": {
                      "type": "boolean"
                    }
                  },
                  "required": [
                    "id",
                    "type",
                    "deleted",
                    "cancelled_schedule"
                  ],
                  "additionalProperties": false
                }
              }
            }
          },
          "400": {
            "description": "The body is not valid JSON (invalid_json) or does not match the schema (invalid_request).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing, invalid or expired access token (unauthorized).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The account has no active plan (plan_required), is not set up yet (account_incomplete), or has not allowed connected apps to publish, schedule or delete (publishing_not_allowed).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "The action could not be completed; the message says why and what to do (action_failed).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (rate_limited). Honour the Retry-After header.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Unexpected error (internal_error). Safe to retry reads.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Temporarily unavailable (temporarily_unavailable). Retry with backoff.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/fetch_item": {
      "post": {
        "operationId": "fetch_item",
        "summary": "Fetch one content item",
        "description": "Loads one item from the user's library in full: the complete body text, image url(s), hashtags, schedule state, and for a series every topic with its per-platform completion state. Use it right after contentio_search_content whenever you need the actual words (to rewrite, critique, summarise, repurpose or answer a question about a specific draft), and always before editing so you work from the current text rather than a remembered version. Do NOT use it to browse or to guess ids: it fails on any id that does not belong to this user, and it cannot look up an item by title. Bodies longer than 6000 characters are truncated with a note, and it returns no analytics, no engagement numbers and no publishing history beyond the current schedule state.",
        "tags": [
          "content"
        ],
        "x-contentio-permissions": [
          "content:read"
        ],
        "x-contentio-requires-confirmation": false,
        "x-contentio-annotations": {
          "readOnlyHint": true,
          "destructiveHint": false,
          "idempotentHint": true,
          "openWorldHint": false
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "id": {
                    "type": "string",
                    "format": "uuid",
                    "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
                  },
                  "type": {
                    "type": "string",
                    "enum": [
                      "post",
                      "tweet",
                      "article",
                      "idea",
                      "series"
                    ],
                    "description": "Must match the \"type\" the search result reported for this id."
                  }
                },
                "required": [
                  "id",
                  "type"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Success.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "type": {
                      "type": "string",
                      "enum": [
                        "post",
                        "tweet",
                        "article",
                        "idea",
                        "series"
                      ]
                    },
                    "title": {
                      "type": "string"
                    },
                    "status": {
                      "type": "string"
                    },
                    "body": {
                      "anyOf": [
                        {
                          "type": "string"
                        },
                        {
                          "type": "null"
                        }
                      ]
                    },
                    "body_truncated": {
                      "type": "boolean"
                    },
                    "created_at": {
                      "anyOf": [
                        {
                          "type": "string"
                        },
                        {
                          "type": "null"
                        }
                      ]
                    },
                    "updated_at": {
                      "anyOf": [
                        {
                          "type": "string"
                        },
                        {
                          "type": "null"
                        }
                      ]
                    },
                    "scheduled_for": {
                      "anyOf": [
                        {
                          "type": "string"
                        },
                        {
                          "type": "null"
                        }
                      ]
                    },
                    "platform": {
                      "anyOf": [
                        {
                          "type": "string"
                        },
                        {
                          "type": "null"
                        }
                      ]
                    },
                    "image_url": {
                      "anyOf": [
                        {
                          "type": "string"
                        },
                        {
                          "type": "null"
                        }
                      ]
                    },
                    "image_urls": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    },
                    "hashtags": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    },
                    "tags": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    },
                    "links": {
                      "type": "object",
                      "properties": {
                        "post_draft_id": {
                          "anyOf": [
                            {
                              "type": "string"
                            },
                            {
                              "type": "null"
                            }
                          ]
                        },
                        "personal_idea_id": {
                          "anyOf": [
                            {
                              "type": "string"
                            },
                            {
                              "type": "null"
                            }
                          ]
                        },
                        "carousel_project_id": {
                          "anyOf": [
                            {
                              "type": "string"
                            },
                            {
                              "type": "null"
                            }
                          ]
                        }
                      },
                      "additionalProperties": false
                    },
                    "series": {
                      "type": "object",
                      "properties": {
                        "linkedin_enabled": {
                          "type": "boolean"
                        },
                        "twitter_enabled": {
                          "type": "boolean"
                        },
                        "is_active": {
                          "type": "boolean"
                        },
                        "completed_at": {
                          "anyOf": [
                            {
                              "type": "string"
                            },
                            {
                              "type": "null"
                            }
                          ]
                        },
                        "total_topics": {
                          "type": "number"
                        },
                        "completed_topics": {
                          "type": "number"
                        }
                      },
                      "required": [
                        "linkedin_enabled",
                        "twitter_enabled",
                        "is_active",
                        "completed_at",
                        "total_topics",
                        "completed_topics"
                      ],
                      "additionalProperties": false
                    },
                    "topics": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "order_index": {
                            "type": "number"
                          },
                          "title": {
                            "type": "string"
                          },
                          "description": {
                            "anyOf": [
                              {
                                "type": "string"
                              },
                              {
                                "type": "null"
                              }
                            ]
                          },
                          "status": {
                            "type": "string"
                          },
                          "linkedin_completed": {
                            "type": "boolean"
                          },
                          "twitter_completed": {
                            "type": "boolean"
                          },
                          "linkedin_post_id": {
                            "anyOf": [
                              {
                                "type": "string"
                              },
                              {
                                "type": "null"
                              }
                            ]
                          },
                          "twitter_post_id": {
                            "anyOf": [
                              {
                                "type": "string"
                              },
                              {
                                "type": "null"
                              }
                            ]
                          }
                        },
                        "required": [
                          "id",
                          "order_index",
                          "title",
                          "description",
                          "status",
                          "linkedin_completed",
                          "twitter_completed",
                          "linkedin_post_id",
                          "twitter_post_id"
                        ],
                        "additionalProperties": false
                      }
                    },
                    "untrusted_source": {
                      "type": "string"
                    },
                    "notes": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    }
                  },
                  "required": [
                    "id",
                    "type",
                    "title",
                    "status",
                    "body",
                    "body_truncated",
                    "created_at",
                    "updated_at",
                    "scheduled_for",
                    "platform"
                  ],
                  "additionalProperties": false
                }
              }
            }
          },
          "400": {
            "description": "The body is not valid JSON (invalid_json) or does not match the schema (invalid_request).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing, invalid or expired access token (unauthorized).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The account has no active plan (plan_required), is not set up yet (account_incomplete), or has not allowed connected apps to publish, schedule or delete (publishing_not_allowed).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "The action could not be completed; the message says why and what to do (action_failed).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (rate_limited). Honour the Retry-After header.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Unexpected error (internal_error). Safe to retry reads.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Temporarily unavailable (temporarily_unavailable). Retry with backoff.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/generate_post": {
      "post": {
        "operationId": "generate_post",
        "summary": "Write a LinkedIn post",
        "description": "Writes one LinkedIn post in the user's own trained voice, using their Voice Bucket, writing traits, tonality and keywords, and saves it to their drafts for review before anything is published. Use it whenever the user wants a new LinkedIn post, or asks you to turn an idea, a topic or a content pillar into a post. Do NOT use it for tweets or X posts, which need contentio_generate_tweet, and do NOT use it to edit or rewrite an existing draft. This tool never publishes and never schedules: it returns a draft id plus the text, and it consumes one post from the user's monthly plan allowance along with AI credits.",
        "tags": [
          "content"
        ],
        "x-contentio-permissions": [
          "content:write"
        ],
        "x-contentio-requires-confirmation": false,
        "x-contentio-annotations": {
          "readOnlyHint": false,
          "destructiveHint": false,
          "idempotentHint": false,
          "openWorldHint": false
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "topic": {
                    "type": "string",
                    "minLength": 3,
                    "maxLength": 2000,
                    "description": "What the post should be about, in the user's own words. A sentence or two works better than a single word."
                  },
                  "instructions": {
                    "description": "Extra steering for this one post: an angle to take, a story to include, something to avoid. Leave empty unless the user asked for something specific.",
                    "type": "string",
                    "maxLength": 2000
                  },
                  "pillar": {
                    "description": "The content pillar this post belongs to, when the user works with pillars. Stored on the draft.",
                    "type": "string",
                    "maxLength": 100
                  },
                  "save_as_draft": {
                    "default": true,
                    "description": "True (the default) saves the post to the user's drafts. Set false only when the user explicitly asked to see the text without saving it.",
                    "type": "boolean"
                  }
                },
                "required": [
                  "topic"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Success.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "draft_id": {
                      "anyOf": [
                        {
                          "type": "string"
                        },
                        {
                          "type": "null"
                        }
                      ]
                    },
                    "title": {
                      "type": "string"
                    },
                    "body": {
                      "type": "string"
                    },
                    "saved": {
                      "type": "boolean"
                    }
                  },
                  "required": [
                    "draft_id",
                    "title",
                    "body",
                    "saved"
                  ],
                  "additionalProperties": false
                }
              }
            }
          },
          "400": {
            "description": "The body is not valid JSON (invalid_json) or does not match the schema (invalid_request).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing, invalid or expired access token (unauthorized).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The account has no active plan (plan_required), is not set up yet (account_incomplete), or has not allowed connected apps to publish, schedule or delete (publishing_not_allowed).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "The action could not be completed; the message says why and what to do (action_failed).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (rate_limited). Honour the Retry-After header.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Unexpected error (internal_error). Safe to retry reads.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Temporarily unavailable (temporarily_unavailable). Retry with backoff.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/generate_tweet": {
      "post": {
        "operationId": "generate_tweet",
        "summary": "Write a tweet",
        "description": "Writes one tweet for X in the user's own trained voice and saves it to their tweet drafts. It works two ways: give a topic to write a fresh tweet, or give from_post_id to condense one of the user's existing LinkedIn post drafts into a tweet. Exactly one of the two is required, never both. Do NOT use it for LinkedIn posts, which need contentio_generate_post, and do NOT use it to post to X: this only produces a draft, which the user reviews and schedules themselves. In topic mode the topic is also saved to the user's ideas so the tweet has a home to hang off; each call consumes one AI action from the plan allowance along with AI credits.",
        "tags": [
          "content"
        ],
        "x-contentio-permissions": [
          "content:write"
        ],
        "x-contentio-requires-confirmation": false,
        "x-contentio-annotations": {
          "readOnlyHint": false,
          "destructiveHint": false,
          "idempotentHint": false,
          "openWorldHint": false
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "topic": {
                    "description": "What the tweet should be about. Use this OR from_post_id, never both.",
                    "type": "string",
                    "minLength": 3,
                    "maxLength": 2000
                  },
                  "from_post_id": {
                    "description": "The id of one of the user's existing LinkedIn post drafts, to turn that post into a tweet. Use this OR topic, never both.",
                    "type": "string",
                    "format": "uuid",
                    "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
                  },
                  "instructions": {
                    "description": "Extra steering for this one tweet: an angle, a hook, something to leave out.",
                    "type": "string",
                    "maxLength": 2000
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Success.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "draft_id": {
                      "type": "string"
                    },
                    "text": {
                      "type": "string"
                    },
                    "from_post_id": {
                      "anyOf": [
                        {
                          "type": "string"
                        },
                        {
                          "type": "null"
                        }
                      ]
                    }
                  },
                  "required": [
                    "draft_id",
                    "text",
                    "from_post_id"
                  ],
                  "additionalProperties": false
                }
              }
            }
          },
          "400": {
            "description": "The body is not valid JSON (invalid_json) or does not match the schema (invalid_request).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing, invalid or expired access token (unauthorized).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The account has no active plan (plan_required), is not set up yet (account_incomplete), or has not allowed connected apps to publish, schedule or delete (publishing_not_allowed).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "The action could not be completed; the message says why and what to do (action_failed).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (rate_limited). Honour the Retry-After header.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Unexpected error (internal_error). Safe to retry reads.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Temporarily unavailable (temporarily_unavailable). Retry with backoff.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/publish_now": {
      "post": {
        "operationId": "publish_now",
        "summary": "Publish now",
        "description": "Posts a draft publicly and immediately: a post draft goes live on the user's LinkedIn feed, a tweet draft goes live on their X account. This is irreversible from inside Contentio: once it is out, followers can see, screenshot and share it, and deleting it afterwards has to be done on the platform itself. The user will always be asked to approve this action before anything is sent, so state clearly which draft, which account and which wording is about to go out. Use it only when the user explicitly asks to publish right now; if they mention any time at all, even \"later today\", use contentio_schedule_content instead. Do NOT use it to preview or check a draft (read it back with contentio_search_content), and do NOT call it a second time after a success, which would post duplicate content. It returns the platform post id and a link when the platform gives one.\n\nRequires the user's confirmation: this affects the user's real LinkedIn or X account, or removes their work. Only call it after the user explicitly approved this exact action.",
        "tags": [
          "content"
        ],
        "x-contentio-permissions": [
          "content:publish"
        ],
        "x-contentio-requires-confirmation": true,
        "x-contentio-annotations": {
          "readOnlyHint": false,
          "destructiveHint": true,
          "idempotentHint": false,
          "openWorldHint": true
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "enum": [
                      "post",
                      "tweet"
                    ],
                    "description": "'post' publishes to LinkedIn, 'tweet' publishes to X."
                  },
                  "id": {
                    "type": "string",
                    "format": "uuid",
                    "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
                    "description": "Id of the draft to publish immediately."
                  }
                },
                "required": [
                  "type",
                  "id"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Success.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "platform": {
                      "type": "string",
                      "enum": [
                        "linkedin",
                        "x"
                      ]
                    },
                    "platform_post_id": {
                      "anyOf": [
                        {
                          "type": "string"
                        },
                        {
                          "type": "null"
                        }
                      ]
                    },
                    "url": {
                      "anyOf": [
                        {
                          "type": "string"
                        },
                        {
                          "type": "null"
                        }
                      ]
                    }
                  },
                  "required": [
                    "platform",
                    "platform_post_id",
                    "url"
                  ],
                  "additionalProperties": false
                }
              }
            }
          },
          "400": {
            "description": "The body is not valid JSON (invalid_json) or does not match the schema (invalid_request).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing, invalid or expired access token (unauthorized).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The account has no active plan (plan_required), is not set up yet (account_incomplete), or has not allowed connected apps to publish, schedule or delete (publishing_not_allowed).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "The action could not be completed; the message says why and what to do (action_failed).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (rate_limited). Honour the Retry-After header.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Unexpected error (internal_error). Safe to retry reads.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Temporarily unavailable (temporarily_unavailable). Retry with backoff.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/search_content": {
      "post": {
        "operationId": "search_content",
        "summary": "Search content",
        "description": "Searches the signed-in user's own Contentio library (LinkedIn post drafts, X/tweet drafts, generated articles, personal ideas and content series) with a case-insensitive partial match, newest-updated first. Use it whenever the user refers to something they already wrote (\"my post about pricing\", \"that idea from last week\") and you need its id before fetching, editing, scheduling or publishing it; call it with no query to list the most recent items. Do NOT use it to search the public web, news, trending posts or other users' content, and do not use it to read a whole draft: results are deliberately truncated to a ~80 character title and ~200 character preview. It does not return full bodies, images, hashtags, series topics or analytics; call contentio_fetch_item with the id and type for the complete object.",
        "tags": [
          "content"
        ],
        "x-contentio-permissions": [
          "content:read"
        ],
        "x-contentio-requires-confirmation": false,
        "x-contentio-annotations": {
          "readOnlyHint": true,
          "destructiveHint": false,
          "idempotentHint": true,
          "openWorldHint": false
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "query": {
                    "description": "Keywords to match. Omit to list the most recently updated items.",
                    "type": "string",
                    "maxLength": 200
                  },
                  "type": {
                    "default": "all",
                    "description": "\"post\" = LinkedIn draft, \"tweet\" = X draft, \"all\" searches every type.",
                    "type": "string",
                    "enum": [
                      "post",
                      "tweet",
                      "article",
                      "idea",
                      "series",
                      "all"
                    ]
                  },
                  "status": {
                    "description": "Publish state filter. Articles and ideas only ever match \"draft\"; series only appear when this is \"any\" or omitted.",
                    "type": "string",
                    "enum": [
                      "draft",
                      "scheduled",
                      "published",
                      "any"
                    ]
                  },
                  "limit": {
                    "default": 20,
                    "type": "integer",
                    "minimum": 1,
                    "maximum": 50
                  },
                  "cursor": {
                    "description": "Pass back next_cursor from a previous call.",
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Success.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "items": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "type": {
                            "type": "string",
                            "enum": [
                              "post",
                              "tweet",
                              "article",
                              "idea",
                              "series"
                            ]
                          },
                          "title": {
                            "type": "string"
                          },
                          "preview": {
                            "anyOf": [
                              {
                                "type": "string"
                              },
                              {
                                "type": "null"
                              }
                            ]
                          },
                          "status": {
                            "type": "string"
                          },
                          "scheduled_at": {
                            "anyOf": [
                              {
                                "type": "string"
                              },
                              {
                                "type": "null"
                              }
                            ]
                          },
                          "updated_at": {
                            "type": "string"
                          },
                          "external_source": {
                            "type": "boolean"
                          }
                        },
                        "required": [
                          "id",
                          "type",
                          "title",
                          "preview",
                          "status",
                          "updated_at"
                        ],
                        "additionalProperties": false
                      }
                    },
                    "has_more": {
                      "type": "boolean"
                    },
                    "next_cursor": {
                      "type": "string"
                    },
                    "total_hint": {
                      "type": "number"
                    },
                    "external_previews": {
                      "type": "string"
                    },
                    "notes": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    }
                  },
                  "required": [
                    "items",
                    "has_more"
                  ],
                  "additionalProperties": false
                }
              }
            }
          },
          "400": {
            "description": "The body is not valid JSON (invalid_json) or does not match the schema (invalid_request).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing, invalid or expired access token (unauthorized).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The account has no active plan (plan_required), is not set up yet (account_incomplete), or has not allowed connected apps to publish, schedule or delete (publishing_not_allowed).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "The action could not be completed; the message says why and what to do (action_failed).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (rate_limited). Honour the Retry-After header.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Unexpected error (internal_error). Safe to retry reads.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Temporarily unavailable (temporarily_unavailable). Retry with backoff.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/update_draft": {
      "post": {
        "operationId": "update_draft",
        "summary": "Update a draft",
        "description": "Edits an existing draft in place: replaces its text, retitles it, or swaps/removes its image. Use it when the user asks for a rewrite, a tweak or a fix to something already saved, and pass only the fields that change; anything you omit is left exactly as it is. Do NOT use it to create something new (use contentio_create_draft), to change scheduling (use contentio_schedule_content or contentio_unschedule_content), or to publish. Passing body REPLACES the entire text rather than appending to it, so send the full final wording, and note that tweets have no title field and are capped at 280 characters. It returns the id and a short preview of the saved result, not the whole row.",
        "tags": [
          "content"
        ],
        "x-contentio-permissions": [
          "content:write"
        ],
        "x-contentio-requires-confirmation": false,
        "x-contentio-annotations": {
          "readOnlyHint": false,
          "destructiveHint": false,
          "idempotentHint": true,
          "openWorldHint": false
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "enum": [
                      "post",
                      "tweet"
                    ]
                  },
                  "id": {
                    "type": "string",
                    "format": "uuid",
                    "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
                    "description": "Id of the draft to change."
                  },
                  "title": {
                    "description": "New internal title. Posts only: tweets have no title.",
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 200
                  },
                  "body": {
                    "description": "Replacement text for the whole draft. This overwrites the existing text.",
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 3000
                  },
                  "image_url": {
                    "description": "New image URL, or null to remove the image from the draft.",
                    "anyOf": [
                      {
                        "type": "string",
                        "format": "uri"
                      },
                      {
                        "type": "null"
                      }
                    ]
                  }
                },
                "required": [
                  "type",
                  "id"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Success.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "type": {
                      "type": "string",
                      "enum": [
                        "post",
                        "tweet"
                      ]
                    },
                    "updated_fields": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    },
                    "title": {
                      "anyOf": [
                        {
                          "type": "string"
                        },
                        {
                          "type": "null"
                        }
                      ]
                    },
                    "preview": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "id",
                    "type",
                    "updated_fields",
                    "title",
                    "preview"
                  ],
                  "additionalProperties": false
                }
              }
            }
          },
          "400": {
            "description": "The body is not valid JSON (invalid_json) or does not match the schema (invalid_request).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing, invalid or expired access token (unauthorized).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The account has no active plan (plan_required), is not set up yet (account_incomplete), or has not allowed connected apps to publish, schedule or delete (publishing_not_allowed).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "The action could not be completed; the message says why and what to do (action_failed).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (rate_limited). Honour the Retry-After header.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Unexpected error (internal_error). Safe to retry reads.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Temporarily unavailable (temporarily_unavailable). Retry with backoff.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/get_profile_context": {
      "post": {
        "operationId": "get_profile_context",
        "summary": "Get writing profile context",
        "description": "Returns everything about the signed-in user that should shape generated content in one call: their subscription plan, niche and professional title, their timezone (for resolving scheduling times), the personality traits and tonalities they picked, their custom hashtags, whether LinkedIn and X are actually connected for publishing, and a summary of their trained voice profile. Call this BEFORE writing or rewriting any post, tweet, article or series for the user, and before promising to schedule or publish so you know which platform is really connected. Do NOT call it repeatedly inside one conversation: the answer is stable, so fetch it once and reuse it. It returns no drafts, no analytics, no credit balance, no email address and no access tokens, and an empty voice summary simply means the user has not trained their voice yet.",
        "tags": [
          "profile"
        ],
        "x-contentio-permissions": [
          "profile:read"
        ],
        "x-contentio-requires-confirmation": false,
        "x-contentio-annotations": {
          "readOnlyHint": true,
          "destructiveHint": false,
          "idempotentHint": true,
          "openWorldHint": false
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {}
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Success.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "plan": {
                      "type": "string"
                    },
                    "niche": {
                      "type": "object",
                      "properties": {
                        "category": {
                          "anyOf": [
                            {
                              "type": "string"
                            },
                            {
                              "type": "null"
                            }
                          ]
                        },
                        "custom_niche": {
                          "anyOf": [
                            {
                              "type": "string"
                            },
                            {
                              "type": "null"
                            }
                          ]
                        },
                        "professional_title": {
                          "anyOf": [
                            {
                              "type": "string"
                            },
                            {
                              "type": "null"
                            }
                          ]
                        }
                      },
                      "required": [
                        "category",
                        "custom_niche",
                        "professional_title"
                      ],
                      "additionalProperties": false
                    },
                    "timezone": {
                      "anyOf": [
                        {
                          "type": "string"
                        },
                        {
                          "type": "null"
                        }
                      ]
                    },
                    "traits": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    },
                    "tonalities": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    },
                    "custom_hashtags": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    },
                    "connections": {
                      "type": "object",
                      "properties": {
                        "linkedin": {
                          "type": "boolean"
                        },
                        "x": {
                          "type": "boolean"
                        }
                      },
                      "required": [
                        "linkedin",
                        "x"
                      ],
                      "additionalProperties": false
                    },
                    "voice": {
                      "type": "object",
                      "properties": {
                        "has_bucket": {
                          "type": "boolean"
                        },
                        "version": {
                          "anyOf": [
                            {
                              "type": "number"
                            },
                            {
                              "type": "null"
                            }
                          ]
                        },
                        "updated_at": {
                          "anyOf": [
                            {
                              "type": "string"
                            },
                            {
                              "type": "null"
                            }
                          ]
                        },
                        "summary": {
                          "anyOf": [
                            {
                              "type": "string"
                            },
                            {
                              "type": "null"
                            }
                          ]
                        },
                        "summary_truncated": {
                          "type": "boolean"
                        }
                      },
                      "required": [
                        "has_bucket",
                        "version",
                        "updated_at",
                        "summary",
                        "summary_truncated"
                      ],
                      "additionalProperties": false
                    },
                    "training_completed": {
                      "type": "boolean"
                    },
                    "notes": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    }
                  },
                  "required": [
                    "plan",
                    "niche",
                    "timezone",
                    "traits",
                    "tonalities",
                    "custom_hashtags",
                    "connections",
                    "voice",
                    "training_completed"
                  ],
                  "additionalProperties": false
                }
              }
            }
          },
          "400": {
            "description": "The body is not valid JSON (invalid_json) or does not match the schema (invalid_request).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing, invalid or expired access token (unauthorized).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The account has no active plan (plan_required), is not set up yet (account_incomplete), or has not allowed connected apps to publish, schedule or delete (publishing_not_allowed).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "The action could not be completed; the message says why and what to do (action_failed).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (rate_limited). Honour the Retry-After header.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Unexpected error (internal_error). Safe to retry reads.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Temporarily unavailable (temporarily_unavailable). Retry with backoff.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/list_scheduled": {
      "post": {
        "operationId": "list_scheduled",
        "summary": "List the scheduled queue",
        "description": "Lists every LinkedIn post and X post the user has scheduled inside a date window, merged into one timeline sorted earliest first, with the real delivery status taken from the publishing queue (pending, claimed, publishing, completed or failed). Use it to answer \"what is going out this week\", to spot empty days before proposing new content, and to check whether a scheduled item actually published or failed. Do NOT use it to find drafts (unscheduled work never appears here) or to read a full post: each slot carries only a short title and preview. It returns no engagement or reach metrics, no articles, ideas or series, and no history older than the window you ask for; the default window is today through 14 days ahead and the maximum span is 180 days.",
        "tags": [
          "scheduling"
        ],
        "x-contentio-permissions": [
          "content:read"
        ],
        "x-contentio-requires-confirmation": false,
        "x-contentio-annotations": {
          "readOnlyHint": true,
          "destructiveHint": false,
          "idempotentHint": true,
          "openWorldHint": false
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "from": {
                    "description": "Window start. Defaults to the start of today (UTC).",
                    "type": "string"
                  },
                  "to": {
                    "description": "Window end. Defaults to 14 days after \"from\".",
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Success.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "slots": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "id": {
                            "type": "string"
                          },
                          "type": {
                            "type": "string",
                            "enum": [
                              "post",
                              "tweet"
                            ]
                          },
                          "platform": {
                            "type": "string"
                          },
                          "title": {
                            "type": "string"
                          },
                          "preview": {
                            "type": "string"
                          },
                          "scheduled_for": {
                            "type": "string"
                          },
                          "status": {
                            "type": "string"
                          }
                        },
                        "required": [
                          "id",
                          "type",
                          "platform",
                          "title",
                          "preview",
                          "scheduled_for",
                          "status"
                        ],
                        "additionalProperties": false
                      }
                    },
                    "count": {
                      "type": "number"
                    },
                    "from": {
                      "type": "string"
                    },
                    "to": {
                      "type": "string"
                    },
                    "notes": {
                      "type": "array",
                      "items": {
                        "type": "string"
                      }
                    }
                  },
                  "required": [
                    "slots",
                    "count",
                    "from",
                    "to"
                  ],
                  "additionalProperties": false
                }
              }
            }
          },
          "400": {
            "description": "The body is not valid JSON (invalid_json) or does not match the schema (invalid_request).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing, invalid or expired access token (unauthorized).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The account has no active plan (plan_required), is not set up yet (account_incomplete), or has not allowed connected apps to publish, schedule or delete (publishing_not_allowed).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "The action could not be completed; the message says why and what to do (action_failed).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (rate_limited). Honour the Retry-After header.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Unexpected error (internal_error). Safe to retry reads.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Temporarily unavailable (temporarily_unavailable). Retry with backoff.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/schedule_content": {
      "post": {
        "operationId": "schedule_content",
        "summary": "Schedule a draft",
        "description": "Puts an existing draft on the publishing calendar so Contentio's worker posts it automatically at the chosen time: LinkedIn for posts, X for tweets. Use it when the user names a time or agrees to one; scheduled_for must be a full ISO 8601 timestamp with a timezone (for example 2026-09-01T14:30:00Z) and must be in the future, and the matching account must already be connected. Calling it again on the same draft moves the existing schedule rather than creating a second one, so use it to reschedule too. Do NOT use it to post right now (use contentio_publish_now), to remove a schedule (use contentio_unschedule_content), or to edit the text before scheduling (use contentio_update_draft first). It returns the confirmed time in UTC; nothing is published at the moment of the call.\n\nRequires the user's confirmation: this affects the user's real LinkedIn or X account, or removes their work. Only call it after the user explicitly approved this exact action.",
        "tags": [
          "scheduling"
        ],
        "x-contentio-permissions": [
          "content:write"
        ],
        "x-contentio-requires-confirmation": true,
        "x-contentio-annotations": {
          "readOnlyHint": false,
          "destructiveHint": false,
          "idempotentHint": true,
          "openWorldHint": true
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "enum": [
                      "post",
                      "tweet"
                    ],
                    "description": "'post' schedules to LinkedIn, 'tweet' schedules to X."
                  },
                  "id": {
                    "type": "string",
                    "format": "uuid",
                    "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
                    "description": "Id of the draft to schedule."
                  },
                  "scheduled_for": {
                    "type": "string",
                    "description": "When to publish, as a full ISO 8601 timestamp with timezone (for example 2026-09-01T14:30:00Z). Must be in the future."
                  }
                },
                "required": [
                  "type",
                  "id",
                  "scheduled_for"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Success.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "type": {
                      "type": "string",
                      "enum": [
                        "post",
                        "tweet"
                      ]
                    },
                    "platform": {
                      "type": "string",
                      "enum": [
                        "linkedin",
                        "x"
                      ]
                    },
                    "scheduled_for": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "id",
                    "type",
                    "platform",
                    "scheduled_for"
                  ],
                  "additionalProperties": false
                }
              }
            }
          },
          "400": {
            "description": "The body is not valid JSON (invalid_json) or does not match the schema (invalid_request).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing, invalid or expired access token (unauthorized).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The account has no active plan (plan_required), is not set up yet (account_incomplete), or has not allowed connected apps to publish, schedule or delete (publishing_not_allowed).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "The action could not be completed; the message says why and what to do (action_failed).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (rate_limited). Honour the Retry-After header.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Unexpected error (internal_error). Safe to retry reads.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Temporarily unavailable (temporarily_unavailable). Retry with backoff.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/unschedule_content": {
      "post": {
        "operationId": "unschedule_content",
        "summary": "Clear a schedule",
        "description": "Takes a draft off the publishing calendar: it clears the scheduled time and cancels the pending job, so the worker will not post it. The draft itself, its text and its images are all kept, ready to be rescheduled later with contentio_schedule_content. Use it when the user wants to hold something back, pause a plan, or fix wording before it goes out. Do NOT use it to delete the draft (use contentio_delete_draft) or to move a schedule to a different time, which contentio_schedule_content already does in one call. It is safe to call on a draft that is not scheduled: nothing changes and no error is raised, and it cannot recall anything that has already been published.",
        "tags": [
          "scheduling"
        ],
        "x-contentio-permissions": [
          "content:write"
        ],
        "x-contentio-requires-confirmation": false,
        "x-contentio-annotations": {
          "readOnlyHint": false,
          "destructiveHint": false,
          "idempotentHint": true,
          "openWorldHint": false
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "type": {
                    "type": "string",
                    "enum": [
                      "post",
                      "tweet"
                    ]
                  },
                  "id": {
                    "type": "string",
                    "format": "uuid",
                    "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
                    "description": "Id of the draft whose schedule should be cleared."
                  }
                },
                "required": [
                  "type",
                  "id"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Success.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "id": {
                      "type": "string"
                    },
                    "type": {
                      "type": "string",
                      "enum": [
                        "post",
                        "tweet"
                      ]
                    },
                    "platform": {
                      "type": "string",
                      "enum": [
                        "linkedin",
                        "x"
                      ]
                    },
                    "scheduled": {
                      "type": "boolean",
                      "const": false
                    }
                  },
                  "required": [
                    "id",
                    "type",
                    "platform",
                    "scheduled"
                  ],
                  "additionalProperties": false
                }
              }
            }
          },
          "400": {
            "description": "The body is not valid JSON (invalid_json) or does not match the schema (invalid_request).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing, invalid or expired access token (unauthorized).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The account has no active plan (plan_required), is not set up yet (account_incomplete), or has not allowed connected apps to publish, schedule or delete (publishing_not_allowed).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "The action could not be completed; the message says why and what to do (action_failed).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (rate_limited). Honour the Retry-After header.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Unexpected error (internal_error). Safe to retry reads.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Temporarily unavailable (temporarily_unavailable). Retry with backoff.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/list_series": {
      "post": {
        "operationId": "list_series",
        "summary": "List content series",
        "description": "Lists the user's content series with how far each one has progressed: how many topics it holds, how many are done, how many are still waiting, and which topic comes next. Use it when the user asks what series they are running, how a series is going, or before creating a series so you can tell them whether they are at the limit. It reads only and changes nothing, so it never needs approval. It does NOT return the full text of the posts written from a series; use the content search tool for that.",
        "tags": [
          "series"
        ],
        "x-contentio-permissions": [
          "content:read"
        ],
        "x-contentio-requires-confirmation": false,
        "x-contentio-annotations": {
          "readOnlyHint": true,
          "destructiveHint": false,
          "idempotentHint": true,
          "openWorldHint": false
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "include_inactive": {
                    "default": false,
                    "description": "True also returns series the user has deactivated. Default false returns only the active ones.",
                    "type": "boolean"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Success.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "series": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "series_id": {
                            "type": "string"
                          },
                          "name": {
                            "type": "string"
                          },
                          "description": {
                            "anyOf": [
                              {
                                "type": "string"
                              },
                              {
                                "type": "null"
                              }
                            ]
                          },
                          "is_active": {
                            "type": "boolean"
                          },
                          "topics_total": {
                            "type": "number"
                          },
                          "topics_completed": {
                            "type": "number"
                          },
                          "topics_pending": {
                            "type": "number"
                          },
                          "progress_percent": {
                            "type": "number"
                          },
                          "next_topic": {
                            "anyOf": [
                              {
                                "type": "string"
                              },
                              {
                                "type": "null"
                              }
                            ]
                          }
                        },
                        "required": [
                          "series_id",
                          "name",
                          "description",
                          "is_active",
                          "topics_total",
                          "topics_completed",
                          "topics_pending",
                          "progress_percent",
                          "next_topic"
                        ],
                        "additionalProperties": false
                      }
                    },
                    "active_count": {
                      "type": "number"
                    },
                    "max_active": {
                      "type": "number"
                    }
                  },
                  "required": [
                    "series",
                    "active_count",
                    "max_active"
                  ],
                  "additionalProperties": false
                }
              }
            }
          },
          "400": {
            "description": "The body is not valid JSON (invalid_json) or does not match the schema (invalid_request).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing, invalid or expired access token (unauthorized).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The account has no active plan (plan_required), is not set up yet (account_incomplete), or has not allowed connected apps to publish, schedule or delete (publishing_not_allowed).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "The action could not be completed; the message says why and what to do (action_failed).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (rate_limited). Honour the Retry-After header.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Unexpected error (internal_error). Safe to retry reads.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Temporarily unavailable (temporarily_unavailable). Retry with backoff.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/manage_series": {
      "post": {
        "operationId": "manage_series",
        "summary": "Create a series or fill it with topics",
        "description": "Creates a content series, or generates the topics that fill one. Use operation \"create\" with a name and description to start a series, then operation \"generate_topics\" with that series id to have the AI plan the topic arc in the user's voice and niche. A user may keep 6 active series at once and a series holds at most 21 topics; a series needs at least 3 topics before it is worth running, so create is normally followed straight away by generate_topics. Generating topics ADDS to whatever the series already holds and never overwrites or deletes existing topics, so ask the user first if they wanted the old ones replaced, because this tool cannot do that. To see existing series or their progress, use contentio_list_series instead; this tool does not write posts, only the plan they come from.",
        "tags": [
          "series"
        ],
        "x-contentio-permissions": [
          "content:write"
        ],
        "x-contentio-requires-confirmation": false,
        "x-contentio-annotations": {
          "readOnlyHint": false,
          "destructiveHint": false,
          "idempotentHint": false,
          "openWorldHint": false
        },
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "operation": {
                    "type": "string",
                    "enum": [
                      "create",
                      "generate_topics"
                    ],
                    "description": "create makes a new empty series. generate_topics fills an existing series with AI-written topics."
                  },
                  "name": {
                    "description": "Required for create: the name of the series, as the user would say it.",
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 200
                  },
                  "description": {
                    "description": "Optional for create: what the series is about and who it is for. It makes the generated topics noticeably better.",
                    "type": "string",
                    "maxLength": 2000
                  },
                  "series_id": {
                    "description": "Required for generate_topics: the id of the series to fill, from contentio_list_series.",
                    "type": "string",
                    "format": "uuid",
                    "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
                  },
                  "topic_count": {
                    "description": "How many topics to generate, up to 21. Defaults to 14 when the user has no preference.",
                    "type": "integer",
                    "minimum": 1,
                    "maximum": 21
                  }
                },
                "required": [
                  "operation"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Success.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "operation": {
                      "type": "string",
                      "enum": [
                        "create",
                        "generate_topics"
                      ]
                    },
                    "series_id": {
                      "type": "string"
                    },
                    "name": {
                      "type": "string"
                    },
                    "topics_added": {
                      "type": "number"
                    },
                    "topics_total": {
                      "type": "number"
                    },
                    "topics": {
                      "type": "array",
                      "items": {
                        "type": "object",
                        "properties": {
                          "order": {
                            "type": "number"
                          },
                          "title": {
                            "type": "string"
                          },
                          "description": {
                            "anyOf": [
                              {
                                "type": "string"
                              },
                              {
                                "type": "null"
                              }
                            ]
                          }
                        },
                        "required": [
                          "order",
                          "title",
                          "description"
                        ],
                        "additionalProperties": false
                      }
                    }
                  },
                  "required": [
                    "operation",
                    "series_id",
                    "name",
                    "topics_added",
                    "topics_total",
                    "topics"
                  ],
                  "additionalProperties": false
                }
              }
            }
          },
          "400": {
            "description": "The body is not valid JSON (invalid_json) or does not match the schema (invalid_request).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "Missing, invalid or expired access token (unauthorized).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "The account has no active plan (plan_required), is not set up yet (account_incomplete), or has not allowed connected apps to publish, schedule or delete (publishing_not_allowed).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "The action could not be completed; the message says why and what to do (action_failed).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "Too many requests (rate_limited). Honour the Retry-After header.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "Unexpected error (internal_error). Safe to retry reads.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "503": {
            "description": "Temporarily unavailable (temporarily_unavailable). Retry with backoff.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "oauth2": {
        "type": "oauth2",
        "description": "OAuth 2.1 with PKCE (S256). Authorization server metadata: https://contentio.io/.well-known/oauth-authorization-server. Dynamic client registration: https://contentio.io/oauth/register.",
        "flows": {
          "authorizationCode": {
            "authorizationUrl": "https://contentio.io/oauth/authorize",
            "tokenUrl": "https://contentio.io/oauth/token",
            "refreshUrl": "https://contentio.io/oauth/token",
            "scopes": {
              "email": "Identify the signed-in Contentio account (default).",
              "profile": "Read the account name and picture.",
              "offline_access": "Receive a refresh token so access survives restarts."
            }
          }
        }
      }
    },
    "schemas": {
      "Error": {
        "type": "object",
        "required": [
          "error"
        ],
        "properties": {
          "error": {
            "type": "object",
            "required": [
              "code",
              "message",
              "docs"
            ],
            "properties": {
              "code": {
                "type": "string",
                "description": "Stable, machine-readable error code.",
                "enum": [
                  "invalid_json",
                  "invalid_request",
                  "unauthorized",
                  "plan_required",
                  "account_incomplete",
                  "publishing_not_allowed",
                  "not_found",
                  "unknown_operation",
                  "method_not_allowed",
                  "action_failed",
                  "rate_limited",
                  "temporarily_unavailable",
                  "internal_error"
                ]
              },
              "message": {
                "type": "string",
                "description": "What went wrong, in plain language."
              },
              "hint": {
                "type": "string",
                "description": "How to fix it, when there is a known fix."
              },
              "docs": {
                "type": "string",
                "format": "uri"
              },
              "details": {
                "description": "Extra context, e.g. schema validation issues."
              }
            }
          }
        }
      }
    }
  }
}