{"openapi":"3.1.0","info":{"title":"PostPlain API","version":"1.0.0","description":"PostPlain Agent-Native Social Media Scheduling API. Schedule posts to X, LinkedIn, Threads, and Facebook. Authenticate with a Bearer token using your mp_ prefixed API key."},"servers":[{"url":"https:\/\/postplain.com\/api\/v1"}],"security":[{"http":[]}],"paths":{"\/accounts":{"get":{"operationId":"account.index","description":"Returns all social media accounts connected to your team, including platform,\nusername, character limits, and connection timestamps.","summary":"List connected accounts","tags":["Account"],"responses":{"200":{"description":"","content":{"application\/json":{"schema":{"type":"integer","const":200}}}}}}},"\/accounts\/connect\/{platform}":{"post":{"operationId":"account.connect","description":"Initiate an OAuth connection for a social platform. Returns a URL that a human\nmust visit to complete the OAuth flow. Supported platforms: x, linkedin, threads, facebook.","summary":"Connect an account","tags":["Account"],"parameters":[{"name":"platform","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"","content":{"application\/json":{"schema":{"type":"integer","const":200}}}}}}},"\/accounts\/{account}":{"delete":{"operationId":"account.destroy","description":"Remove a connected social media account from your team. This is a soft delete;\npreviously published posts are not affected.","summary":"Disconnect an account","tags":["Account"],"parameters":[{"name":"account","in":"path","required":true,"description":"The account ID","schema":{"type":"integer"}}],"responses":{"200":{"description":"","content":{"application\/json":{"schema":{"type":"integer","const":200}}}},"404":{"$ref":"#\/components\/responses\/ModelNotFoundException"}}}},"\/health":{"get":{"operationId":"health.index","description":"Returns the current health status of the API. This endpoint is unauthenticated\nand can be used to verify connectivity before making authenticated requests.","summary":"Health check","tags":["Health"],"responses":{"200":{"description":"","content":{"application\/json":{"schema":{"type":"object","properties":{"status":{"type":"string","const":"healthy"},"version":{"type":"string","const":"1.0.0"},"timestamp":{"type":"string"}},"required":["status","version","timestamp"]}}}}},"security":[]}},"\/limits":{"get":{"operationId":"limits.index","description":"Returns your team's current plan details, post usage, rate limits,\nconnected account count, and per-platform character constraints.\nUse this before scheduling to check remaining quota.","summary":"Get plan limits","tags":["Limits"],"responses":{"200":{"description":"","content":{"application\/json":{"schema":{"type":"integer","const":200}}}}}}},"\/posts\/schedule":{"post":{"operationId":"post.schedule","description":"Schedule a post to one or more connected social accounts. If `scheduled_at` is omitted,\nthe post begins publishing in the background immediately after creation.\nSupports idempotency keys to prevent duplicate posts.","summary":"Schedule a post","tags":["Post"],"requestBody":{"required":true,"content":{"application\/json":{"schema":{"type":"object","properties":{"text":{"type":"string","maxLength":25000},"scheduled_at":{"type":["string","null"],"format":"date-time"},"idempotency_key":{"type":["string","null"],"maxLength":255},"account_ids":{"type":"array","items":{"type":"integer"},"minItems":1},"media_urls":{"type":["array","null"],"items":{"type":"string","format":"uri"},"maxItems":4},"platform_overrides":{"type":"object","properties":{"x":{"type":["string","null"],"maxLength":25000},"linkedin":{"type":["string","null"],"maxLength":3000},"threads":{"type":["string","null"],"maxLength":500},"facebook":{"type":["string","null"],"maxLength":63206},"bluesky":{"type":["string","null"],"maxLength":300}}}},"required":["text","account_ids"]}}}},"responses":{"200":{"description":"","content":{"application\/json":{"schema":{"type":"integer","const":201}}}},"422":{"$ref":"#\/components\/responses\/ValidationException"}}}},"\/posts\/bulk-schedule":{"post":{"operationId":"post.bulkSchedule","description":"Schedule up to 50 posts in a single request. Each post is processed independently;\nfailures for individual posts do not affect others. Returns separate arrays\nof scheduled and failed posts.","summary":"Bulk schedule posts","tags":["Post"],"requestBody":{"required":true,"content":{"application\/json":{"schema":{"type":"object","properties":{"posts":{"type":"array","items":{"type":"object","properties":{"text":{"type":"string","maxLength":25000},"account_ids":{"type":"array","items":{"type":"integer"},"minItems":1},"scheduled_at":{"type":["string","null"],"format":"date-time"},"media_urls":{"type":["array","null"],"items":{"type":"string","format":"uri"},"maxItems":4},"platform_overrides":{"type":["object","null"],"properties":{"x":{"type":["string","null"],"maxLength":25000},"linkedin":{"type":["string","null"],"maxLength":3000},"threads":{"type":["string","null"],"maxLength":500},"facebook":{"type":["string","null"],"maxLength":63206},"bluesky":{"type":["string","null"],"maxLength":300}}},"idempotency_key":{"type":["string","null"],"maxLength":255}},"required":["text","account_ids"]},"minItems":1,"maxItems":50}},"required":["posts"]}}}},"responses":{"200":{"description":"","content":{"application\/json":{"schema":{"type":"integer","const":201}}}},"422":{"$ref":"#\/components\/responses\/ValidationException"}}}},"\/posts\/{post}":{"get":{"operationId":"post.show","description":"Retrieve full details of a specific post, including per-platform publishing status,\nplatform post IDs, and timestamps.","summary":"Get a post","tags":["Post"],"parameters":[{"name":"post","in":"path","required":true,"description":"The post ID","schema":{"type":"integer"}}],"responses":{"200":{"description":"","content":{"application\/json":{"schema":{"type":"integer","const":200}}}},"404":{"$ref":"#\/components\/responses\/ModelNotFoundException"}}},"delete":{"operationId":"post.destroy","description":"Cancel a post that has not yet been published. Only posts with status `scheduled`\ncan be cancelled. Already published or failed posts cannot be cancelled.","summary":"Cancel a scheduled post","tags":["Post"],"parameters":[{"name":"post","in":"path","required":true,"description":"The post ID","schema":{"type":"integer"}}],"responses":{"200":{"description":"","content":{"application\/json":{"schema":{"type":"integer","const":200}}}},"404":{"$ref":"#\/components\/responses\/ModelNotFoundException"}}}},"\/posts\/{post}\/analytics":{"get":{"operationId":"post.analytics","description":"Retrieve engagement analytics for a published post, broken down by platform.\nIncludes impressions, likes, comments, shares, and clicks.","summary":"Get post analytics","tags":["Post"],"parameters":[{"name":"post","in":"path","required":true,"description":"The post ID","schema":{"type":"integer"}}],"responses":{"200":{"description":"","content":{"application\/json":{"schema":{"type":"integer","const":200}}}},"404":{"$ref":"#\/components\/responses\/ModelNotFoundException"}}}},"\/posts":{"get":{"operationId":"post.index","description":"Returns a paginated list of posts. Filter by status (scheduled, publishing, published,\nfailed, cancelled),\nplatform (x, linkedin, threads, facebook, bluesky), or date range (from\/to as ISO 8601).","summary":"List posts","tags":["Post"],"parameters":[{"name":"status","in":"query","schema":{"type":"string"}},{"name":"platform","in":"query","schema":{"type":"string"}},{"name":"from","in":"query","schema":{"type":"string"}},{"name":"to","in":"query","schema":{"type":"string"}},{"name":"per_page","in":"query","schema":{"type":"string","default":20}}],"responses":{"200":{"description":"","content":{"application\/json":{"schema":{"type":"integer","const":200}}}}}}},"\/webhooks":{"post":{"operationId":"webhook.store","description":"Register a URL to receive POST notifications for specified events.\nReturns a signing secret (whsec_ prefixed) for verifying webhook payloads.\nAvailable events: post.published, post.failed, account.connected, account.disconnected.\n\nAll webhook payloads follow a standard envelope: `{\"event\": \"post.published\", \"timestamp\": \"2026-04-01T09:00:05Z\", \"data\": {...}}`.\nSee the WebhookPayload_PostPublished, WebhookPayload_PostFailed, WebhookPayload_AccountConnected,\nand WebhookPayload_AccountDisconnected component schemas for full payload details.","summary":"Create a webhook","tags":["Webhook"],"requestBody":{"required":true,"content":{"application\/json":{"schema":{"type":"object","properties":{"url":{"type":"string","format":"uri"},"events":{"type":"array","items":{"type":"string"},"minItems":1}},"required":["url","events"]}}}},"responses":{"200":{"description":"","content":{"application\/json":{"schema":{"type":"integer","const":201}}}},"422":{"$ref":"#\/components\/responses\/ValidationException"}}},"get":{"operationId":"webhook.index","description":"Returns all webhook subscriptions for your team.","summary":"List webhooks","tags":["Webhook"],"responses":{"200":{"description":"","content":{"application\/json":{"schema":{"type":"integer","const":200}}}}}}},"\/webhooks\/{webhook}":{"put":{"operationId":"webhook.update","description":"Modify a webhook's URL, events, or active status. Only provided fields are updated.","summary":"Update a webhook","tags":["Webhook"],"parameters":[{"name":"webhook","in":"path","required":true,"description":"The webhook ID","schema":{"type":"integer"}}],"requestBody":{"content":{"application\/json":{"schema":{"type":"object","properties":{"url":{"type":"string","format":"uri"},"is_active":{"type":"boolean"},"events":{"type":"array","items":{"type":"string"},"minItems":1}}}}}},"responses":{"200":{"description":"","content":{"application\/json":{"schema":{"type":"integer","const":200}}}},"422":{"$ref":"#\/components\/responses\/ValidationException"},"404":{"$ref":"#\/components\/responses\/ModelNotFoundException"}}},"delete":{"operationId":"webhook.destroy","description":"Permanently remove a webhook subscription. No further events will be delivered to this URL.","summary":"Delete a webhook","tags":["Webhook"],"parameters":[{"name":"webhook","in":"path","required":true,"description":"The webhook ID","schema":{"type":"integer"}}],"responses":{"200":{"description":"","content":{"application\/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"id":{"type":"integer"},"status":{"type":"string","const":"deleted"}},"required":["id","status"]},"_meta":{"type":"string"}},"required":["data","_meta"]}}}},"404":{"$ref":"#\/components\/responses\/ModelNotFoundException"}}}}},"components":{"securitySchemes":{"http":{"type":"http","scheme":"bearer"}},"responses":{"ValidationException":{"description":"Validation error","content":{"application\/json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Errors overview."},"errors":{"type":"object","description":"A detailed description of each field that failed validation.","additionalProperties":{"type":"array","items":{"type":"string"}}}},"required":["message","errors"]}}}},"ModelNotFoundException":{"description":"Not found","content":{"application\/json":{"schema":{"type":"object","properties":{"message":{"type":"string","description":"Error overview."}},"required":["message"]}}}}}}}