Docs for TryPost — open-source social media scheduling platform. Built with Mintlify.
TryPost is a Laravel + Inertia.js + Vue 3 application that lets users schedule and publish posts across 12 social platforms (14 platform identifiers in code, since LinkedIn and Instagram each have two connection flavors). It has two deployment modes: Cloud (managed by us) and Self-Hosted (user deploys on their own server).
The app source code lives at ~/Herd/trypost. This repo (trypost-docs) is the documentation site only.
CRITICAL: There are NO subdomains. Everything uses path-based routing under a single domain.
| Service | URL |
|---|---|
| Web app | https://app.trypost.it |
| REST API | https://app.trypost.it/api |
| MCP server | https://app.trypost.it/mcp/trypost |
| Register | https://app.trypost.it/register |
Same paths, user configures APP_URL in .env:
| Service | URL |
|---|---|
| Web app | {APP_URL} |
| REST API | {APP_URL}/api |
| MCP server | {APP_URL}/mcp/trypost |
Never use api.trypost.it or mcp.trypost.it — these do not exist.
5 top-level navigation anchors in docs.json:
| Anchor | Icon | Purpose |
|---|---|---|
| Documentation | book-open |
Product docs: quickstart, platforms, features, contributing |
| API Reference | code |
REST API endpoint docs with curl examples |
| Build with AI | microchip-ai |
MCP server intro, tools reference, 14 setup guides |
| Knowledge Base | book |
Conceptual guides: posts, media, signatures, labels, accounts, team, notifications |
| Self-Hosting | server |
Installation, configuration, production, Docker — isolated from the rest |
- Default instructions assume the user is on TryPost Cloud
- Self-hosted specifics go in
<Accordion>,<Note>, or the Self-Hosting anchor - Example: Platform pages say "click Connect" for cloud, API credentials in
<Accordion>for self-hosted
- API curl examples:
https://app.trypost.it/api/... - MCP server configs:
https://app.trypost.it/mcp/trypost - Dashboard links:
https://app.trypost.it - Cloud media URLs (R2 with custom domain):
https://media.trypost.it/medias/{uuid}.{ext} - Self-hosted examples:
{APP_URL}/api/...orhttps://trypost.yourdomain.com/api/... - Stored media
pathis alwaysmedias/{uuid}.{ext}; the public URL comes fromStorage::url($path)which prepends the configured disk URL (R2_URL, S3 endpoint, or local${APP_URL}/storage).
- SendKit (default mailer):
https://sendkit.dev?utm_source=trypost&utm_medium=docs&utm_campaign={context} - Hetzner (recommended hosting):
https://hetzner.cloud/?ref=V4djx1Vt7Mm7— always disclose as referral link
- REST API: Bearer token in
Authorizationheader (Personal Access Tokens issued by Laravel Passport, JWT format — do not assume any prefix or fixed length). In examples useYOUR_API_KEYas the placeholder. - MCP server: OAuth 2.1 with Dynamic Client Registration (PKCE S256, scope
mcp:use). Clients discover endpoints via/.well-known/oauth-authorization-serverand/.well-known/oauth-protected-resource, register at/oauth/register, then walk the user through/oauth/authorize→/oauth/tokenin a browser. The same PAT also works as a Bearer token against the MCP endpoint as a fallback for CLIs that don't speak OAuth yet. - Same PAT works for REST API and MCP server (Bearer fallback).
- Issued via
POST /api/api-keysor Settings > API Keys in the dashboard.
| Group | Endpoints |
|---|---|
| Posts | GET /posts, POST /posts, GET /posts/{post}, PUT /posts/{post}, DELETE /posts/{post}, POST /posts/{post}/media, POST /posts/{post}/media/from-url, GET /posts/{post}/metrics, GET /posts/{post}/preview |
| Platforms | GET /content-types |
| Workspace | GET /workspace |
| Signatures | GET /signatures, POST /signatures, PUT /signatures/{id}, DELETE /signatures/{id} |
| Labels | GET /labels, POST /labels, PUT /labels/{id}, DELETE /labels/{id} |
| Social Accounts | GET /social-accounts, PUT /social-accounts/{account}/toggle |
| API Keys | GET /api-keys, POST /api-keys, DELETE /api-keys/{apiToken} |
PUT /posts/{post} accepts a status of publishing to publish immediately (no separate publish endpoint at the REST level).
- Resources are returned unwrapped (
JsonResource::withoutWrapping()is set globally). A single resource looks like{ "id": ..., ... }— nodata:envelope. - Non-paginated collections return a plain JSON array (
[ {...}, {...} ]). - Only
GET /postsreturns the Laravel pagination envelope ({ data, links, meta }). attach-media-from-url,metrics, andprevieware bespoke — not Resource-shaped (nodata:wrapper, just the documented fields at top level). The multipart/mediaupload returns a regular Post resource.POST /api-keysreturns{ "token": {...}, "plain_token": "..." }. The plain token is shown ONCE.
Only GET /posts paginates (15 per page). All other list endpoints return full results as a plain array.
- Tool names are auto-derived from class basenames as kebab-case +
-toolsuffix (Laravel MCP convention — no#[Name]overrides inTryPostServer). - Post tools:
list-posts-tool,get-post-tool,create-post-tool,update-post-tool,publish-post-tool,preview-post-tool,delete-post-tool,attach-media-from-url-tool,request-media-upload-tool,attach-media-from-upload-tool,get-post-metrics-tool. - Plus:
list-content-types-tool(Platforms),list-signatures-tool/create-signature-tool/update-signature-tool/delete-signature-tool,list-labels-tool/create-label-tool/update-label-tool/delete-label-tool,list-social-accounts-tool/toggle-social-account-tool,get-workspace-tool,list-api-keys-tool/create-api-key-tool/delete-api-key-tool. create-post-toolacceptsplatforms[](each withsocial_account_idandcontent_type),scheduled_at,label_ids, etc. — same shape as RESTPOST /posts.publish-post-toolis a separate, destructive tool (annotatedIsDestructive); REST clients usePUT /posts/{id}withstatus=publishinginstead.- Server route:
Mcp::web('/mcp/trypost', TryPostServer::class)->middleware(['auth:api', 'workspace.token']). Theworkspace.tokenmiddleware (LoadWorkspaceFromToken) is what returns402 Active subscription requiredon Cloud accounts without an active trial/subscription.
These are the exact string values used in API responses. Never use alternatives.
draft, scheduled, publishing, published, partially_published, failed
pending, publishing, published, failed
connected, disconnected, token_expired
NOT active/inactive — those are for is_active boolean field.
linkedin, linkedin-page, x, tiktok, youtube, facebook, instagram, instagram-facebook, threads, pinterest, bluesky, mastodon, telegram, discord
instagram is the standalone Basic Display flow; instagram-facebook is the Business-via-Facebook-Page flavor.
| Platform | Content types |
|---|---|
linkedin_post, linkedin_carousel |
|
| LinkedIn Page | linkedin_page_post, linkedin_page_carousel |
| X | x_post |
facebook_post, facebook_reel, facebook_story |
|
instagram_feed, instagram_carousel, instagram_reel, instagram_story |
|
| TikTok | tiktok_video, tiktok_photo |
| YouTube | youtube_short |
| Threads | threads_post |
pinterest_pin, pinterest_video_pin, pinterest_carousel |
|
| Bluesky | bluesky_post |
| Mastodon | mastodon_post |
| Telegram | telegram_post |
| Discord | discord_message |
Instagram content types work for both instagram and instagram-facebook accounts.
Never use generic values like text, image, video.
There is no status enum on tokens. AccessToken exposes revoked (boolean) and expires_at (nullable timestamp). The ApiKeyResource derives is_active from ! revoked && (expires_at is null || expires_at > now).
image, video
There is no document type. Allowed MIME types: images = image/jpeg, image/png, image/gif, image/webp (PNG/WebP stills are normalized to JPEG q100); videos = video/mp4, video/quicktime. Default size caps: 10 MB image, 1024 MB video (overridable via MEDIA_IMAGE_MAX_SIZE_MB / MEDIA_VIDEO_MAX_SIZE_MB). The MCP signed-URL upload path has its own tighter cap of 50 MB regardless of type (overridable via MCP_UPLOAD_MAX_SIZE_MB), with a 15-minute single-use URL TTL (MCP_UPLOAD_URL_TTL_MINUTES) and a 10/min/IP rate limit.
post_published, post_failed, post_partially_published, post_ready, account_disconnected, invite_received, member_joined, member_removed, mentioned_in_comment
email, in_app, both
admin, member, viewer
There is no owner role — the workspace owner is the user on workspaces.owner_id. Roles only apply to additional members joined via invites.
| Trigger | |
|---|---|
PostPublished |
Post published successfully |
PostPublishFailed |
Post failed to publish on one or more platforms |
AccountDisconnected |
A single social account lost authorization |
WorkspaceConnectionsDisconnected |
Daily check found disconnected accounts in a workspace |
WorkspaceInvite |
Team member invited to join a workspace |
| Email verification | New user registration (Laravel built-in) |
| Password reset | User requests password reset (Laravel built-in) |
Default mailer is SendKit (MAIL_MAILER=sendkit).
| Command | Schedule | Purpose |
|---|---|---|
posts:process-scheduled |
Every minute | Dispatches PublishPost jobs for posts that are due |
social:refresh-expiring-tokens |
Hourly | Proactively refreshes OAuth tokens before they expire |
social:recover-stuck-posts |
Every 30 minutes | Recovers posts stuck in publishing (retry or mark failed) |
social:check-connections |
Daily | Verifies all connected social accounts still authenticate |
- PHP 8.2+, Node.js 20+, PostgreSQL 14+ or MySQL 8+, Redis 6+
- Supervisor for Horizon (queues) and Reverb (WebSockets)
- Cron job running
php artisan schedule:runevery minute - Upload limits:
upload_max_filesize=2G,post_max_size=2G(video support) - Bluesky and Mastodon work without API credentials
- All other platforms require developer app credentials
English (en), Spanish (es), Portuguese (pt-BR).
npx @mintlify/cli dev