# Ampost API and MCP Reference This is the canonical LLM-readable documentation for Ampost. It is served at `/llm.txt`. Ampost is a unified social media publishing API for developers, SaaS tools, and AI agents. It supports creating draft and scheduled posts, publishing through platform brokers, listing post state, and exposing an MCP server for agent workflows. ## Service Metadata - Base URL: `https://api.ampost.io` - Dashboard/UI host: `https://ampost.io` - REST API prefix: `/api/v1` - MCP endpoint: `https://api.ampost.io/mcp` - Public API paths on `https://ampost.io` continue to respond temporarily for backwards compatibility, but new integrations should use `https://api.ampost.io`. - Auth for public REST and MCP: `Authorization: Bearer amp_live_...` - Content type: `application/json` - Support: `support@ampost.io` ## Supported Platforms Only these platforms are currently supported by working brokers: | Platform | Key | Text-only | Scheduling | Media support | |---|---|---|---:|---:|---| | Instagram | `instagram` | No | Yes | images, video | | Facebook | `facebook` | Yes | Yes | images, video | | Twitter/X | `twitter` | Yes | No | images, video, gif | | LinkedIn | `linkedin` | Yes | No | images, article link thumbnail, one video | | TikTok | `tiktok` | No | No | image, video | | YouTube | `youtube` | No | Yes* | explicit video | | Threads | `threads` | Yes | No | images, video | Launch-state caveats: - LinkedIn is member-profile publishing only. Organization/Page posting, analytics, comments, and read-heavy Community Management surfaces are not part of the public launch. - Bluesky remains planned and is not a working publish target yet. `*` YouTube scheduling support is real, but it has two layers: - Ampost-level `scheduledFor` support is available. - YouTube-level `platformOverrides.youtube.settings.publishAt` is also available, but it requires `privacyStatus: "private"` and cannot be combined with `scheduledFor`. - Public or unlisted visibility may still be forced back to private while the Google project is unverified or audit review is still pending. ## Important Semantics - `POST /api/v1/posts` creates a `draft` when `scheduledFor` is `null`. - `POST /api/v1/posts` creates a `scheduled` post when `scheduledFor` is a future ISO 8601 timestamp. - REST create does not publish immediately. Publishing is handled by scheduled processing or MCP `posts_publish`. - Connections are resolved automatically from the platform set. Each platform in the set must have an active connection. - `platformSetId` is required. Every post must be scoped to a platform set. - TikTok and YouTube require explicit typed media objects. Do not rely on legacy bare `mediaUrls` for either platform. - TikTok creator visibility is audit-sensitive: if the connected TikTok app is still limited to private-only posting during app review or Direct Post approval, treat that as the real launch posture. - LinkedIn video requires explicit typed media objects and LinkedIn image posts are safest with explicit media metadata as well. - LinkedIn scheduling is not supported. Organization/Page posting fields exist for future gating but are rejected in the public launch surface. - YouTube requires exactly one video, rejects image-only and mixed-media posts, and needs a title through `content.text` or `platformOverrides.youtube.settings.title`. - YouTube launch state is audit-sensitive: Google documents that uploads from unverified projects created after July 28, 2020 remain private until audit completes. ## Authentication and API Keys API key management is dashboard-only and uses Supabase cookie auth: | Method | Path | Description | |---|---|---| | `GET` | `/api/keys` | List the current user's API keys | | `POST` | `/api/keys` | Create an API key; body `{ "name": "My Key" }`; returns `rawKey` once | | `DELETE` | `/api/keys/:id` | Revoke an API key | API keys are stored hashed at rest. Any valid API key can access the public REST API and MCP tools your plan allows. Ampost API keys are scope-free: the key identifies the owning user, and the endpoint decides whether Bearer-key access is allowed. OAuth connection flows remain outside the public REST and MCP surface: auth URL and callback routes are browser/dashboard entrypoints, while refresh, page selection, and disconnect routes use dashboard session auth rather than Bearer API keys. ## REST API ### List Posts `GET /api/v1/posts` Auth: valid API key Rate limit operation: `apiRead` Query parameters: | Name | Type | Description | |---|---|---| | `status` | string | Optional post status filter | | `platform` | string | Optional supported platform key | | `search` | string | Optional text search over post content | | `limit` | integer | Optional, default 20, max 100 | | `offset` | integer | Optional, default 0 | Example: ```bash curl "https://api.ampost.io/api/v1/posts?platform=threads&limit=10" \ -H "Authorization: Bearer $AMPOST_API_KEY" ``` ### Create Draft or Scheduled Post `POST /api/v1/posts` Auth: valid API key Rate limit operation: `createPost` Request body: ```json { "content": { "text": "Hello from Ampost!", "media": [], "linkUrl": null, "platformOverrides": {} }, "platforms": ["threads"], "scheduledFor": null, "platformSetId": "set_uuid" } ``` Response: `201` with `{ "post": Post }`. Use `scheduledFor: "2026-06-01T09:00:00Z"` to schedule a supported scheduling platform such as `facebook`, `instagram`, or `youtube`. When targeting `tiktok` or `youtube`, send explicit `content.media` objects with `type`, `mimeType`, `sizeBytes`, and video `durationSeconds` when relevant. LinkedIn video also requires explicit `content.media`, and LinkedIn image posts should prefer it so Ampost can preserve accessibility metadata. Bare legacy URL-only media lists are rejected for TikTok, YouTube, and LinkedIn video. TikTok posts cannot be text-only or scheduled, and TikTok visibility may remain private-only until app review or Direct Post approval allows broader posting. LinkedIn posts are immediate-only. YouTube posts must be video-only and may require private visibility until Google verification or audit is complete. ### Get Post `GET /api/v1/posts/:id` Auth: valid API key Rate limit operation: `apiRead` Response: `200` with `{ "post": Post }`, or `404` if the post does not exist or belongs to another user. ### Update Post `PATCH /api/v1/posts/:id` Auth: valid API key Rate limit operation: `createPost` Provide any subset of `content`, `platforms`, `scheduledFor`, or `platformSetId`. Response: `200` with `{ "post": Post }`. ### Cancel / Discard Post `POST /api/v1/posts/:id/cancel` Auth: valid API key Rate limit operation: `createPost` Cancels/discards posts in these states: `draft`, `scheduled`, `queued`, `partial`, `failed`. Cannot cancel `published`, `posting`, or already `discarded` posts. ### Delete / Discard Post `DELETE /api/v1/posts/:id` Auth: valid API key Rate limit operation: `createPost` Discards the post through the primary resource endpoint and returns `{ "post": Post }`. ### Publish Post Immediately `POST /api/v1/posts/:id/publish` Auth: valid API key Rate limit operation: `createPost` Publishes a `draft` or `scheduled` post immediately and returns `{ "post": Post }`. ### Retry Failed Platforms `POST /api/v1/posts/:id/retry` Auth: valid API key Rate limit operation: `createPost` Optionally pass `{ "platforms": ["threads"] }` to retry only specific failed platforms. ### Accounts `GET /api/v1/accounts` Auth: valid API key Rate limit operation: `apiRead` Lists connected accounts. Optional query params: - `platform` - `platformSetId` Response: `200` with `{ "accounts": [...] }`. `GET /api/v1/accounts/:id` Auth: valid API key Rate limit operation: `apiRead` Returns `{ "account": ... }` or `404`. ## Post Shapes Minimal `PostContent`: ```ts { text: string; media: PostMedia[]; linkUrl: string | null; platformOverrides: Record }>; } ``` Platform override `settings` currently supports placement selection for Instagram and Facebook. For Instagram, valid placements are `feed`, `story`, `reel`, `carousel`. For Facebook, valid placements are `feed`, `story`, `reel`. Placement selection is validated against the media configuration (e.g. carousel requires 2+ images, reel requires exactly one video). Facebook Reels currently expect 9:16 video between 4 and 60 seconds when media metadata is available. TikTok settings support creator-aware controls such as `privacyLevel`, `disableComment`, `disableDuet`, `disableStitch`, `brandContentToggle`, `brandOrganicToggle`, `isAigc`, and `videoCoverTimestampMs`. YouTube settings support `title`, `description`, `tags`, `categoryId`, `privacyStatus`, `publishAt`, `notifySubscribers`, `selfDeclaredMadeForKids`, and `containsSyntheticMedia`. Minimal `Post`: ```ts { // ... PostContent fields id: string; userId: string; platformPosts: PlatformPost[]; status: PostStatus; scheduledFor: string | null; createdAt: string; updatedAt: string; platformSetId: string; } ``` Minimal `PostMedia`: ```ts { id?: string; type: "image" | "video" | "gif"; url: string; filename?: string; mimeType?: string; sizeBytes?: number; width?: number | null; height?: number | null; durationSeconds?: number | null; altText?: string | null; thumbnailUrl?: string | null; } ``` Post statuses: - `draft` - `scheduled` - `queued` - `posting` - `published` - `partial` - `failed` - `discarded` Per-platform statuses: - `pending` - `uploading` - `processing` - `publishing` - `published` - `failed` - `cancelled` ## Platform Sets Platform sets group connected social accounts into named collections. Each user can create up to 20 sets and designate one as default. Every post must be scoped to a platform set via `platformSetId`. Platform set management routes are available on the public versioned REST surface with Bearer API keys. Canonical endpoints: - `GET /api/v1/platform-sets` - `POST /api/v1/platform-sets` - `PATCH /api/v1/platform-sets/:id` - `DELETE /api/v1/platform-sets/:id` Compatibility routes that also accept Bearer API keys for the same list/create/rename/delete behavior: - `GET /api/platforms/sets` - `POST /api/platforms/sets` - `PATCH /api/platforms/sets/:id` - `DELETE /api/platforms/sets/:id` The multipart post creation route `POST /api/posts` also accepts dashboard cookie auth or Bearer API keys. It supports direct file upload for media posts. ### List Platform Sets `GET /api/v1/platform-sets` Returns all platform sets for the authenticated user with active connection counts. Response `200`: ```json { "sets": [ { "id": "set_uuid", "userId": "user_xyz", "name": "My Brand Accounts", "isDefault": true, "createdAt": "2026-04-30T10:00:00Z", "updatedAt": "2026-04-30T10:00:00Z", "connectionCount": 3 } ] } ``` ### Create Platform Set `POST /api/v1/platform-sets` Request body: | Field | Type | Required | Description | |---|---|---|---| | `name` | string | Yes | Display name (1–50 characters) | Response `200`: ```json { "set": { "id": "set_uuid", "userId": "user_xyz", "name": "My Brand Accounts", "isDefault": false, "createdAt": "2026-04-30T10:00:00Z", "updatedAt": "2026-04-30T10:00:00Z", "connectionCount": 0 } } ``` Error `400`: name missing, name exceeds 50 characters, or max 20 sets reached. ### Rename Platform Set `PATCH /api/v1/platform-sets/:id` Request body: | Field | Type | Required | Description | |---|---|---|---| | `name` | string | Yes | New display name (1–50 characters) | Response `200`: ```json { "set": { "id": "set_uuid", "userId": "user_xyz", "name": "Renamed Set", "isDefault": false, "createdAt": "2026-04-30T10:00:00Z", "updatedAt": "2026-05-01T12:00:00Z" } } ``` Errors: `400` (invalid name), `403` (not owner), `404` (not found). ### Delete Platform Set `DELETE /api/v1/platform-sets/:id` Deletes a platform set. All active connections in the set are revoked (status set to `revoked`, tokens cleared). The default set cannot be deleted. Response `200`: ```json { "success": true } ``` Errors: `400` (cannot delete default set), `404` (not found). ## YouTube Current YouTube support should be described as video-publishing beta with explicit launch-state caveats, not as a generic fully-open publishing surface. Server setup: - Required env vars: `YOUTUBE_CLIENT_ID`, `YOUTUBE_CLIENT_SECRET`, `YOUTUBE_REDIRECT_URI` - Optional env var: `YOUTUBE_FORCE_PRIVATE_UPLOADS=true` to force requested public or unlisted uploads back to private while audit or verification is pending - Required OAuth scopes: `https://www.googleapis.com/auth/youtube.upload`, `https://www.googleapis.com/auth/youtube` Behavior and limits: - Exactly one video per post - No text-only, image-only, or mixed-media YouTube posts - Title required through `content.text` or `platformOverrides.youtube.settings.title` - `privacyStatus` defaults to `private` - `categoryId` defaults to `"22"` if omitted - `publishAt` must be a future ISO 8601 timestamp, requires `privacyStatus: "private"`, and cannot be combined with Ampost `scheduledFor` - If the Google project is still unverified or not yet audited, requested `public` or `unlisted` visibility may still become `private` Current quota notes from Google's official docs as of June 1, 2026: - `videos.insert` uses a dedicated Video Uploads quota bucket with a default daily limit of 100 calls and a per-call cost of 1 - Other YouTube Data API methods continue to use the general daily quota bucket, which defaults to 10,000 units - Invalid requests still consume quota Example JSON request body: ```json { "content": { "text": "Fallback title", "media": [ { "url": "https://cdn.example.com/product-demo.mp4", "type": "video", "filename": "product-demo.mp4", "mimeType": "video/mp4", "sizeBytes": 18432000, "width": 1920, "height": 1080, "durationSeconds": 45, "thumbnailUrl": "https://cdn.example.com/product-demo-thumb.jpg" } ], "linkUrl": null, "platformOverrides": { "youtube": { "settings": { "title": "Explicit YouTube title", "description": "Launch walkthrough", "tags": ["launch", "demo"], "categoryId": "28", "privacyStatus": "private", "notifySubscribers": false, "selfDeclaredMadeForKids": false, "containsSyntheticMedia": true } } } }, "platforms": ["youtube"], "scheduledFor": null, "platformSetId": "set_uuid" } ``` ## LinkedIn Current LinkedIn support should be described as member-profile publishing, not as a generic fully-open LinkedIn integration. Server setup: - Required env vars: `LINKEDIN_CLIENT_ID`, `LINKEDIN_CLIENT_SECRET`, `LINKEDIN_REDIRECT_URI`, `LINKEDIN_API_VERSION` - Current default version header: `Linkedin-Version: 202605` - Optional env var: `LINKEDIN_ENABLE_ORGANIZATION_POSTING=false` should stay false unless you have approved organization access and have shipped the related admin checks - Required OAuth scopes: `openid`, `profile`, `email`, `w_member_social` Behavior and limits: - Text-only, image, article, and single-video member posts are supported - Scheduling is not supported - Organization/Page posting is intentionally gated and rejected in the public launch - `visibility` supports `PUBLIC` and `CONNECTIONS` - `articleTitle` and `articleDescription` require `content.linkUrl` - `altText` is supported for image accessibility metadata - LinkedIn documents daily limits per application and per member, both resetting at midnight UTC - Exact endpoint quotas are not published; inspect the LinkedIn Developer Portal Analytics tab after the app has made at least one request that UTC day - LinkedIn Marketing REST calls require `Linkedin-Version: YYYYMM` and `X-Restli-Protocol-Version: 2.0.0` - LinkedIn's published versioning guidance says each monthly version is supported for at least one year, so operators should review and bump `LINKEDIN_API_VERSION` before sunset - If LinkedIn does not return a refresh token for the app, Ampost requires reconnect instead of claiming indefinite silent refresh Example JSON request body: ```json { "content": { "text": "LinkedIn image post", "media": [ { "url": "https://cdn.example.com/product-shot.png", "type": "image", "filename": "product-shot.png", "mimeType": "image/png", "sizeBytes": 248120, "width": 1600, "height": 900, "altText": "Product dashboard with the LinkedIn publish flow open." } ], "linkUrl": null, "platformOverrides": { "linkedin": { "settings": { "visibility": "CONNECTIONS" } } } }, "platforms": ["linkedin"], "scheduledFor": null, "platformSetId": "set_uuid" } ``` ## Media Upload Media files are uploaded inline when creating a post through the dashboard endpoint. Send a `multipart/form-data` request with a `data` field (JSON post metadata) and one or more `files` fields (media files): `POST /api/posts` Accepted MIME types: `image/jpeg`, `image/png`, `image/webp`, `video/mp4`, `video/quicktime`. Max file size: 200 MB per file. Example: ```bash curl -X POST https://api.ampost.io/api/posts \ -H "Cookie: ..." \ -F 'data={"text":"Hello","platforms":["instagram"],"platformSetId":"set_uuid"}' \ -F "files=@photo.jpg" ``` Response returns the created post object with media URLs. Use the media fields in the response as source of truth for post content. For YouTube specifically: - Inline multipart uploads still become explicit typed media objects inside Ampost - JSON and MCP callers should continue to send explicit media descriptors - Ampost's inline upload cap is 200 MB per file even though YouTube's upstream platform limit is much higher ## MCP Server Endpoint: `https://api.ampost.io/mcp` Transport: Streamable HTTP Auth: same Bearer API key header as REST. Session lifecycle: initialize with `POST /mcp`, then reuse the returned `Mcp-Session-Id` header on follow-up `GET`, `POST`, or `DELETE` requests for that session. Example MCP config: ```json { "mcpServers": { "ampost": { "url": "https://api.ampost.io/mcp", "headers": { "Authorization": "Bearer amp_live_YOUR_KEY" } } } } ``` ### MCP Tools | Tool | Auth | Description | |---|---|---| | `posts_create` | valid API key | Create a draft or scheduled post | | `posts_get` | valid API key | Get one post | | `posts_list` | valid API key | List posts with filters | | `posts_update` | valid API key | Update a draft or scheduled post | | `posts_delete` | valid API key | Discard a discardable post | | `posts_publish` | valid API key | Publish a draft or scheduled post immediately | | `posts_retry` | valid API key | Retry failed platform publishes | | `accounts_list` | valid API key | List connected accounts | | `accounts_get` | valid API key | Get one connected account | | `platform_sets_list` | valid API key | List platform sets with connection counts | | `platform_sets_create` | valid API key | Create a platform set | | `platform_sets_rename` | valid API key | Rename a platform set | | `platform_sets_delete` | valid API key | Delete a non-default platform set and revoke its active connections | ### MCP `posts_create` Required parameters: - `text`: post text - `platforms`: supported platform keys - `platformSetId`: required platform set UUID Optional parameters: - `scheduledFor`: ISO 8601 timestamp; omit/null for draft - `mediaUrls`: legacy public media URLs for non-TikTok cases - `media`: explicit media descriptors with `url`, `type`, `mimeType`, `sizeBytes`, optional dimensions, and `durationSeconds` - `linkUrl` - `platformOverrides`: per-platform overrides including `text` and `settings` (for placement, LinkedIn member-post settings, TikTok creator controls, or YouTube video metadata) Example: ```json { "text": "TikTok launch clip", "platforms": ["tiktok"], "scheduledFor": null, "media": [ { "url": "https://cdn.example.com/launch.mp4", "type": "video", "mimeType": "video/mp4", "sizeBytes": 18432000, "fileName": "launch.mp4", "width": 1080, "height": 1920, "durationSeconds": 24 } ], "platformOverrides": { "tiktok": { "settings": { "privacyLevel": "SELF_ONLY", "disableComment": true } } }, "platformSetId": "set_uuid" } ``` Do not use legacy `mediaUrls` for TikTok or YouTube. Both require explicit `media` objects. LinkedIn video also requires explicit `media` objects. TikTok rejects scheduled or text-only posts and may expose only private-only visibility while app review is still limiting the connected client. LinkedIn rejects scheduling and organization/Page posting in the current launch. YouTube requires exactly one video and may keep requested public or unlisted visibility private during beta launch mode. ### MCP `posts_update` Required parameter: - `postId` Optional parameters: - `text` - `platforms` - `scheduledFor` - `mediaUrls` - `media` - `linkUrl` - `platformOverrides` - `platformSetId` If you change `platforms` or `platformSetId`, Ampost re-resolves the active connections from the selected platform set. For TikTok and YouTube updates, keep using explicit `media` objects instead of `mediaUrls` so Ampost can validate creator restrictions, media metadata, and launch-state behavior. For TikTok, inspect creator privacy options before assuming public visibility because unaudited or review-limited apps may be private-only. For LinkedIn, keep using explicit `media` objects for video and whenever you need reliable alt-text or media-type preservation. ### MCP Platform Set Management Use the dedicated MCP tools to manage sets, then reuse the same `platformSetId` across post and account operations. Example: ```json { "platform_sets_create": { "name": "Client A" }, "accounts_list": { "platformSetId": "set_uuid" }, "posts_list": { "platformSetId": "set_uuid", "platform": "threads" } } ``` ### MCP Account Discovery Use `accounts_list` to inspect which active connections are available inside a platform set before creating posts: ```json { "platform": "threads" } ``` Returned account objects include `id`, but `posts_create` and `posts_update` resolve connections automatically from the selected platform set rather than taking connection IDs directly. LinkedIn accounts also expose non-secret connection metadata such as OIDC profile fields, granted scopes, token health, and the organization-posting gate state. ## Tier and Rate Limit Notes Ampost uses three tiers: `free`, `pro`, `max`. The API-key and MCP surfaces require the `apiKeys` tier feature. In current tier config, `pro` and `max` have `apiKeys: true`; `free` has `apiKeys: false`. Per-operation limits: | Operation | Free | Pro | Max | Window | |---|---:|---:|---:|---| | `createPost` | 30 | 300 | 1000 | hour | | `publishPost` | 10 | 100 | 500 | hour | | `connectPlatform` | 5 | 20 | 50 | hour | | `mediaUpload` | 30 | 200 | 500 | hour | | `apiRead` | 100 | 600 | 2000 | minute | Rate limit responses may include: - `X-RateLimit-Remaining` - `X-RateLimit-Reset` ## Error Codes Common post-engine errors: | Code | HTTP | Meaning | |---|---:|---| | `VALIDATION_FAILED` | 400 | Request failed validation | | `NO_PLATFORMS_SELECTED` | 400 | No target platform selected | | `PLATFORM_NOT_CONNECTED` | 400 | Missing connection ID for a platform | | `CONTENT_TOO_LONG` | 400 | Text exceeds platform limit | | `UNSUPPORTED_MEDIA_TYPE` | 400 | Media type not supported | | `UNSUPPORTED_MEDIA_CONFIG` | 400 | Media configuration not supported by platform | | `INVALID_PLACEMENT` | 400 | Selected placement is invalid for the media configuration | | `MIXED_MEDIA_NOT_SUPPORTED` | 400 | Platform does not support mixing image and video media | | `PAGE_IDENTITY_CONFIRMATION_REQUIRED` | 403 | Facebook requires the Page manager to complete identity confirmation in the Facebook mobile app before publishing | | `POST_NOT_FOUND` | 404 | Post missing or not owned by user | | `INVALID_STATUS_TRANSITION` | 409 | Operation is invalid for current post status | | `RATE_LIMITED` | 429 | Rate limit exceeded | | `MEDIA_UPLOAD_FAILED` | 502 | Platform media upload failed | | `BROKER_ERROR` | 502 | Platform broker failure | Common broker errors include: - `AUTH_EXPIRED` - `AUTH_REVOKED` - `REFRESH_TOKEN_EXPIRED` - `CONNECTION_NOT_FOUND` - `TOKEN_EXCHANGE_FAILED` - `PROFILE_FETCH_FAILED` - `PUBLISH_FAILED` - `PLATFORM_ERROR` - `INVALID_CONFIG` ## Dashboard and OAuth Routes These routes are not part of the public Bearer-key REST or MCP surface: - `GET /api/platforms/{platform}/auth-url` - `GET /api/platforms/{platform}/callback` - `POST /api/platforms/{platform}/refresh` - `DELETE /api/platforms/{platform}/disconnect` - `GET /api/platforms/facebook/pages` Auth URL and callback routes are browser/dashboard entrypoints. Refresh, disconnect, and Facebook page-selection routes use dashboard session auth and reject Bearer API keys. Supported OAuth platform route groups currently exist for: - `facebook` - `instagram` - `linkedin` - `twitter` - `tiktok` - `youtube` - `threads` ## Billing Dashboard subscription routes use Supabase cookie auth: - `POST /api/checkout` - `POST /api/billing-portal` - `POST /api/subscription/cancel` - `POST /api/subscription/resume` Stripe sends subscription webhooks to: - `POST /api/webhooks/stripe` ## Integration Guidance for LLMs When asked to create or schedule a post: 1. Use `accounts_list` to see available connections. 2. Use only supported platform keys. 3. Provide a valid `platformSetId` (required). Use `platform_sets_list` to discover available sets. Connections are resolved automatically from the set. 4. Use `posts_create` to create a draft or scheduled post. 5. Use `posts_publish` only when the user explicitly wants immediate publishing. Do not suggest `bluesky` as a working publish target yet. For LinkedIn, describe it as member-profile publishing only and do not advertise organization/Page posting, analytics, comments, or scheduling unless the product surface changes.