# Contentio API

> Draft, schedule, publish and analyse LinkedIn and X content in a Contentio account from your own app or AI agent. A REST API described by an OpenAPI 3.1 spec, OAuth 2.1 sign-in, and an MCP server over the same actions.

- REST base URL: https://contentio.io/api/v1
- OpenAPI spec: https://contentio.io/openapi.json
- MCP server: https://contentio.io/mcp (Streamable HTTP)
- Guide: https://contentio.io/developers

## Quickstart

### 1. Register your app

Register once with dynamic client registration (RFC 7591). Public clients use PKCE and no secret.

```
curl -X POST https://contentio.io/oauth/register \
  -H "Content-Type: application/json" \
  -d '{
    "client_name": "My app",
    "redirect_uris": ["https://myapp.example/callback"],
    "token_endpoint_auth_method": "none",
    "grant_types": ["authorization_code", "refresh_token"],
    "response_types": ["code"]
  }'
```

### 2. Send the user to sign in

Open the authorize URL with a PKCE S256 challenge. The user signs in to Contentio and approves your app.

```
https://contentio.io/oauth/authorize
  ?response_type=code
  &client_id=CLIENT_ID
  &redirect_uri=https://myapp.example/callback
  &code_challenge=CHALLENGE
  &code_challenge_method=S256
  &scope=email%20offline_access
  &state=STATE
```

### 3. Exchange the code for tokens

Your redirect URI receives code and state query parameters. Exchange the code using the PKCE verifier.

```
curl -X POST https://contentio.io/oauth/token \
  -d grant_type=authorization_code -d code=CODE \
  -d redirect_uri=https://myapp.example/callback \
  -d client_id=CLIENT_ID -d code_verifier=VERIFIER
```

### 4. Call an operation

Every operation is a POST with a JSON body, acting as the signed-in user.

```
curl -X POST https://contentio.io/api/v1/list_scheduled \
  -H "Authorization: Bearer ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{}'
```

## Operations

Every operation is `POST https://contentio.io/api/v1/{operation}` with a JSON body.

| Operation | What it does | Needs user confirmation |
| --- | --- | --- |
| `POST /api/v1/search_content` | Search your posts, tweets, articles, ideas and series. | No |
| `POST /api/v1/fetch_item` | Open one item in full by its id. | No |
| `POST /api/v1/list_scheduled` | List what is scheduled in a date range. | No |
| `POST /api/v1/get_profile_context` | Read your voice, niche, traits and connected accounts. | No |
| `POST /api/v1/get_analytics` | Read your growth, consistency, pillar and credit analytics. | No |
| `POST /api/v1/generate_post` | Write a LinkedIn post draft in your voice (uses AI credits). | No |
| `POST /api/v1/generate_tweet` | Write a single X post (tweet) draft in your voice (uses AI credits). | No |
| `POST /api/v1/create_draft` | Save a post or tweet draft you wrote together. | No |
| `POST /api/v1/update_draft` | Edit an existing draft. | No |
| `POST /api/v1/delete_draft` | Delete a draft. | Yes |
| `POST /api/v1/schedule_content` | Schedule a draft to publish to LinkedIn or X. | Yes |
| `POST /api/v1/unschedule_content` | Take a draft off the schedule. | No |
| `POST /api/v1/publish_now` | Publish a draft to LinkedIn or X right now. | Yes |
| `POST /api/v1/list_series` | List your content series and their progress. | No |
| `POST /api/v1/manage_series` | Create or update a content series and its topics. | No |

## Authentication

OAuth 2.1 authorization code flow with PKCE (S256) and dynamic client registration. Send `Authorization: Bearer <token>`. Tokens act as the user who approved your app.

Scopes:

- `email`: Identify the signed-in account (the default).
- `profile`: Read the account name and picture.
- `offline_access`: Receive a refresh token.

Discovery:

- [OpenAPI 3.1 spec](https://contentio.io/openapi.json): Every operation, with typed request and response schemas.
- [API index](https://contentio.io/api/v1): JSON list of operations.
- [Authorization server metadata](https://contentio.io/.well-known/oauth-authorization-server): RFC 8414: authorize, token and registration endpoints.
- [Protected resource metadata](https://contentio.io/.well-known/oauth-protected-resource/api/v1): RFC 9728: where to sign in and which scopes exist.
- [MCP server](https://contentio.io/mcp): The same actions as MCP tools for AI apps.
- [llms.txt](https://contentio.io/llms.txt): A guide to Contentio for language models.

## Errors

Every error is JSON with a stable `code`, a plain-language `message` and, when there is a known fix, a `hint`.

```json
{
  "error": {
    "code": "invalid_request",
    "message": "The request body does not match this operation's schema.",
    "hint": "See the operation's requestBody schema in https://contentio.io/openapi.json.",
    "docs": "https://contentio.io/developers#errors",
    "details": [{ "path": "type", "message": "Invalid option: expected one of \"post\"|\"tweet\"" }]
  }
}
```

| Status | Code | Meaning |
| --- | --- | --- |
| 400 | `invalid_json` | The body is not valid JSON. |
| 400 | `invalid_request` | The body does not match the schema; details lists each problem. |
| 401 | `unauthorized` | Missing, invalid or expired token. The WWW-Authenticate header says where to sign in. |
| 403 | `plan_required` | The account has no active paid plan. |
| 403 | `account_incomplete` | The account has not finished setup in Contentio. |
| 403 | `publishing_not_allowed` | Publish, schedule, delete or editing a scheduled draft, before the account owner allowed connected apps to do that. |
| 404 | `unknown_operation` | No operation at this path. |
| 404 | `not_found` | No API endpoint at this address. |
| 405 | `method_not_allowed` | Operations only accept POST. |
| 422 | `action_failed` | The action could not be completed; message explains why and what to do. |
| 429 | `rate_limited` | Too many requests; wait for Retry-After. |
| 500 | `internal_error` | Unexpected error on our side. |
| 503 | `temporarily_unavailable` | Try again shortly with backoff. |

## Plans, limits and versioning

- Every call acts as the signed-in user, inside their own account, and requires a paid Contentio plan (Plus, Premium or Creator).
- Generation operations use the account's normal AI credits and plan limits.
- Requests are rate limited per user, shared across the REST API and the MCP server: about 30 per minute on Plus and 60 per minute on Premium and Creator. A 429 carries Retry-After.
- Publish, schedule and delete affect the user's real LinkedIn and X accounts or remove their work. They answer 403 publishing_not_allowed until the account owner allows them in Settings > Connections > AI apps, and even then only call them after the user confirmed that exact action.
- Users see and disconnect connected apps in Settings > Connections; disconnecting revokes your tokens immediately.

v1 is stable: operations and fields are only ever added. Anything that would break an existing integration ships as /api/v2, announced in advance.
