# Analytics Source: https://docs.postiz.com/cli/analytics View platform and post-level analytics from the command line ## Platform Analytics Get analytics for a specific integration/channel. Returns metrics like followers, impressions, and engagement over time. ```bash theme={null} postiz analytics:platform ``` ### Options | Flag | Description | | ------------ | ---------------------------------------- | | `-d, --date` | Number of days to look back (default: 7) | ### Examples ```bash theme={null} # Last 7 days (default) postiz analytics:platform your-integration-id # Last 30 days postiz analytics:platform your-integration-id -d 30 # Last 90 days postiz analytics:platform your-integration-id -d 90 ``` The response is an array of metrics, each with daily data points: ```json theme={null} [ { "label": "Followers", "data": [ { "total": "1250", "date": "2025-01-01" }, { "total": "1280", "date": "2025-01-02" } ], "percentageChange": 2.4 }, { "label": "Impressions", "data": [ { "total": "5000", "date": "2025-01-01" }, { "total": "5200", "date": "2025-01-02" } ], "percentageChange": 4.0 } ] ``` The metrics returned depend on the platform. For example, X returns followers and impressions, while YouTube may return subscribers and views. ## Post Analytics Get analytics for a specific published post. Returns metrics like likes, comments, shares, and impressions. ```bash theme={null} postiz analytics:post ``` ### Options | Flag | Description | | ------------ | ---------------------------------------- | | `-d, --date` | Number of days to look back (default: 7) | ### Examples ```bash theme={null} # Last 7 days (default) postiz analytics:post your-post-id # Last 30 days postiz analytics:post your-post-id -d 30 ``` The response follows the same format as platform analytics: ```json theme={null} [ { "label": "Likes", "data": [ { "total": "150", "date": "2025-01-01" }, { "total": "175", "date": "2025-01-02" } ], "percentageChange": 16.7 }, { "label": "Comments", "data": [ { "total": "25", "date": "2025-01-01" }, { "total": "30", "date": "2025-01-02" } ], "percentageChange": 20.0 } ] ``` Post analytics are only available for published posts. Draft or queued posts won't return analytics data. ## Scripting with Analytics Extract specific metrics using `jq`: ```bash theme={null} # Get just the follower count trend postiz analytics:platform integration-id -d 30 | jq '.[] | select(.label=="Followers")' # Get percentage changes for all metrics postiz analytics:platform integration-id | jq '.[] | {label, percentageChange}' # Get the latest total for each post metric postiz analytics:post post-id | jq '.[] | {label, latest: .data[-1].total}' ``` # Authentication Source: https://docs.postiz.com/cli/authentication Set up OAuth2 or API key authentication for the Postiz CLI ## OAuth2 (Recommended) Authenticate using the device flow — no client ID or secret needed: ```bash theme={null} postiz auth:login ``` This will: 1. Display a one-time code in your terminal 2. Open your browser to authorize 3. Automatically save credentials to `~/.postiz/credentials.json` ### Auth Commands ```bash theme={null} # Check current auth status (verifies credentials are still valid) postiz auth:status # Remove stored credentials postiz auth:logout ``` ## API Key Alternatively, set your Postiz API key as an environment variable: ```bash theme={null} export POSTIZ_API_KEY=your_api_key_here ``` You can get your API key from the Postiz Settings page. OAuth2 credentials take priority over the API key when both are present. ## Environment Variables | Variable | Required | Default | Description | | -------------------- | -------- | ----------------------------- | ---------------------------------------------------- | | `POSTIZ_API_KEY` | No\* | - | Your Postiz API key | | `POSTIZ_API_URL` | No | `https://api.postiz.com` | Custom API endpoint (for self-hosted Postiz) | | `POSTIZ_AUTH_SERVER` | No | `https://cli-auth.postiz.com` | Custom auth server URL (for self-hosted auth server) | \*Either OAuth2 (via `postiz auth:login`) or `POSTIZ_API_KEY` is required. ## Self-Hosting the Auth Server By default, `postiz auth:login` uses the hosted auth server at `cli-auth.postiz.com`. If you want to self-host the OAuth2 device flow server, you can run your own instance. The auth server mediates the OAuth2 device flow so CLI users can authenticate without needing client credentials. ### Prerequisites * Node.js >= 18 * PostgreSQL ### How It Works ``` CLI Auth Server Postiz | | | |-- POST /device/code ------->| | |<-- device_code + user_code --| | | | | | User opens browser ------->| | | Enters code | | | |-- redirect to OAuth ----->| | |<-- callback with code ----| | |-- exchange for token ---->| | |<-- access_token ----------| | | (stored in Postgres) | | | | | POST /device/token (poll) >| | |<-- access_token ------------| | ``` ### 1. Clone the Repository The auth server lives in the [postiz-agent](https://github.com/gitroomhq/postiz-agent) repository: ```bash theme={null} git clone https://github.com/gitroomhq/postiz-agent.git cd postiz-agent/server ``` ### 2. Create an OAuth App in Postiz Go to **Postiz Settings > Developer > OAuth Apps** and create a new app. Set the callback URL to: ``` https://your-server-domain.com/device/callback ``` ### 3. Set Up Postgres Create a database. The server auto-creates the `device_requests` table on startup. ### 4. Configure Environment ```bash theme={null} export DATABASE_URL="postgresql://user:password@localhost:5432/postiz_auth" export POSTIZ_OAUTH_CLIENT_ID="pca_xxx" export POSTIZ_OAUTH_CLIENT_SECRET="pcs_xxx" export SERVER_URL="https://your-server-domain.com" ``` | Variable | Required | Default | Description | | ---------------------------- | -------- | ----------------------------- | --------------------------------------- | | `DATABASE_URL` | Yes | - | Postgres connection string | | `POSTIZ_OAUTH_CLIENT_ID` | Yes | - | OAuth app client ID from Postiz | | `POSTIZ_OAUTH_CLIENT_SECRET` | Yes | - | OAuth app client secret from Postiz | | `PORT` | No | `3111` | Server port | | `SERVER_URL` | No | `http://localhost:{PORT}` | Public URL of this server | | `POSTIZ_FRONTEND_URL` | No | `https://platform.postiz.com` | Postiz frontend URL for OAuth redirects | | `POSTIZ_API_URL` | No | `https://api.postiz.com` | Postiz API URL for token exchange | ### 5. Run the Server ```bash theme={null} pnpm install # Development pnpm dev # Production pnpm build pnpm start:prod ``` ### 6. Point the CLI to Your Server ```bash theme={null} export POSTIZ_AUTH_SERVER="https://your-server-domain.com" postiz auth:login ``` ### Server Endpoints | Method | Path | Description | | ------ | ------------------ | ------------------------------------------------------------------------------------ | | `POST` | `/device/code` | Start a new device flow. Returns `device_code`, `user_code`, and `verification_uri`. | | `GET` | `/device/verify` | Browser page where the user enters their code. | | `POST` | `/device/verify` | Validates user code and redirects to Postiz OAuth. | | `GET` | `/device/callback` | Postiz redirects here after authorization. Exchanges auth code for token. | | `POST` | `/device/token` | CLI polls this with `device_code`. Returns token when auth completes. | | `GET` | `/health` | Health check. | ### Deployment Any platform that runs Node.js and can connect to Postgres works — Railway, Fly.io, Render, VPS, etc. The server is stateless beyond Postgres, so it scales horizontally. Run multiple instances behind a load balancer if needed. # Integrations Source: https://docs.postiz.com/cli/integrations Discover connected accounts, settings schemas, and dynamic tools ## Listing Integrations List all connected social media accounts to get their IDs: ```bash theme={null} postiz integrations:list ``` This returns a JSON array of integrations. Use `jq` to extract specific fields: ```bash theme={null} # Get just the IDs and platform names postiz integrations:list | jq '.[] | {id, identifier}' ``` ```bash theme={null} # Find a specific platform postiz integrations:list | jq '.[] | select(.identifier=="reddit")' ``` ### Filtering by Group If your channels are organized into groups (customers), filter the list to a single group with `--group`: ```bash theme={null} postiz integrations:list --group "customer-id" ``` ## Listing Groups List all groups (customers) for your organization to get their IDs: ```bash theme={null} postiz integrations:groups ``` This returns a JSON array of `{id, name}` objects. Use a group's `id` with `integrations:list --group` to filter channels: ```bash theme={null} # Find a group by name, then list its integrations GROUP_ID=$(postiz integrations:groups | jq -r '.[] | select(.name=="My Company") | .id') postiz integrations:list --group "$GROUP_ID" ``` ## Getting Settings Each platform has its own settings schema with character limits, required fields, and available options. Retrieve it with: ```bash theme={null} postiz integrations:settings ``` The response tells you: * What fields are available (title, privacy level, subreddit, etc.) * Which fields are required * Character limits and validation rules * Available dynamic tools you can trigger Always check `integrations:settings` before posting to a new platform to understand what settings are available. ## Triggering Tools Some platforms expose dynamic tools — for example, fetching Reddit flairs, YouTube playlists, or LinkedIn company pages. These return data you need when constructing platform-specific settings. ```bash theme={null} postiz integrations:trigger ``` Pass additional data with `-d`: ```bash theme={null} postiz integrations:trigger -d '{"key":"value"}' ``` ### Examples **Get Reddit flairs for a subreddit:** ```bash theme={null} postiz integrations:trigger reddit-id getFlairs -d '{"subreddit":"programming"}' ``` **Get YouTube playlists:** ```bash theme={null} postiz integrations:trigger youtube-id getPlaylists ``` **Get LinkedIn company pages:** ```bash theme={null} postiz integrations:trigger linkedin-id getCompanies ``` **Get Pinterest boards:** ```bash theme={null} postiz integrations:trigger pinterest-id getBoards ``` **Search Instagram audio for a Reel** (Facebook Business-linked channels only, empty `q` returns trending audio): ```bash theme={null} postiz integrations:trigger instagram-id audioSearch -d '{"q":"summer vibes","type":"music"}' ``` ## Discovery Workflow When working with a new platform, follow this workflow: ```bash theme={null} # 1. Find the integration ID INTEGRATION_ID=$(postiz integrations:list | jq -r '.[] | select(.identifier=="reddit") | .id') # 2. Check what settings and tools are available postiz integrations:settings "$INTEGRATION_ID" # 3. Use tools to fetch dynamic data (e.g., flairs) postiz integrations:trigger "$INTEGRATION_ID" getFlairs -d '{"subreddit":"programming"}' # 4. Create a post with the discovered settings postiz posts:create \ -c "My post" \ -s "2025-01-15T10:00:00Z" \ --settings '{"subreddit":[{"value":{"subreddit":"programming","title":"Post Title","type":"text"}}]}' \ -i "$INTEGRATION_ID" ``` # Introduction Source: https://docs.postiz.com/cli/introduction Automate social media posting from the command line with the Postiz CLI Create AI-powered UGC videos for your social media with [Agent Media](https://agent-media.ai) — generate engaging video content and schedule it directly with Postiz. Perfect for OpenClaw 🦞 For your AI agent to work best with Postiz, install the skill by running: ```bash theme={null} npx skills add gitroomhq/postiz-agent ``` Or load the SKILL md file from [github.com/gitroomhq/postiz-agent](https://github.com/gitroomhq/postiz-agent). The Postiz CLI is a command-line tool for automating social media posting across 28+ platforms. It wraps the [Public API](/public-api/introduction) so you can schedule posts, manage integrations, and upload media directly from your terminal or shell scripts. ## Installation ```bash theme={null} npm install -g postiz ``` ```bash theme={null} pnpm install -g postiz ``` Verify the installation: ```bash theme={null} postiz --help ``` ## Authentication ### Option 1: OAuth2 (Recommended) Authenticate using the device flow — no client ID or secret needed: ```bash theme={null} postiz auth:login ``` This will: 1. Display a one-time code in your terminal 2. Open your browser to authorize 3. Automatically save credentials to `~/.postiz/credentials.json` ```bash theme={null} # Check current auth status postiz auth:status # Remove stored credentials postiz auth:logout ``` ### Option 2: API Key Set your Postiz API key as an environment variable. You can get your API key from the Postiz Settings page. ```bash theme={null} export POSTIZ_API_KEY=your_api_key_here ``` Add this to your shell profile (`~/.bashrc`, `~/.zshrc`, etc.) so it persists across sessions. OAuth2 credentials take priority over the API key when both are present. ### Custom API URL (self-hosted) If you're running a self-hosted Postiz instance, point the CLI to your server: ```bash theme={null} export POSTIZ_API_URL=https://your-postiz-server.com ``` ### Self-Hosting the Auth Server By default, `postiz auth:login` uses the hosted auth server at `cli-auth.postiz.com`. If you want to self-host the OAuth2 device flow server, see the [Authentication](/cli/authentication) page for the full setup guide. ## Quick Start ```bash theme={null} # 1. List your connected social media accounts postiz integrations:list # 2. Create a scheduled post postiz posts:create \ -c "Hello from the Postiz CLI!" \ -s "2025-01-15T10:00:00Z" \ -i "your-integration-id" # 3. List your scheduled posts postiz posts:list ``` ## Commands Overview | Command | Description | | ----------------------- | ----------------------------------------------------------------------- | | `auth:login` | Authenticate via OAuth2 device flow | | `auth:status` | Check current authentication status | | `auth:logout` | Remove stored credentials | | `integrations:list` | List all connected social media accounts | | `integrations:settings` | Get the settings schema for an integration | | `integrations:trigger` | Trigger a dynamic tool on an integration | | `posts:create` | Create a new post | | `posts:list` | List posts with optional date filtering | | `posts:delete` | Delete a post by ID | | `posts:missing` | List available content from provider for a post with missing release ID | | `posts:connect` | Connect a post to its published content by release ID | | `analytics:platform` | Get analytics for an integration/channel | | `analytics:post` | Get analytics for a specific post | | `upload` | Upload a media file | All commands output JSON, making the CLI easy to use in scripts and automation pipelines. # Managing Posts Source: https://docs.postiz.com/cli/managing-posts Create, list, and delete social media posts from the command line ## Creating Posts Use `posts:create` to schedule or draft posts to one or more platforms. ### Simple Post ```bash theme={null} postiz posts:create \ -c "Hello world!" \ -s "2025-01-15T10:00:00Z" \ -i "your-integration-id" ``` ### Options | Flag | Description | | -------------------- | ------------------------------------------------------ | | `-c, --content` | Post content. Use multiple times for threads/comments. | | `-s, --date` | Schedule date in ISO 8601 format (required) | | `-t, --type` | `schedule` (default) or `draft` | | `-m, --media` | Comma-separated media URLs (use after uploading) | | `-i, --integrations` | Comma-separated integration IDs (required) | | `-d, --delay` | Delay between comments in milliseconds (default: 5000) | | `--settings` | Platform-specific settings as JSON | | `-j, --json` | Path to a JSON file for complex posts | ### Draft Post ```bash theme={null} postiz posts:create \ -c "Review this before publishing" \ -s "2025-01-15T10:00:00Z" \ -t draft \ -i "your-integration-id" ``` ### Post with Media Upload your media first with the [`upload`](/cli/media-upload) command, then reference the returned URL: ```bash theme={null} postiz posts:create \ -c "Check out this photo!" \ -m "https://uploads.postiz.com/your-image.jpg" \ -s "2025-01-15T10:00:00Z" \ -i "your-integration-id" ``` ### Threads and Comments Pass `-c` multiple times to create a thread. Each comment can have its own media with a corresponding `-m` flag: ```bash theme={null} postiz posts:create \ -c "Thread 1/3" -m "image1.jpg" \ -c "Thread 2/3" -m "image2.jpg" \ -c "Thread 3/3" \ -s "2025-01-15T10:00:00Z" \ -i "twitter-integration-id" ``` Use `-d` to control the delay between comments (in milliseconds): ```bash theme={null} postiz posts:create \ -c "First tweet" \ -c "Second tweet" \ -c "Third tweet" \ -s "2025-01-15T10:00:00Z" \ -d 2000 \ -i "twitter-integration-id" ``` ### Multi-Platform Post Send the same content to multiple platforms by passing comma-separated integration IDs: ```bash theme={null} postiz posts:create \ -c "Posting everywhere!" \ -s "2025-01-15T10:00:00Z" \ -i "twitter-id,linkedin-id,facebook-id" ``` ### Platform-Specific Settings Some platforms require additional settings. Pass them as JSON with `--settings`: ```bash theme={null} postiz posts:create \ -c "Check out this discussion" \ -s "2025-01-15T10:00:00Z" \ --settings '{"subreddit":[{"value":{"subreddit":"programming","title":"My Post","type":"text"}}]}' \ -i "reddit-integration-id" ``` Use `postiz integrations:settings ` to discover what settings are available for each platform. See [Integrations](/cli/integrations) for details. ### Complex Posts with JSON For posts with detailed platform-specific content, use a JSON file: ```bash theme={null} postiz posts:create --json post.json ``` Example `post.json`: ```json theme={null} { "integrations": ["twitter-123", "linkedin-456"], "posts": [ { "provider": "twitter", "post": [{ "content": "Short tweet version", "image": [] }] }, { "provider": "linkedin", "post": [{ "content": "Longer LinkedIn version with more detail", "image": [] }], "settings": { "__type": "linkedin" } } ] } ``` ## Listing Posts ```bash theme={null} postiz posts:list ``` ### Filter by Date Range ```bash theme={null} postiz posts:list \ --startDate "2025-01-01T00:00:00Z" \ --endDate "2025-01-31T23:59:59Z" ``` ### Filter by Customer ```bash theme={null} postiz posts:list --customer "customer-id" ``` ## Connecting Missing Posts Some platforms don't return a post ID immediately after publishing (the `releaseId` is set to `"missing"`). When this happens, you can fetch recent content from the provider and connect the correct one to your post. This enables analytics tracking. ### List Available Content ```bash theme={null} postiz posts:missing ``` Returns an array of recent content items from the provider with their ID and thumbnail URL: ```json theme={null} [ { "id": "7321456789012345678", "url": "https://p16-sign.tiktokcdn-us.com/obj/cover-image.jpeg" }, { "id": "7321456789012345679", "url": "https://p16-sign.tiktokcdn-us.com/obj/cover-image2.jpeg" } ] ``` This only works for posts where the `releaseId` is `"missing"`. Returns an empty array if the provider doesn't support this feature. ### Connect a Post Once you've identified the correct content, update the release ID: ```bash theme={null} postiz posts:connect --release-id "7321456789012345678" ``` After connecting, the post will support full analytics via `postiz analytics:post`. ### Full Workflow ```bash theme={null} # 1. Find posts with missing release IDs postiz posts:list | jq '.posts[] | select(.releaseId == "missing") | {id, content}' # 2. Get available content from the provider postiz posts:missing # 3. Connect the correct content postiz posts:connect --release-id "7321456789012345678" # 4. Verify analytics now work postiz analytics:post ``` ## Changing Post Status Move a post between `draft` and `schedule` without changing its date. ```bash theme={null} postiz posts:status --status draft postiz posts:status --status schedule ``` * `--status schedule` promotes a draft into the publishing queue and (re)starts the workflow so it will publish at its stored date. * `--status draft` moves a scheduled post back to draft **and terminates any running publishing workflow**, so it will not publish. Use this when you want to pause a scheduled post without deleting it, or hand a draft off to the scheduler once it's ready. ## Deleting Posts ```bash theme={null} postiz posts:delete ``` # Media Upload Source: https://docs.postiz.com/cli/media-upload Upload images, videos, and other media files for use in posts ## Uploading Files Upload a local file and receive a URL you can use in posts: ```bash theme={null} postiz upload ``` The command returns a JSON response with the uploaded file's URL: ```json theme={null} { "id": "img-123", "path": "https://uploads.postiz.com/your-file.jpg" } ``` You must upload media files to Postiz before using them in posts. Many platforms (TikTok, Instagram, YouTube) require verified URLs and will reject external links. ## Upload and Post Workflow ```bash theme={null} # 1. Upload the file RESULT=$(postiz upload photo.jpg) FILE_URL=$(echo "$RESULT" | jq -r '.path') # 2. Use the URL in a post postiz posts:create \ -c "Check out this photo!" \ -m "$FILE_URL" \ -s "2025-01-15T10:00:00Z" \ -i "your-integration-id" ``` ## Supported File Types PNG, JPG, JPEG, GIF, WEBP, SVG, BMP, ICO MP4, MOV, AVI, MKV, WEBM, FLV, WMV, M4V, MPEG, 3GP MP3, WAV, OGG, AAC, FLAC, M4A PDF, DOC, DOCX ## Video Upload Example Platforms like TikTok, YouTube, and Instagram require video uploads through Postiz: ```bash theme={null} # Upload the video VIDEO=$(postiz upload video.mp4) VIDEO_URL=$(echo "$VIDEO" | jq -r '.path') # Post to TikTok postiz posts:create \ -c "New video! #fyp" \ -m "$VIDEO_URL" \ -s "2025-01-15T10:00:00Z" \ --settings '{"privacy_level":"PUBLIC_TO_EVERYONE"}' \ -i "tiktok-integration-id" ``` `privacy_level` and the other TikTok settings apply only when `content_posting_method` is `"DIRECT_POST"`, with `"UPLOAD"` (send to the TikTok app inbox instead of publishing) TikTok keeps only the post content. # Platform Examples Source: https://docs.postiz.com/cli/platform-examples Ready-to-use examples for posting to specific platforms ## X (Twitter) ### Simple Post ```bash theme={null} postiz posts:create \ -c "Hello Twitter!" \ -s "2025-01-15T10:00:00Z" \ -i "twitter-id" ``` ### Thread ```bash theme={null} postiz posts:create \ -c "Thread 1/3: Introduction" \ -c "Thread 2/3: Main point" \ -c "Thread 3/3: Conclusion" \ -s "2025-01-15T10:00:00Z" \ -d 2000 \ -i "twitter-id" ``` ### With Reply Controls ```bash theme={null} postiz posts:create \ -c "Only followers can reply to this" \ -s "2025-01-15T10:00:00Z" \ --settings '{"who_can_reply_post":"followers"}' \ -i "twitter-id" ``` ## Reddit ### Post with Flair ```bash theme={null} # 1. Get available flairs postiz integrations:trigger reddit-id getFlairs -d '{"subreddit":"programming"}' # 2. Post with a flair postiz posts:create \ -c "My post content" \ -s "2025-01-15T10:00:00Z" \ --settings '{"subreddit":[{"value":{"subreddit":"programming","title":"Post Title","type":"text","flair":{"id":"flair-id","name":"Discussion"}}}]}' \ -i "reddit-id" ``` ### Scripted Workflow ```bash theme={null} #!/bin/bash REDDIT_ID=$(postiz integrations:list | jq -r '.[] | select(.identifier=="reddit") | .id') FLAIRS=$(postiz integrations:trigger "$REDDIT_ID" getFlairs -d '{"subreddit":"programming"}') FLAIR_ID=$(echo "$FLAIRS" | jq -r '.output[0].id') postiz posts:create \ -c "Automated Reddit post" \ -s "2025-01-15T10:00:00Z" \ --settings "{\"subreddit\":[{\"value\":{\"subreddit\":\"programming\",\"title\":\"Post Title\",\"type\":\"text\",\"flair\":{\"id\":\"$FLAIR_ID\"}}}]}" \ -i "$REDDIT_ID" ``` ## YouTube ```bash theme={null} # Upload video first VIDEO=$(postiz upload video.mp4) VIDEO_URL=$(echo "$VIDEO" | jq -r '.path') postiz posts:create \ -c "Video description here" \ -m "$VIDEO_URL" \ -s "2025-01-15T10:00:00Z" \ --settings '{"title":"My Video Title","type":"public","tags":[{"value":"tech","label":"Tech"}]}' \ -i "youtube-id" ``` ## TikTok ```bash theme={null} # Upload video first VIDEO=$(postiz upload video.mp4) VIDEO_URL=$(echo "$VIDEO" | jq -r '.path') postiz posts:create \ -c "Check this out! #fyp" \ -m "$VIDEO_URL" \ -s "2025-01-15T10:00:00Z" \ --settings '{"privacy_level":"PUBLIC_TO_EVERYONE","duet":true,"stitch":true}' \ -i "tiktok-id" ``` TikTok applies these settings only when `content_posting_method` is `"DIRECT_POST"`, with `"UPLOAD"` (send to the TikTok app inbox instead of publishing) every setting except the post content is silently discarded. `duet` and `stitch` apply to video posts only. ## Instagram ```bash theme={null} # Upload image first IMAGE=$(postiz upload photo.jpg) IMAGE_URL=$(echo "$IMAGE" | jq -r '.path') # Regular post postiz posts:create \ -c "Beautiful day! #photography" \ -m "$IMAGE_URL" \ -s "2025-01-15T10:00:00Z" \ --settings '{"post_type":"post"}' \ -i "instagram-id" ``` ### Story ```bash theme={null} postiz posts:create \ -c "Story content" \ -m "$IMAGE_URL" \ -s "2025-01-15T10:00:00Z" \ --settings '{"post_type":"story"}' \ -i "instagram-id" ``` ### Reel A single video with `post_type: "post"` is published as a Reel: ```bash theme={null} postiz posts:create \ -c "Reel caption" \ -m "$VIDEO_URL" \ -s "2025-01-15T10:00:00Z" \ --settings '{"post_type":"post"}' \ -i "instagram-id" ``` ### Reel with Audio Search the Instagram audio catalog and attach a track to the Reel (Facebook Business-linked channels only — an empty `q` returns trending audio): ```bash theme={null} # Find an audio ID AUDIO_ID=$(postiz integrations:trigger instagram-id audioSearch \ -d '{"q":"summer vibes","type":"music"}' | jq -r '.output[0].id') postiz posts:create \ -c "Reel with trending audio" \ -m "$VIDEO_URL" \ -s "2025-01-15T10:00:00Z" \ --settings "{\"post_type\":\"post\",\"audio\":{\"id\":\"$AUDIO_ID\",\"audio_volume\":80,\"video_volume\":20}}" \ -i "instagram-id" ``` ## LinkedIn ```bash theme={null} postiz posts:create \ -c "Professional update on LinkedIn" \ -s "2025-01-15T10:00:00Z" \ -i "linkedin-id" ``` ### Image Carousel ```bash theme={null} postiz posts:create \ -c "Check out these slides!" \ -m "image1.jpg,image2.jpg,image3.jpg" \ -s "2025-01-15T10:00:00Z" \ --settings '{"post_as_images_carousel":true}' \ -i "linkedin-id" ``` ## Pinterest ```bash theme={null} postiz posts:create \ -c "Pin description" \ -m "$IMAGE_URL" \ -s "2025-01-15T10:00:00Z" \ --settings '{"board":"board-id","title":"Pin Title","link":"https://example.com"}' \ -i "pinterest-id" ``` ## Discord ```bash theme={null} postiz posts:create \ -c "Message to Discord" \ -s "2025-01-15T10:00:00Z" \ --settings '{"channel":"channel-id"}' \ -i "discord-id" ``` ## Batch Scheduling Schedule multiple posts across different dates: ```bash theme={null} #!/bin/bash DATES=("2025-01-14T09:00:00Z" "2025-01-15T09:00:00Z" "2025-01-16T09:00:00Z") CONTENT=("Monday motivation" "Tuesday tips" "Wednesday wisdom") for i in "${!DATES[@]}"; do postiz posts:create \ -c "${CONTENT[$i]}" \ -s "${DATES[$i]}" \ -i "twitter-id" done ``` ## Multi-Platform Campaign Post different content per platform in one command using a JSON file: ```bash theme={null} postiz posts:create --json campaign.json ``` Example `campaign.json`: ```json theme={null} { "integrations": ["twitter-123", "linkedin-456", "reddit-789"], "posts": [ { "provider": "twitter", "post": [{ "content": "Short tweet version", "image": [] }] }, { "provider": "linkedin", "post": [{ "content": "More detailed LinkedIn post with professional tone", "image": [] }] }, { "provider": "reddit", "post": [{ "content": "Reddit post body", "image": [] }], "settings": { "__type": "reddit", "subreddit": [{ "value": { "subreddit": "programming", "title": "Post Title", "type": "text" } }] } } ] } ``` # Chrome Extension Source: https://docs.postiz.com/configuration/chrome-extension Set up the Postiz browser extension for cookie-based integrations Some platforms (like Skool) do not offer public OAuth APIs. Postiz connects to these platforms using a browser extension that securely extracts session cookies from your browser. ## How It Works 1. You install the Postiz Chrome Extension in your browser. 2. When adding a cookie-based channel, the extension reads your session cookies for that platform. 3. The cookies are sent to your Postiz backend and stored securely as an encrypted JWT. 4. The extension automatically refreshes cookies every 24 hours to keep connections alive. Using cookies to interact with platforms may violate their terms of service. Use this feature at your own risk. ## Installation Install the Postiz browser extension from the [Chrome Web Store](https://chromewebstore.google.com/detail/postiz/cidhffagahknaeodkplfbcpfeielnkjl?hl=en). Alternatively, for self-hosted setups, you can build the extension from source: ```bash theme={null} cd apps/extension pnpm build ``` Then load the `apps/extension/dist` folder as an unpacked extension in Chrome: 1. Navigate to `chrome://extensions/` 2. Enable **Developer mode** (top right toggle) 3. Click **Load unpacked** and select the `dist` folder After installing the extension, you need its **Extension ID**: 1. Go to `chrome://extensions/` in Chrome 2. Find the **Postiz** extension 3. Copy the **ID** shown under the extension name (e.g., `cidhffagahknaeodkplfbcpfeielnkjl`) If you installed from the Chrome Web Store, the ID is: `cidhffagahknaeodkplfbcpfeielnkjl` Add the extension ID to your Postiz environment variables: ```env theme={null} EXTENSION_ID="cidhffagahknaeodkplfbcpfeielnkjl" ``` Restart Postiz after setting this variable. ## Supported Platforms The following platforms use the Chrome Extension for authentication: Post to Skool communities ## Self-Hosted Considerations If you are self-hosting Postiz on a domain other than `localhost` or `*.postiz.com`, you need to build a custom extension with your domain in the `externally_connectable` manifest field. Edit `apps/extension/manifest.json` and add your domain: ```json theme={null} { "externally_connectable": { "matches": [ "http://localhost/*", "https://localhost/*", "https://*.postiz.com/*", "https://your-domain.com/*" ] } } ``` Then rebuild the extension and load it as an unpacked extension in Chrome. ## Troubleshooting * **"Extension not found"** — Make sure the `EXTENSION_ID` environment variable is set and Postiz has been restarted. * **"Extension not reachable"** — The extension may be disabled. Check `chrome://extensions/` and make sure Postiz is enabled. * **"Could not get cookies"** — You must be logged in to the platform in the same Chrome browser where the extension is installed. * **Cookies expire** — The extension automatically refreshes cookies every 24 hours. If a connection drops, try reconnecting the channel. # How to add a new provider Source: https://docs.postiz.com/configuration/create-provider How to add a new provider to Postiz # Steps to implement a new provider 1. **The backend logic:** * Define DTO for the settings of the provider * Generate an authentication URL * Authenticate the user from the callback * Refresh the user token 2. **The frontend logic:** * Implement the settings page * Implement the preview page * Upload the provider image ## Social Media ### Backend For our example, we will use the X provider. Head over to `nestjs-libraries/src/dtos/posts/providers-settings` and create a new file `x-provider-settings.dto.ts` You don't have to create a DTO if there are no settings Once created head over to `nestjs-libraries/src/dtos/posts/providers-settings/all.providers.settings.ts` and add the new DTO. Head to `libraries/nestjs-libraries/src/dtos/posts/create.post.dto.ts`, look for the discriminator and add another line in the format of: ```typescript theme={null} { value: DTOClassName, name: 'providerName' }, ``` Head over to `libraries/nestjs-libraries/src/integrations/social` and create a new provider file `providerName.provider.ts` For oAuth2 providers, the content of the file should look like this: ```typescript theme={null} import { AuthTokenDetails, PostDetails, PostResponse, SocialProvider, } from '@gitroom/nestjs-libraries/integrations/social/social.integrations.interface'; export class XProvider implements SocialProvider { identifier = 'providerName'; name = 'Provider Name'; async refreshToken(refreshToken: string): Promise { // ...refresh the token } async generateAuthUrl() { // ...generate the auth url } async authenticate(params: { code: string; codeVerifier: string }) { // ...authenticate the user } async post( id: string, accessToken: string, postDetails: PostDetails[] ): Promise { // ...post the content } } ``` Take a look at the existing providers to see how to implement the methods. Open `libraries/nestjs-libraries/src/integrations/integration.manager.ts` and add the new provider to either `socialIntegrationList` (oAuth2) or `articleIntegrationList` (Token) ### Custom functions You might want to create custom functions for the providers for example: get available orgs, get available pages, etc. You can create a public function in the provider for example `organizations` and later call it from a special hook from the frontend. *** ### Frontend Head over to `apps/frontend/src/components/launches/providers` and create a new folder with the providerName. Add a new file `providerName.provider.tsx` with the following content: ```typescript theme={null} import { FC } from 'react'; import { withProvider } from '@gitroom/frontend/components/launches/providers/high.order.provider'; import { useSettings } from '@gitroom/frontend/components/launches/helpers/use.values'; import { useIntegration } from '@gitroom/frontend/components/launches/helpers/use.integration'; const ProviderPreview: FC = () => { const { value } = useIntegration(); const settings = useSettings(); return ( // ...Preview ); }; const ProviderSettings: FC = () => { const form = useSettings(); const { date } = useIntegration(); return ( // ...Settings ); }; export default withProvider(DevtoSettings, DevtoPreview, DTOClassName); ``` If you want to use a custom function for the provider you can use the `useCustomProviderFunction` hook. ```typescript theme={null} import { useCustomProviderFunction } from '@gitroom/frontend/components/launches/helpers/use.custom.provider.function'; import { useCallback } from 'react'; const customFunc = useCustomProviderFunction(); // and use it like that: const getOrgs = useCallback(() => { customFunc.get('organizations', { anyKey: 'anyValue' }) }, []); ``` It will automatically interact with the right provider saved for the user. You can look at the other integrations to understand what data to put inside. Open `apps/frontend/src/components/launches/providers/show.all.providers.tsx` and add the new provider to the list. ```typescript theme={null} {identifier: 'providerName', component: DefaultImportFromHighOrderProvider}, ``` # Docker Compose Configuration Source: https://docs.postiz.com/configuration/docker How to configure your docker-compose file for Postiz You will often see, when for example configuring providers, that the environment variables will look like this: ```env theme={null} INSTAGRAM_CLIENT_ID=12345678901234567890 ``` You have 2 options on how to set these variables in your `docker-compose.yml` file. ## Option 1: Direct in docker-compose.yml You can set them directly in the `environment` section of the service. ```yaml theme={null} services: postiz: environment: YOUR_ENV_VAR: "value" YOUR_OTHER_ENV_VAR: "value" ``` ## Option 2: Using a .env file You can use a `.env` file to set the variables. **docker-compose.yml:** ```yaml theme={null} services: postiz: env_file: - .env ``` **.env:** ```env theme={null} YOUR_ENV_VAR=value YOUR_OTHER_ENV_VAR=value ``` ## Option 3: Combine both You can also use both! **docker-compose.yml:** ```yaml theme={null} services: postiz: environment: YOUR_ENV_VAR: "value" env_file: - .env ``` **.env:** ```env theme={null} YOUR_OTHER_ENV_VAR=value ``` When using an .env file, you will need to transfer all environment variables from the docker-compose.yml file to the .env file. An .env file will override any variables set in the .yml file. Using an .env file for the DB / Redis won't be necessary. # Email Notifications Source: https://docs.postiz.com/configuration/emails How to send notifications to users Postiz supports two email providers: Resend and NodeMailer (SMTP). If you have an email provider configured, then new users will require activation. ```env theme={null} EMAIL_PROVIDER: "resend|nodemailer" ``` You must also set the sender name and email address for all providers as follows; ```env theme={null} EMAIL_FROM_NAME: "Postiz Emailer" EMAIL_FROM_ADDRESS: "postiz@example.com" ``` ## Resend Postiz uses Resend to send email notifications to users. If this key is set, users will also require activation. Register to [Resend](https://resend.com), and connect your domain. Copy your API Key from the Resend control panel. Open the .env file and edit the following line. ```env theme={null} EMAIL_PROVIDER="resend" RESEND_API_KEY="" ``` ## NodeMailer (SMTP) This is an alternative to Resend. You can use NodeMailer, which is simply a SMTP library, to connect to any SMTP server. ```env theme={null} EMAIL_PROVIDER: "nodemailer" EMAIL_HOST: "smtp.gmail.com" # smtp host if you choose nodemailer EMAIL_PORT: "465" # smtp port if you choose nodemailer EMAIL_SECURE: "true" # smtp secure if you choose nodemailer EMAIL_USER: "user" # smtp user if you choose nodemailer EMAIL_PASS: "pass" # smtp pass if you choose nodemailer ``` # OIDC Configuration Source: https://docs.postiz.com/configuration/oauth How to configure OIDC for Postiz **Warning:** With the actual implementation of the OIDC provider, GitHub / Google login provider will be disabled. If you want to use OAuth/OIDC, please follow the instructions below. We will use [Authentik](https://goauthentik.io/) as an OIDC provider example, with base URL `https://authentik.example.com` You will find the following important information: * `redirect_uri` => `https://postiz.yourserver.com/settings` * `client_id` => `randomclientid` * `client_secret` => `randomclientsecret` * `auth_url` => `https://authentik.example.com/application/o/authorize` * `token_url` => `https://authentik.example.com/application/o/token` * `userinfo_url`=> `https://authentik.example.com/application/o/userinfo` The same information needs to be configured on other OIDC providers such as Keycloak, Dex, etc. ```env theme={null} POSTIZ_GENERIC_OAUTH="true" ``` Set to `true` to enable OIDC login. ```env theme={null} NEXT_PUBLIC_POSTIZ_OAUTH_DISPLAY_NAME="Authentik" ``` Will display the name of the OIDC provider on the login page. ```env theme={null} NEXT_PUBLIC_POSTIZ_OAUTH_LOGO_URL="https://raw.githubusercontent.com/walkxcode/dashboard-icons/master/png/authentik.png" ``` Will display the logo of the OIDC provider on the login page button. ```env theme={null} POSTIZ_OAUTH_URL="https://authentik.example.com" ``` The base URL of the OIDC provider. ```env theme={null} POSTIZ_OAUTH_AUTH_URL="https://authentik.example.com/application/o/authorize/" ``` The authorization URL of the OIDC provider. ```env theme={null} POSTIZ_OAUTH_TOKEN_URL="https://authentik.example.com/application/o/token/" ``` The token URL of the OIDC provider. ```env theme={null} POSTIZ_OAUTH_USERINFO_URL="https://authentik.example.com/application/o/userinfo/" ``` The userinfo URL of the OIDC provider. ```env theme={null} POSTIZ_OAUTH_CLIENT_ID="randomclientid" ``` The client ID of the OIDC provider. ```env theme={null} POSTIZ_OAUTH_CLIENT_SECRET="randomclientsecret" ``` The client secret of the OIDC provider. # Image & Video Editing (Polotno) Source: https://docs.postiz.com/configuration/polotno Enable in-app image and video editing in Postiz via the Polotno SDK Postiz supports in-app image and video editing through the [Polotno SDK](https://polotno.com/). Once enabled, users can design and edit visuals directly inside the Postiz workflow while preparing scheduled posts — no round-trip to an external editor. ## What you get * Create and edit visuals inside the Postiz post composer. * Work with templates, text, images, and brand assets. * Update designs in place without re-uploading finished files. * Reuse and modify visuals across scheduled posts. Designs are stored as structured data and rendered when needed. Scheduling, publishing, and platform-specific logic remain handled by Postiz. ## Setup Sign up at [polotno.com](https://polotno.com/) and open the [API dashboard](https://polotno.com/cabinet/) to generate an API key. Set `NEXT_PUBLIC_POLOTNO` in your environment to the key from the Polotno dashboard: ```env theme={null} NEXT_PUBLIC_POLOTNO="your-polotno-api-key" ``` In `docker-compose.yaml`: ```yaml theme={null} services: postiz: environment: NEXT_PUBLIC_POLOTNO: "your-polotno-api-key" ``` `NEXT_PUBLIC_POLOTNO` is read by the frontend at build time, so you need to rebuild the frontend image (or restart the dev server) for the change to take effect. Exact steps may vary slightly depending on your deployment — see [Docker Compose](/installation/docker-compose) or [Development](/installation/development) for environment-specific notes. If the variable is unset, the editor falls back to a demo/anonymous mode that is not suitable for production. ## Licensing Polotno is a commercial SDK and requires a valid license for production use. * Licenses are purchased and managed directly through Polotno. * Postiz does not bundle, resell, or proxy Polotno licenses. * Billing, licensing, and usage terms are handled by Polotno. Postiz users can use the coupon code **`postizfriends`** at checkout to receive **\$100 off** a Polotno license. ## Reference * Env var: [`NEXT_PUBLIC_POLOTNO`](/configuration/reference#misc-frontend) * Polotno docs: [polotno.com/docs](https://polotno.com/docs/) * Polotno pricing: [polotno.com/pricing](https://polotno.com/pricing/) # R2 Configuration Source: https://docs.postiz.com/configuration/r2 How to use Cloudflare R2 for file storage If you do not wish to (or can't) use local storage, an alternative way to upload images is to configure R2. It's free. Go to the [Cloudflare Dashboard](https://dash.cloudflare.com/r2/overview), and register if needed, then login. In the dashboard sidebar, and head to the R2 page. R2 Page Create a new Bucket. * Choose Automatic * Choose Standard Create Bucket Create your R2 Token by going to R2 Object Storage: R2 Object Storage Click on the API dropdown, and select [Manage API tokens](https://dash.cloudflare.com/?to=/:account/r2/api-tokens): Manage API tokens Copy your Account ID for later, and click on "Create an API token": Create API Token Create an Account API token: Account API Token Under "Permissions" choose "Object Read & Write" and under "Specify bucket(s)" search for your created Bucket. Permissions After the R2 Token is created, copy your "Access Key ID" and "Secret Access Key": Copy Credentials Paste the respective information into your .env environment. ```env theme={null} CLOUDFLARE_ACCOUNT_ID="accountId" CLOUDFLARE_ACCESS_KEY="accessKey" CLOUDFLARE_SECRET_ACCESS_KEY="secretAccessKey" CLOUDFLARE_BUCKETNAME="bucketName" CLOUDFLARE_REGION="region (like wnam)" ``` Go to configuration and connect a custom domain (if you don't have one, you can use the one that CloudFlare provides.) Add it to your .env file. ```env theme={null} CLOUDFLARE_BUCKET_URL="https://customdomain.com" ``` Custom Domain Click to edit the CORS policy and add the following JSON: ```json theme={null} [ { "AllowedOrigins": [ "http://localhost:4200", "https://yourDomain.com" ], "AllowedMethods": [ "GET", "POST", "HEAD", "PUT", "DELETE" ], "AllowedHeaders": [ "Authorization", "x-amz-date", "x-amz-content-sha256", "content-type" ], "ExposeHeaders": [ "ETag", "Location" ], "MaxAgeSeconds": 3600 } ] ``` CORS Policy # Configuration Reference Source: https://docs.postiz.com/configuration/reference Environment variables reference for Postiz Postiz is configured entirely through environment variables. Any change requires an application restart. The canonical list lives in the [example postiz.env file](https://raw.githubusercontent.com/gitroomhq/postiz-app/main/.env.example). This page documents every variable Postiz reads, grouped by purpose. Variables marked **Required** are validated on boot — Postiz will fail to start if they're missing or malformed. ## Required core These six variables are non-optional for any deployment. ### `DATABASE_URL` Required PostgreSQL connection string used by Prisma. ``` DATABASE_URL="postgresql://postiz-user:postiz-password@localhost:5432/postiz-db-local" ``` ### `REDIS_URL` Required Redis connection string used for queues, rate limiting, and short-lived caches. ``` REDIS_URL="redis://localhost:6379" ``` ### `JWT_SECRET` Required A long random string used to sign session JWTs. Rotating this invalidates every existing session. ### `FRONTEND_URL` Required The URL the **browser** uses to reach the Postiz frontend. Used as the OAuth redirect base and for email links. ``` FRONTEND_URL="https://postiz.example.com" ``` ### `NEXT_PUBLIC_BACKEND_URL` Required The URL the **browser** uses to reach the Postiz backend. ``` NEXT_PUBLIC_BACKEND_URL="https://api.postiz.example.com" ``` ### `BACKEND_INTERNAL_URL` Required The URL the **frontend SSR server** uses to reach the backend from inside your network. If everything runs in the same container/host, this is usually `http://localhost:3000`. *** ## Application behaviour ### `DISABLE_REGISTRATION` Set to `true` to allow only a single user signup and then disable the sign-up page. Useful for self-host where you want full control. This also disables OIDC / OAuth sign-in. ### `API_LIMIT` Per-hour limit on the public-API create-post endpoint. Defaults to `90`. Postiz Cloud uses `100`. Channel and post quotas are tiered separately by plan. ### `RUN_CRON` When set, the backend process runs the scheduled-task workers. Leave unset on API-only instances when workers are deployed separately. ### `RESTRICT_UPLOAD_DOMAINS` Comma-separated allowlist of domains for `/public/v1/upload-from-url`. If set, only URLs whose hostname matches an entry are accepted. ### `DISALLOW_PLUS` When set, blocks the upgrade UI elements pointing to paid plans. Used for self-host deployments that don't want to surface cloud-only billing. ### `IS_GENERAL` Switches the frontend between routes available to the open-source build (`/launches`) and the hosted build (`/analytics`). Set to `"true"` on self-host. Leave unset on Postiz Cloud. ### `DISABLE_IMAGE_COMPRESSION` When truthy, the frontend skips client-side image compression on upload. Set this if you need pixel-exact originals at the cost of larger uploads. ### `DISABLE_SSRF_PROTECTION` When connecting providers that take a self-hosted URL (WordPress, Mastodon, Lemmy, Listmonk, Bluesky PDS, etc.), posting media to such providers, fetching webhooks, or handling `/public/v1/upload-from-url`, Postiz fetches the URL server-side and blocks requests that resolve to private, internal, loopback, or link-local IPs to prevent SSRF. Blocked requests surface as `fetch failed` with `Error: Blocked IP` in the backend or orchestrator logs. Set to `true` to disable the guard. Set it on both the backend and orchestrator containers. This disables SSRF protection globally, not per provider. Only set it if your Postiz instance must reach those services on a trusted private network (e.g. same Docker network, VPC, or homelab LAN) **and** all users of the instance are trusted (single-tenant). Any user who can submit a URL can make the server fetch internal services. Prefer fixing DNS/routing so the hostname resolves to a reachable address. ### `NOT_SECURED` Dev only. Never set in production — it disables security checks that exist for a reason. ### `MAIN_URL` Primary application URL used for absolute links in some emails and SEO metadata. Falls back to `FRONTEND_URL` when not set. ### `EXTENSION_ID` The Chrome Extension ID for cookie-based platform integrations (e.g. Skool). See the [Chrome Extension guide](/configuration/chrome-extension). ### `MOBILE_APP_SCHEME` URL scheme used for deep-linking from emails into the mobile app. *** ## Storage See also: [Cloudflare R2](/configuration/r2) and [Uploads & Storage](/configuration/uploads). | Variable | Purpose | | ------------------------------------- | ---------------------------------------------------------------- | | `STORAGE_PROVIDER` | `local` or `cloudflare`. Defaults to `local`. | | `UPLOAD_DIRECTORY` | Filesystem path for `local` storage writes. | | `NEXT_PUBLIC_UPLOAD_STATIC_DIRECTORY` | Public URL prefix the frontend uses to reference uploaded files. | | `CLOUDFLARE_ACCOUNT_ID` | R2 account ID. | | `CLOUDFLARE_ACCESS_KEY` | R2 access key. | | `CLOUDFLARE_SECRET_ACCESS_KEY` | R2 secret access key. | | `CLOUDFLARE_BUCKETNAME` | R2 bucket name. | | `CLOUDFLARE_BUCKET_URL` | Public-facing URL the bucket is served from. | | `CLOUDFLARE_REGION` | R2 region (usually `auto`). | *** ## Email See also: [Email configuration](/configuration/emails). | Variable | Purpose | | -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `EMAIL_PROVIDER` | `resend` (default) or `nodemailer`. | | `RESEND_API_KEY` | Resend API key — required when `EMAIL_PROVIDER=resend`. Presence of this variable also gates whether user activation is required (set → required; unset → users are auto-activated). | | `EMAIL_HOST` | SMTP host — required when `EMAIL_PROVIDER=nodemailer`. | | `EMAIL_PORT` | SMTP port. | | `EMAIL_USER` | SMTP user. | | `EMAIL_PASS` | SMTP password. | | `EMAIL_SECURE` | `true` for SMTPS / implicit TLS. | | `EMAIL_FROM_ADDRESS` | From-address used on all outgoing email. | | `EMAIL_FROM_NAME` | From-name used on all outgoing email. | *** ## OAuth sign-in (OIDC) See also: [OAuth configuration](/configuration/oauth). | Variable | Purpose | | --------------------------------------- | ------------------------------------------------- | | `POSTIZ_GENERIC_OAUTH` | `true` to enable a generic OIDC sign-in provider. | | `POSTIZ_OAUTH_URL` | Base URL of the IdP. | | `POSTIZ_OAUTH_AUTH_URL` | Authorization endpoint. | | `POSTIZ_OAUTH_TOKEN_URL` | Token endpoint. | | `POSTIZ_OAUTH_USERINFO_URL` | UserInfo endpoint. | | `POSTIZ_OAUTH_CLIENT_ID` | Client ID issued by the IdP. | | `POSTIZ_OAUTH_CLIENT_SECRET` | Client secret issued by the IdP. | | `POSTIZ_OAUTH_SCOPE` | OIDC scope. Defaults to `openid profile email`. | | `NEXT_PUBLIC_POSTIZ_OAUTH_DISPLAY_NAME` | Label shown on the sign-in button. | | `NEXT_PUBLIC_POSTIZ_OAUTH_LOGO_URL` | Icon shown on the sign-in button. | *** ## Temporal (workflow orchestration) Since v2.12.0, Postiz uses Temporal for scheduled posts and background workflows. Self-host deployments need to run a Temporal stack (the official docker-compose ships with one). See the [Temporal migration guide](/installation/migration). | Variable | Purpose | | -------------------- | ------------------------------------------------------ | | `TEMPORAL_ADDRESS` | host:port of the Temporal frontend. | | `TEMPORAL_NAMESPACE` | Temporal namespace to run workflows in. | | `TEMPORAL_API_KEY` | API key for Temporal Cloud. Leave unset for self-host. | | `TEMPORAL_TLS` | `true` to require TLS to the Temporal frontend. | | `ORCHESTRATOR_PORT` | Port the in-process orchestrator binds to. | *** ## Public API & MCP | Variable | Purpose | | --------------------- | --------------------------------------------------------------------- | | `MCP_URL` | URL the frontend uses to advertise the MCP endpoint to clients. | | `AGENT_API_KEY` | Shared secret used by the agent runtime to call privileged endpoints. | | `AGENT_MEDIA_SSO_KEY` | Signing key for short-lived agent-media SSO tokens. | | `BACKEND_URL` | Legacy alias for `NEXT_PUBLIC_BACKEND_URL` in a few server contexts. | *** ## AI / generation | Variable | Purpose | | ---------------------- | -------------------------------------------------------------------- | | `OPENAI_API_KEY` | OpenAI key used for the copilot and AI image generation. | | `OPENAI_APP_CHALLANGE` | Verification challenge string for the OpenAI custom GPT integration. | | `ELEVENSLABS_API_KEY` | ElevenLabs API key for voice generation. | | `FAL_KEY` | fal.ai API key for image/video models. | | `TAVILY_API_KEY` | Tavily search API key used by the research tool. | | `KIEAI_API_KEY` | KieAI API key. | | `TRANSLOADIT_AUTH` | Transloadit auth key for video pipelines. | | `TRANSLOADIT_SECRET` | Transloadit signing secret. | | `TRANSLOADIT_TEMPLATE` | Transloadit template ID used by the video generator. | *** ## Short-link providers If a Postiz user configures short-link replacement, Postiz proxies link shortening through one of the configured providers. Pick one set. ### Dub.co ```env theme={null} DUB_TOKEN="" DUB_API_ENDPOINT="https://api.dub.co" DUB_SHORT_LINK_DOMAIN="dub.sh" ``` ### Short.io ```env theme={null} SHORT_IO_SECRET_KEY="" ``` ### Kutt.it ```env theme={null} KUTT_API_KEY="" KUTT_API_ENDPOINT="https://kutt.it/api/v2" KUTT_SHORT_LINK_DOMAIN="kutt.it" ``` ### LinkDrip ```env theme={null} LINK_DRIP_API_KEY="" LINK_DRIP_API_ENDPOINT="https://api.linkdrip.com/v1/" LINK_DRIP_SHORT_LINK_DOMAIN="dripl.ink" ``` *** ## Payments | Variable | Purpose | | ---------------------------- | ----------------------------------------------------- | | `STRIPE_PUBLISHABLE_KEY` | Stripe publishable key. | | `STRIPE_SECRET_KEY` | Stripe secret key. | | `STRIPE_SIGNING_KEY` | Stripe webhook signing key for subscription events. | | `STRIPE_SIGNING_KEY_CONNECT` | Stripe Connect webhook signing key. | | `STRIPE_DISCOUNT_ID` | Default Stripe discount applied to new subscriptions. | | `FEE_AMOUNT` | Platform fee fraction (e.g. `0.05`). | | `NOWPAYMENTS_API_KEY` | NOWPayments API key for crypto checkout. | | `NOWPAYMENTS_AMOUNT` | Default NOWPayments invoice amount. | *** ## Analytics & tracking (frontend) All of these are optional. Frontend reads `NEXT_PUBLIC_*` at build time. | Variable | Purpose | | ----------------------------- | ----------------------------------------------- | | `NEXT_PUBLIC_SENTRY_DSN` | Frontend Sentry DSN. | | `NEXT_PUBLIC_GTM_ID` | Google Tag Manager container ID. | | `NEXT_PUBLIC_FACEBOOK_PIXEL` | Facebook Pixel ID. | | `FACEBOOK_PIXEL_ACCESS_TOKEN` | Server-side Pixel Conversions API token. | | `NEXT_PUBLIC_POSTHOG_HOST` | PostHog host (e.g. `https://eu.posthog.com`). | | `NEXT_PUBLIC_POSTHOG_KEY` | PostHog project API key. | | `NEXT_PUBLIC_TRACKING_TRIAL` | When set, enables trial-funnel tracking events. | | `DATAFAST_API_KEY` | Datafast analytics API key. | | `DATAFAST_WEBSITE_ID` | Datafast website ID. | | `SENTRY_AUTH_TOKEN` | Build-time Sentry token for sourcemap upload. | | `SENTRY_ORG` | Sentry org slug. | | `SENTRY_PROJECT` | Sentry project slug. | | `SENTRY_SPOTLIGHT` | Enable Sentry Spotlight in dev. | *** ## Misc frontend | Variable | Purpose | | ---------------------------------- | -------------------------------------------------------------------------------------- | | `NEXT_PUBLIC_DISCORD_SUPPORT` | Discord invite URL shown in the support widget. | | `NEXT_PUBLIC_POLOTNO` | Polotno API key for the image editor. | | `NEXT_PUBLIC_VERSION` | Version string shown in the footer. | | `NEXT_PUBLIC_APP_VERSION` | Mobile/web version label. | | `NEXT_PUBLIC_OVERRIDE_BACKEND_URL` | When set, overrides `NEXT_PUBLIC_BACKEND_URL` at runtime. Useful for tunnel-based dev. | *** ## Social provider keys Each social provider has its own env-var block. See the per-provider setup pages under [Providers](/providers/overview) for the exact OAuth steps; this table is just a lookup so you can find which page documents which variable. | Provider | Variables | Setup | | ------------------------- | ---------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------ | | X (Twitter) | `X_API_KEY`, `X_API_SECRET`, `X_URL`, `DISABLE_X_ANALYTICS`, `STRIP_LINKS_FROM_X_POSTS` | [/providers/x-twitter](/providers/x-twitter) | | LinkedIn | `LINKEDIN_CLIENT_ID`, `LINKEDIN_CLIENT_SECRET` | [/providers/linkedin](/providers/linkedin), [/providers/linkedin-page](/providers/linkedin-page) | | Facebook / Instagram (FB) | `FACEBOOK_APP_ID`, `FACEBOOK_APP_SECRET` | [/providers/facebook](/providers/facebook), [/providers/instagram](/providers/instagram) | | Instagram (Standalone) | `INSTAGRAM_APP_ID`, `INSTAGRAM_APP_SECRET` | [/providers/instagram](/providers/instagram) | | Threads | `THREADS_APP_ID`, `THREADS_APP_SECRET` | [/providers/threads](/providers/threads) | | YouTube | `YOUTUBE_CLIENT_ID`, `YOUTUBE_CLIENT_SECRET` | [/providers/youtube](/providers/youtube) | | Google My Business | `GOOGLE_GMB_CLIENT_ID`, `GOOGLE_GMB_CLIENT_SECRET` | [/providers/google-my-business](/providers/google-my-business) | | TikTok | `TIKTOK_CLIENT_ID`, `TIKTOK_CLIENT_SECRET` | [/providers/tiktok](/providers/tiktok) | | Reddit | `REDDIT_CLIENT_ID`, `REDDIT_CLIENT_SECRET` | [/providers/reddit](/providers/reddit) | | Pinterest | `PINTEREST_CLIENT_ID`, `PINTEREST_CLIENT_SECRET` | [/providers/pinterest](/providers/pinterest) | | Discord | `DISCORD_CLIENT_ID`, `DISCORD_CLIENT_SECRET`, `DISCORD_BOT_TOKEN_ID` | [/providers/discord](/providers/discord) | | Slack | `SLACK_ID`, `SLACK_SECRET`, `SLACK_SIGNING_SECRET` | [/providers/slack](/providers/slack) | | Telegram | `TELEGRAM_TOKEN`, `TELEGRAM_BOT_NAME` | [/providers/telegram](/providers/telegram) | | Mastodon | `MASTODON_URL`, `MASTODON_CLIENT_ID`, `MASTODON_CLIENT_SECRET` | [/providers/mastodon](/providers/mastodon) | | Dribbble | `DRIBBBLE_CLIENT_ID`, `DRIBBBLE_CLIENT_SECRET` | [/providers/dribbble](/providers/dribbble) | | Farcaster | `NEYNAR_CLIENT_ID`, `NEYNAR_SECRET_KEY`, `NEYNAR_LOGIN_URL` | [/providers/farcaster](/providers/farcaster) | | MeWe | `MEWE_HOST`, `MEWE_APP_ID`, `MEWE_API_KEY` | [/providers/mewe](/providers/mewe) | | Twitch | `TWITCH_CLIENT_ID`, `TWITCH_CLIENT_SECRET` | API only — see [/public-api/providers/twitch](/public-api/providers/twitch) | | Kick | `KICK_CLIENT_ID`, `KICK_SECRET` | API only — see [/public-api/providers/kick](/public-api/providers/kick) | | VK | `VK_ID` | API only — see [/public-api/providers/vk](/public-api/providers/vk) | | Whop | `WHOP_CLIENT_ID` | [/providers/whop](/providers/whop) | | GitHub (sign-in) | `GITHUB_CLIENT_ID`, `GITHUB_CLIENT_SECRET` | [/configuration/oauth](/configuration/oauth) | | Beehiiv | `BEEHIIVE_API_KEY`, `BEEHIIVE_PUBLICATION_ID` | Newsletter provider | | Listmonk | `LISTMONK_DOMAIN`, `LISTMONK_USER`, `LISTMONK_API_KEY`, `LISTMONK_LIST_ID`, `LISTMONK_WELCOME_TEMPLATE_ID` | Newsletter provider | *** ## Runtime & build These are read from the environment but are typically managed by your runtime, hosting platform, or framework rather than set by hand. | Variable | Purpose | | ----------------------- | ---------------------------------------------------------------------------------------------------------------- | | `PORT` | Port the backend HTTP server binds to. Defaults to `3000`. | | `TZ` | Process timezone. The backend forces this to `UTC` on boot. | | `NODE_ENV` | Standard Node environment flag (`development` / `production`). Toggles dev-only behaviour like sourcemaps. | | `NEXT_RUNTIME` | Set by Next.js (`nodejs` / `edge`) to select the instrumentation hook. Framework-injected — do not set manually. | | `VERCEL_GIT_COMMIT_SHA` | Commit SHA used as the Sentry release tag on the frontend. Injected by Vercel. | | `GITHUB_SHA` | Fallback commit SHA for the Sentry release tag when not on Vercel. Injected by GitHub Actions. | # Uploads & Storage Source: https://docs.postiz.com/configuration/uploads Local filesystem vs Cloudflare R2 for media uploads Postiz writes user-uploaded media (post images, avatars, generated content) through a single storage abstraction. Pick one of two backends. ## Pick a backend ```env theme={null} STORAGE_PROVIDER="local" # default — write to local filesystem # or STORAGE_PROVIDER="cloudflare" # write to Cloudflare R2 ``` ## Local filesystem Set the path where Postiz should write: ```env theme={null} STORAGE_PROVIDER="local" UPLOAD_DIRECTORY="/data/postiz/uploads" NEXT_PUBLIC_UPLOAD_STATIC_DIRECTORY="/uploads" ``` * `UPLOAD_DIRECTORY` — where the backend writes files on disk. * `NEXT_PUBLIC_UPLOAD_STATIC_DIRECTORY` — the URL prefix the frontend uses to reference those files. Default `/uploads`. The Next.js frontend rewrites `/uploads/:path*` to `/api/uploads/:path*` on the backend (only active when `STORAGE_PROVIDER=local`), so the public URL stays `/uploads/...` while the actual file is served from the backend. ### Docker volume mount In `docker-compose.yaml`: ```yaml theme={null} services: postiz: environment: STORAGE_PROVIDER: "local" UPLOAD_DIRECTORY: "/uploads" NEXT_PUBLIC_UPLOAD_STATIC_DIRECTORY: "/uploads" volumes: - postiz-uploads:/uploads volumes: postiz-uploads: ``` If you scale beyond one backend replica, you need a shared volume — or switch to Cloudflare R2. ### Caveat: some providers need public HTTPS URLs TikTok (and a few others) fetch media via "pull from URL" rather than multipart upload. Your local `/uploads` path must therefore be reachable from the public internet over HTTPS for those providers to work. If your deployment is internet-facing through a reverse proxy with TLS, you're fine. If Postiz is on a private network, those providers will fail and you should use [Cloudflare R2](/configuration/r2) or a CDN instead. ## Cloudflare R2 Set `STORAGE_PROVIDER=cloudflare` and configure the R2 credentials. See the dedicated [R2 setup guide](/configuration/r2) for the OAuth and bucket-permissions walkthrough. ```env theme={null} STORAGE_PROVIDER="cloudflare" CLOUDFLARE_ACCOUNT_ID="…" CLOUDFLARE_ACCESS_KEY="…" CLOUDFLARE_SECRET_ACCESS_KEY="…" CLOUDFLARE_BUCKETNAME="…" CLOUDFLARE_BUCKET_URL="https://your-bucket-url.r2.cloudflarestorage.com/" CLOUDFLARE_REGION="auto" ``` R2 gives you public HTTPS URLs out of the box, so the TikTok caveat above doesn't apply. ## Public-API uploads Both `/public/v1/upload` and `/public/v1/upload-from-url` write through the configured `STORAGE_PROVIDER`. The accepted MIME types and body-size limits are documented in [troubleshooting/uploads](/troubleshooting/uploads). # Developer Guide Source: https://docs.postiz.com/developer-guide How to get started developing with Postiz ## Understand how to develop with Postiz ## How to setup your development environment This page explains [How to setup your development environment](/installation/development). ## Architecture Overview Before getting started with development, have a good read of the [architecture overview](/howitworks). This will give you a good understanding of how the project is structured and how the different parts of the project interact with each other. ## Repository Overview Postiz is an open-source project, and the source code is available on [GitHub](https://github.com/gitroomhq/postiz-app). The project is generally built using scripts in the `package.json` file with npm. The main scripts are: * `npm run dev` - Starts the development server * `npm run prisma-generate` - Generates the Prisma client * `npm run prisma-db-push` - Pushes the database schema to the database The entire project is built under [NX](https://nx.dev/) to have a monorepo with multiple projects. Unlike other NX project, this project has one `.env` file that is shared between all the apps. It makes it easier to develop and deploy the project. ### Frontend The frontend is built with [NextJS](https://nextjs.org/) and [TailwindCSS](https://tailwindcss.com/). ### Backend The backend is built with [NestJS](https://nestjs.com/) with a basic architecture of controllers, services, repositories and dtos. It uses [Prisma](https://www.prisma.io/) as an ORM to interact with the database. By default Prisma uses [Postgres](https://www.postgresql.org/) as a database, but it can be easily changed to any other database since there are no native queries. It uses Redis to schedule posts and run background jobs. ### Cron cron is built with [NestJS](https://nestjs.com/) and share components with the backend. ### Worker worker is built with [NestJS](https://nestjs.com/) and share components with the backend. ## Contributors Guide The Postiz [contributors guide](https://github.com/gitroomhq/postiz-app/blob/main/CONTRIBUTING.md) is contained in the main repository. It provides information on how to contribute to the project, mainly the format for how to submit a pull request. # How it works Source: https://docs.postiz.com/howitworks Learn the architecture of the project ## Architecture Postiz is composed of 3 main services and 4 external services - all 3 of the main services typically run within a **single docker container**, and talk to each other through HTTP. Those services talk to other containers running the external services - the SQL Database, Redis, Temporal and Storage. ```mermaid theme={null} flowchart LR; classDef ext fill:#8ED14F,color:black,stroke:#fff classDef svc fill:#9900e6,color:white,stroke:#fff frontend[Frontend Service]:::svc backend[Backend Service]:::svc orchestrator[Orchestrator Service]:::svc temporal[Temporal]:::ext redis[Redis]:::ext db[SQL Database]:::ext storage[Storage]:::ext frontend --> backend backend --> db backend --> redis backend --> temporal temporal --> orchestrator orchestrator --> db orchestrator --> storage backend --> storage ``` * [Frontend](#frontend) - Provides the Web user interface, talks to the Backend. * [Backend](#backend) - Does all the real work, provides an API for the frontend, and triggers workflows via Temporal. * [Orchestrator](#orchestrator) - Runs Temporal workflows and activities, replacing the old cron and worker services. * [Temporal](#temporal) - A durable workflow engine that manages scheduling, retries, and task distribution. * [Redis](#redis) - Used for session state management and caching. * [SQL Database](#db) - Stores all the data, Postgres is typically used, but any SQL database can be used. * [Storage](#storage) - Stores all the files, this used to be CloudFlare R2 as the default, but now it's just a local file system. ### Frontend The frontend is the part that you see, the web interface. It relies on the backend to: * Schedule posts * Show analytics * Manage users ### Backend The backend is the "brain" of Postiz, and coordinates all the work. It triggers Temporal workflows for async operations like posting to social media, sending emails, and refreshing tokens. Typically the SQL database it talks to is Postgres, but other databases can be used. ### Orchestrator The orchestrator replaces the old cron and worker services with Temporal workflows. It handles: * Posting scheduled content to social media platforms. * Refreshing tokens from different social media platforms. * Sending digest and notification emails. * Checking for missing posts and auto-posting. * Tracking user posting streaks. ### Temporal Temporal is a durable workflow execution engine. It provides: * **Reliable scheduling** - Workflows run at the right time with automatic retries on failure. * **Task queues** - Each social platform gets its own task queue for concurrency control. * **Workflow visibility** - A built-in UI for monitoring and debugging workflow execution. * **Durable state** - Workflow state is persisted, so nothing is lost if a service restarts. # Dev Container Source: https://docs.postiz.com/installation/devcontainer Install Postiz using Dev Container ```bash theme={null} npm install -g @devcontainers/cli devcontainer up ``` # Development Environment Source: https://docs.postiz.com/installation/development Set up Postiz for local development This article guides you for local development on Postiz. If you're only looking to self-host, docker-compose is the recommended method. [Docker-Compose](/installation/docker-compose) is the recommended method and now includes the Temporal stack for workflow processing. Important: Postiz uses Temporal for background workflows. If you are upgrading from v2.11.2 to v2.12.0 or later, follow the migration guide at [/installation/migration](/installation/migration) and use the maintained Docker Compose repository which includes the Temporal stack: [/installation/docker-compose](/installation/docker-compose). ## Tested configurations * MacOS * Linux (Fedora 40) Naturally you can use these instructions to setup a development environment on any platform, but there may not be much experience in the community to help you with any issues you may encounter. ### Warning about Windows Several users using Windows (and WSL) have reported issues with the setup. This is not well tested as the main developers of the project do not use Windows/WSL for development. If you are using Windows and encounter issues, please do not try to get support, as we aren't able to support you. ### Prerequisite Local Services * **Node.js** - for running the code! (version 18+) * **PostgreSQL** - or any other SQL database (instructions below suggest Docker) * **Redis** - for handling worker queues (instructions below suggest Docker) * **Temporal** - runs as a separate stack (Postgres + Elasticsearch + Temporal services). For local development run the Temporal stack via the `postiz-docker-compose` repository described in [/installation/docker-compose](/installation/docker-compose). Set `TEMPORAL_ADDRESS` in your `.env` to point at the Temporal service (example below). We have some messages from users who are using Windows, which should work, but they are not tested well yet. ## Installation Instructions ### NodeJS (version 18+) A complete guide of how to install NodeJS can be found [here](https://nodejs.org/en/download/). ### PostgreSQL (or any other SQL database) & Redis You can choose **Option A** to **Option B** to install the database. #### Option A) Postgres and Redis as Single containers You can install [Docker](https://www.docker.com/products/docker-desktop) and run: ```bash theme={null} docker run -e POSTGRES_USER=root -e POSTGRES_PASSWORD=your_password --name postgres -p 5432:5432 -d postgres docker run --name redis -p 6379:6379 -d redis ``` #### Option B) Postgres and Redis as docker-compose Download the [docker-compose.yaml file here](https://raw.githubusercontent.com/gitroomhq/postiz-app/main/docker-compose.dev.yaml), or grab it from the repository in the next step. ```bash theme={null} docker compose -f "docker-compose.dev.yaml" up ``` To run Temporal locally, clone the maintained Docker Compose repository which includes the Temporal stack and follow the instructions in [/installation/docker-compose](/installation/docker-compose). See [/installation/migration](/installation/migration) for migration steps when moving data to the Temporal-enabled setup. ## Build Postiz ```bash theme={null} git clone https://github.com/gitroomhq/postiz-app.git ``` Copy the `.env.example` file to `.env` and fill in the values ```bash theme={null} # Required Settings DATABASE_URL="postgresql://postiz-user:postiz-password@localhost:5432/postiz-db-local" REDIS_URL="redis://localhost:6379" JWT_SECRET="random string for your JWT secret, make it long" FRONTEND_URL="http://localhost:4200" NEXT_PUBLIC_BACKEND_URL="http://localhost:3000" BACKEND_INTERNAL_URL="http://localhost:3000" TEMPORAL_ADDRESS="localhost:7233" # Optional. Your upload directory path if you host your files locally. UPLOAD_DIRECTORY="/opt/postiz/uploads/" # Optional: your upload directory slug if you host your files locally. NEXT_PUBLIC_UPLOAD_STATIC_DIRECTORY="" # Your email provider, optional EMAIL_PROVIDER="resend|nodemailer" RESEND_API_KEY="re_1234567890" # api key if you choose resend EMAIL_HOST="smtp.gmail.com" # smtp host if you choose nodemailer EMAIL_PORT="465" # smtp port if you choose nodemailer EMAIL_SECURE="true" # smtp secure if you choose nodemailer EMAIL_USER="user" # smtp user if you choose nodemailer EMAIL_PASS="pass" # smtp pass if you choose nodemailer ## These are dummy values, you must create your own from Cloudflare. ## Remember to set your public internet IP address in the allow-list for the API token. CLOUDFLARE_ACCOUNT_ID="QhcMSXQyPuMCRpSQcSYdEuTYgHeCXHbu" CLOUDFLARE_ACCESS_KEY="dcfCMSuFEeCNfvByUureMZEfxWJmDqZe" CLOUDFLARE_SECRET_ACCESS_KEY="zTTMXBmtyLPwHEdpACGHgDgzRTNpTJewiNriLnUS" CLOUDFLARE_BUCKETNAME="postiz" CLOUDFLARE_BUCKET_URL="https://QhcMSXQyPuMCRpSQcSYdEuTYgHeCXHbu.r2.cloudflarestorage.com/" CLOUDFLARE_REGION="auto" # Social Media API Settings X_API_KEY="Twitter API key for normal oAuth not oAuth2" X_API_SECRET="Twitter API secret for normal oAuth not oAuth2" LINKEDIN_CLIENT_ID="Linkedin Client ID" LINKEDIN_CLIENT_SECRET="Linkedin Client Secret" REDDIT_CLIENT_ID="Reddit Client ID" REDDIT_CLIENT_SECRET="Linkedin Client Secret" GITHUB_CLIENT_ID="GitHub Client ID" GITHUB_CLIENT_SECRET="GitHub Client Secret" # AI OPENAI_API_KEY="OpenAI API key" # Developer Settings NX_ADD_PLUGINS=false IS_GENERAL="true" # required for now ``` ```bash theme={null} pnpm install ``` ```bash theme={null} pnpm run prisma-db-push ``` ```bash theme={null} pnpm run dev ``` If everything is running successfully, open [http://localhost:4200](http://localhost:4200) in your browser! If everything is not running - you had errors in the steps above, please head over to our [support](/support) page. ## Next Steps Set up R2 for file storage Learn the architecture of the project Set up email for notifications Set up providers such as LinkedIn, X and Reddit # Docker Source: https://docs.postiz.com/installation/docker Install Postiz using Docker standalone ## Set environment variables Postiz configuration is entirely via environment variables for now. You might be used to setting environment variables when starting containers, however postiz needs a LOT of environment variables, so setting these on command line or in a docker-compose is probably not practical for long term maintainability. It is recommended to use a `.env` file, which the Postiz containers look for in /config. Docker will automatically create this file for you on a docker volume the first time you start up Postiz. The default .env file can be found here; [example .env file](https://raw.githubusercontent.com/gitroomhq/postiz-app/main/.env.example) ## Create the container This example below shows how to create the Postiz container on the command line. Note that you will need to replace the `./config` with the path to your config directory. You will also need Postgres and Redis running. ```bash theme={null} docker create --name postiz -v postiz-uploads:/uploads/ -v postiz-config:/config/ -p 5000:5000 ghcr.io/gitroomhq/postiz-app:latest ``` ## Next Steps Set up R2 for file storage Learn the architecture of the project Set up email for notifications Set up providers such as LinkedIn, X and Reddit # Docker Compose Source: https://docs.postiz.com/installation/docker-compose Install Postiz using Docker Compose Watch the Tutorial for docker-compose install: [https://m.youtube.com/watch?v=A6CjAmJOWvA\&t=5s](https://m.youtube.com/watch?v=A6CjAmJOWvA\&t=5s) Warning: Please read this migration guide, on how to upgrade from v2.11.2 to v2.12.0+ for Temporal: [https://docs.postiz.com/installation/migration](https://docs.postiz.com/installation/migration) ## Docker Compose This guide assumes that you have docker installed, with a reasonable amount of resources to run Postiz. This Docker Compose setup has been tested with; * Virtual Machine, Ubuntu 24.04, 2Gb RAM, 2 vCPUs. ### Configuration uses environment variables The docker containers for Postiz are entirely configured with environment variables. * **Option A** - environment variables in your `docker-compose.yml` file * **Option B** - environment variables in a `postiz.env` file mounted in `/config` for the Postiz container only * **Option C** - environment variables in a `.env` file next to your `docker-compose.yml` file (not recommended). ... or a mixture of the above options! ## Installation ``` git clone https://github.com/gitroomhq/postiz-docker-compose ``` Configure your docker compose variables ``` docker compose up ``` 1. Access your frontend at: [http://localhost:4007](http://localhost:4007) (unless changed in variables) 2. Visualize and monitor your workflows with temporal at: [http://localhost:8080](http://localhost:8080) There is a [configuration reference](/configuration/reference) page with a list of configuration settings. ## The `docker-compose.yaml` file The full, up-to-date Docker Compose file is maintained in the [gitroomhq/postiz-docker-compose](https://github.com/gitroomhq/postiz-docker-compose) repository. Cloning that repository (see the steps above) gives you the `docker-compose.yaml` together with the `dynamicconfig` directory that the Temporal service mounts, so there is nothing to copy by hand. Always pull the file from the repository rather than copying a snapshot — the services, images, and environment variables change between releases, and the repository is the canonical source. ## How to use docker compose From the cloned repository directory, run `docker compose up` to start the services. **Note** When you change variables, you must run `docker compose down` and then `docker compose up` to recreate these containers with these updated variables. Look through the logs for startup errors, and if you have problems, check out the [support](/support) page. If everything looks good, then you can access the Postiz web interface at [https://postiz.your-server.com](https://postiz.your-server.com) ## Next Steps Learn the architecture of the project Set up providers such as LinkedIn, X and Reddit # Helm Source: https://docs.postiz.com/installation/kubernetes-helm Install Postiz using Kubernetes and Helm ## The Helm Chart Postiz has a helm chart that is in very active development. You can find it here; Note that this is a OCI compliant helm chart, meaning that you don't do `helm repo add`, and if you are using Flux or Helm, you must set them to OCI mode. [https://github.com/gitroomhq/postiz-helmchart](https://github.com/gitroomhq/postiz-helmchart) The `values.yml` file can be found in the repository, or a direct link to it is: [https://github.com/gitroomhq/postiz-helmchart/blob/main/charts/postiz/values.yaml](https://github.com/gitroomhq/postiz-helmchart/blob/main/charts/postiz/values.yaml) ## Next Steps Set up providers such as LinkedIn, X and Reddit Learn the architecture of the project # Migration to Temporal Source: https://docs.postiz.com/installation/migration A guide to migrating to the new Temporal infrastructure ## Prerequisites * Have your existing data saved somewhere (or use the existing PostgreSQL DB) * Cloned the [new docker-compose repo](https://github.com/gitroomhq/postiz-docker-compose) ## Migration Steps ``` cd ./postiz-docker-compose ``` ``` nano docker-compose.yaml ``` And insert all your previous secrets from the existing docker-compose.yml before v1.12.0 ``` docker compose up -d ``` Wait for a bit to let it start. ``` docker compose down ``` 1. Identify volumes: ```bash theme={null} docker volume ls ``` 2. Copy data (helper container): ```bash theme={null} docker run --rm -v :/from -v :/to \ alpine sh -c "cp -a /from/. /to/" ``` ``` docker compose up -d ``` And wait for it to start **Congratulations! You have now successfully migrated to v2.12.0 or later, and can enjoy the new Temporal infrastructure and updates!** # System Requirements Source: https://docs.postiz.com/installation/system-requirements Hardware, services, ports, and network requirements for self-hosting Postiz ## Hardware Recommended starting point for a small team (≤ 20 users): | Component | Supported floor | Recommended | | --------- | ---------------------------- | ------------------------------------- | | CPU | 2 vCPU | 4 vCPU | | RAM | 2 GB (all-in-one, light use) | 8 GB | | Disk | 20 GB | 50 GB + persistent volume for uploads | The official Docker Compose has been tested on a 2 GB / 2 vCPU Ubuntu VM running everything on one host (see [Docker Compose](/installation/docker-compose)). That works for a single-user install with occasional posting, but leaves no headroom — once you have multiple users, scheduled workflows, or external Postgres/Redis on the same host, plan for **4 GB or more**. The build step (`pnpm install` / `pnpm build` from source) is the most memory-hungry part and can OOM on 2 GB VMs. Bump the Node heap if it fails: ```bash theme={null} NODE_OPTIONS="--max-old-space-size=4096" pnpm install ``` ## Recommended install path The canonical self-host setup is the official Docker Compose repo: Pre-wired Postiz + Postgres + Redis + Temporal. The fastest way to a working install. If you'd rather build from source or use Kubernetes, see [Docker Compose](/installation/docker-compose), [Docker](/installation/docker), or [Kubernetes / Helm](/installation/kubernetes-helm). ## Required services Postiz needs four external services: | Service | Minimum version | | -------------- | --------------------------------------------------------------------------------------------------------------------- | | PostgreSQL | 14 | | Redis | 6 | | Temporal | bundled with official docker-compose; required since v2.12.0 | | Object storage | Optional — local filesystem works (`STORAGE_PROVIDER=local`), Cloudflare R2 supported (`STORAGE_PROVIDER=cloudflare`) | The official [docker-compose](https://github.com/gitroomhq/postiz-docker-compose) ships Postgres, Redis, and Temporal pre-wired. If you're running them externally, point Postiz at them via `DATABASE_URL`, `REDIS_URL`, and `TEMPORAL_ADDRESS`. ## Default ports | Service | Port | When | | ---------------------------------- | ------ | --------------------------------------------------------------------------------------------------------------------------- | | Postiz container (bundled FE + BE) | `5000` | Official Docker image (`ghcr.io/gitroomhq/postiz-app`) — exposed for you to map. The bundled compose maps host `4007:5000`. | | Backend (Nest) | `3000` | Running from source (`pnpm dev` / `pnpm start`). Override via `PORT`. | | Frontend (Next.js) | `4200` | Running from source. | | Temporal frontend | `7233` | gRPC; both modes. | | Temporal UI | `8080` | If bundled compose is used. | ## Filesystem If `STORAGE_PROVIDER=local`, set `UPLOAD_DIRECTORY` to a host path and mount that path into the backend container. The frontend serves the same files through `/uploads/:path*`. See [Uploads & storage](/configuration/uploads). If you'd rather offload media: configure [Cloudflare R2](/configuration/r2). ## Outbound network Postiz initiates outbound HTTPS to every social provider you connect. Strict egress firewalls **will** break OAuth — make sure the backend can reach: * `api.twitter.com`, `upload.twitter.com` * `graph.facebook.com`, `graph.instagram.com`, `graph.threads.net` * `linkedin.com`, `api.linkedin.com` * `openapi.tiktok.com`, `open.tiktokapis.com` * `googleapis.com` (YouTube + GMB) * the Mastodon instance you configure via `MASTODON_URL` * any other provider you intend to use If your environment requires a proxy, set `HTTPS_PROXY` and `HTTP_PROXY` on the backend. ## Inbound network The frontend talks to the backend from the browser, so `NEXT_PUBLIC_BACKEND_URL` must be **reachable from your users' browsers**, not just from the frontend SSR server. A reverse proxy in front of both ports is the typical setup — see [Reverse Proxies](/reverse-proxies/caddy). # Introduction Source: https://docs.postiz.com/introduction Welcome to Postiz documentation Create AI-powered UGC videos for your social media with [Agent Media](https://agent-media.ai) — generate engaging video content and schedule it directly with Postiz. Perfect for OpenClaw 🦞 YouTube Channel: [https://youtube.com/@postizofficial](https://youtube.com/@postizofficial) Looking to integrate with Postiz programmatically? Check out the [Public API documentation](/public-api/introduction). ## What is Postiz? Postiz helps you to manage all your social media accounts. * Schedule social media and articles * Generate posts with AI * Exchange or buy posts from other members on the marketplace Learn how to install the project and start using it Learn the architecture of the project # Examples Source: https://docs.postiz.com/mcp/examples Common workflows when using Postiz through MCP These examples show the tool calls an AI agent makes behind the scenes. You don't need to write these yourself — just describe what you want in natural language and your AI agent handles the rest. ## Schedule a Post to X A typical flow to schedule a post to X (Twitter): The agent calls `integrationList` and finds your X account: ```json theme={null} { "output": [ { "id": "abc123", "name": "My X Account", "picture": "https://...", "platform": "x" } ] } ``` The agent calls `integrationSchema` with `platform: "x"` to learn the rules: ```json theme={null} { "output": { "rules": "...", "maxLength": 280, "settings": { ... }, "tools": [] } } ``` The agent calls `schedulePostTool`: ```json theme={null} { "socialPost": [ { "integrationId": "abc123", "isPremium": false, "date": "2025-01-15T10:00:00.000Z", "shortLink": false, "type": "schedule", "postsAndComments": [ { "content": "

Excited to announce our new feature!

", "attachments": [] } ], "settings": [ { "key": "who_can_reply_post", "value": "everyone" } ] } ] } ```
## Post to Discord with Channel Selection Platforms like Discord require selecting a channel first: The agent calls `integrationSchema` with `platform: "discord"` and discovers a tool to list channels. The agent calls `triggerTool`: ```json theme={null} { "integrationId": "discord-123", "methodName": "listChannels", "dataSchema": [] } ``` Returns available channels with their IDs. The agent includes the channel ID in settings: ```json theme={null} { "socialPost": [ { "integrationId": "discord-123", "isPremium": false, "date": "2025-01-15T10:00:00.000Z", "shortLink": false, "type": "now", "postsAndComments": [ { "content": "

Hello Discord!

", "attachments": [] } ], "settings": [ { "key": "channel", "value": "channel-id-here" } ] } ] } ```
## Schedule an Instagram Reel with Trending Audio Instagram (Facebook Business-linked) exposes an `audioSearch` tool for finding music or original sounds to attach to a Reel: The agent calls `triggerTool` (an empty `q` returns trending audio): ```json theme={null} { "integrationId": "instagram-123", "methodName": "audioSearch", "dataSchema": [ { "key": "q", "value": "summer vibes" }, { "key": "type", "value": "music" } ] } ``` Returns audio assets with their IDs: ```json theme={null} { "output": [ { "id": "587784541076604", "title": "Summer Vibes", "artist": "Some Artist", "duration": 30000, "previewUrl": "https://..." } ] } ``` The agent includes the chosen audio in settings — the post must be a single video with `post_type: "post"`: ```json theme={null} { "socialPost": [ { "integrationId": "instagram-123", "isPremium": false, "date": "2025-01-15T10:00:00.000Z", "shortLink": false, "type": "schedule", "postsAndComments": [ { "content": "

New reel with trending audio!

", "attachments": ["https://uploads.postiz.com/reel.mp4"] } ], "settings": [ { "key": "post_type", "value": "post" }, { "key": "audio", "value": { "id": "587784541076604", "audio_volume": 80, "video_volume": 20 } } ] } ] } ```
Audio is not available on `instagram-standalone` channels — the Instagram Audio API requires Facebook Login. ## Post with an AI-Generated Image The agent calls `generateImageTool`: ```json theme={null} { "prompt": "A futuristic city skyline at sunset, digital art style" } ``` Returns: ```json theme={null} { "id": "img-456", "path": "https://uploads.postiz.com/generated-image.png" } ``` The agent includes the image URL in attachments: ```json theme={null} { "postsAndComments": [ { "content": "

The future is here

", "attachments": ["https://uploads.postiz.com/generated-image.png"] } ] } ```
## Generate a Video and Post The agent calls `generateVideoOptions` to see available generators. For Image Text Slides, the agent calls `videoFunctionTool`: ```json theme={null} { "identifier": "image-text-slides", "functionName": "loadVoices" } ``` Returns a list of available voices with their IDs. The agent calls `generateVideoTool`: ```json theme={null} { "identifier": "image-text-slides", "output": "vertical", "customParams": [ { "key": "prompt", "value": "5 tips for better social media engagement" }, { "key": "voice", "value": "voice-id-here" } ] } ``` Returns the video URL. The agent uses the video URL as an attachment when calling `schedulePostTool`. ## Create an X Thread To create a thread on X, add multiple items to `postsAndComments`: ```json theme={null} { "socialPost": [ { "integrationId": "x-123", "isPremium": false, "date": "2025-01-15T10:00:00.000Z", "shortLink": false, "type": "schedule", "postsAndComments": [ { "content": "

Thread: 5 things I learned this week

", "attachments": [] }, { "content": "

1. Consistency beats intensity

", "attachments": [] }, { "content": "

2. Start before you're ready

", "attachments": [] } ], "settings": [ { "key": "who_can_reply_post", "value": "everyone" } ] } ] } ``` ## Post to LinkedIn with a Comment For LinkedIn, the first item in `postsAndComments` is the post and the rest are comments: ```json theme={null} { "socialPost": [ { "integrationId": "linkedin-123", "isPremium": false, "date": "2025-01-15T10:00:00.000Z", "shortLink": false, "type": "schedule", "postsAndComments": [ { "content": "

We just launched something big!

", "attachments": [] }, { "content": "

Check it out at example.com

", "attachments": [] } ], "settings": [] } ] } ``` ## Bulk Schedule Schedule 5 posts across different days: ```json theme={null} { "socialPost": [ { "integrationId": "x-123", "isPremium": false, "date": "2025-01-13T10:00:00.000Z", "shortLink": false, "type": "schedule", "postsAndComments": [{ "content": "

Monday motivation

", "attachments": [] }], "settings": [{ "key": "who_can_reply_post", "value": "everyone" }] }, { "integrationId": "x-123", "isPremium": false, "date": "2025-01-14T10:00:00.000Z", "shortLink": false, "type": "schedule", "postsAndComments": [{ "content": "

Tuesday tip

", "attachments": [] }], "settings": [{ "key": "who_can_reply_post", "value": "everyone" }] }, { "integrationId": "x-123", "isPremium": false, "date": "2025-01-15T10:00:00.000Z", "shortLink": false, "type": "schedule", "postsAndComments": [{ "content": "

Midweek thoughts

", "attachments": [] }], "settings": [{ "key": "who_can_reply_post", "value": "everyone" }] }, { "integrationId": "x-123", "isPremium": false, "date": "2025-01-16T10:00:00.000Z", "shortLink": false, "type": "schedule", "postsAndComments": [{ "content": "

Thursday throwback

", "attachments": [] }], "settings": [{ "key": "who_can_reply_post", "value": "everyone" }] }, { "integrationId": "x-123", "isPremium": false, "date": "2025-01-17T10:00:00.000Z", "shortLink": false, "type": "schedule", "postsAndComments": [{ "content": "

Friday wrap-up

", "attachments": [] }], "settings": [{ "key": "who_can_reply_post", "value": "everyone" }] } ] } ``` Each item in the `socialPost` array is an independent post with its own date, content, and settings. # Introduction Source: https://docs.postiz.com/mcp/introduction Connect AI agents to Postiz using the Model Context Protocol (MCP) MCP (Model Context Protocol) lets AI agents interact with Postiz directly — listing integrations, scheduling posts, generating images and videos — all through a standardized tool-calling interface. This means you can connect Claude, ChatGPT, Cursor, or any MCP-compatible client to your Postiz account and manage your social media through natural language. ## How It Works Postiz exposes an MCP server that provides **9 tools** to AI agents. The agent discovers these tools, understands their schemas, and calls them on your behalf. ```mermaid theme={null} sequenceDiagram participant Agent as AI Agent participant MCP as Postiz MCP Server participant Postiz as Postiz Backend Agent->>MCP: Connect with API key / OAuth token MCP-->>Agent: List available tools Agent->>MCP: Call tool (e.g., schedule post) MCP->>Postiz: Execute action Postiz-->>MCP: Return result MCP-->>Agent: Tool response ``` ## Available Tools | Tool | Description | | ---------------------- | ----------------------------------------------------------------------- | | `integrationList` | List all connected social media accounts (optionally filtered by group) | | `groupList` | List all groups (customers) for your organization | | `integrationSchema` | Get platform-specific posting rules and settings schema | | `triggerTool` | Execute platform-specific helpers (e.g., list Discord channels) | | `schedulePostTool` | Schedule, draft, or immediately publish posts | | `generateImageTool` | Generate AI images for posts | | `generateVideoOptions` | List available video generation options | | `videoFunctionTool` | Get video generator settings (e.g., available voices) | | `generateVideoTool` | Generate videos for posts | ## Authentication There are two ways to authenticate with the MCP server: ### API Key Get your API key from **Settings > Developers > Public API** in Postiz. Use it directly in the MCP endpoint URL or as a Bearer token. ### OAuth Token If you're building an app for other Postiz users, use [OAuth2](/public-api/oauth) to obtain tokens. OAuth tokens start with `pos_` and work the same way as API keys. ## Connecting Use the `/mcp` endpoint with your API key or OAuth token as a Bearer token: ``` URL: https://api.postiz.com/mcp Authorization: Bearer your-api-key ``` This method supports both API keys and OAuth tokens (prefixed with `pos_`). Use the `/mcp/:apiKey` endpoint with your API key embedded in the URL: ``` URL: https://api.postiz.com/mcp/your-api-key ``` For self-hosted instances, replace `https://api.postiz.com` with your `NEXT_PUBLIC_BACKEND_URL`. ## Quick Example Here's what a typical interaction looks like when an AI agent uses Postiz MCP: 1. **Agent calls `integrationList`** — gets back your connected accounts (X, LinkedIn, etc.) 2. **Agent calls `integrationSchema`** with `platform: "x"` — learns X's character limits, settings, and rules 3. **Agent calls `schedulePostTool`** — schedules your post with the correct format All of this happens automatically when you tell your AI agent something like: > "Schedule a post to X for tomorrow at 10am: Excited to announce our new feature!" ## FAQ ### Do I need an OpenAI key to use Postiz MCP? No. The MCP server just exposes Postiz's tools — your AI client (Claude, ChatGPT, Cursor, etc.) provides the model. Postiz only needs an `OPENAI_API_KEY` if you use Postiz's own AI features (image generation, copilot) which are separate from the MCP tools surfaced to your client. ### What happens when my API key expires or is rotated? Postiz API keys don't auto-rotate, but if you regenerate one in Settings → Developers → Public API, every MCP client using the old key stops working until you update its config. Update the URL or the `Authorization` header in your client config and reconnect. ### Self-hosted: how do I expose the MCP endpoint? The MCP server starts as part of the Postiz backend and is reachable at `/mcp` (Bearer auth), `/mcp/:apiKey` (key in URL), and `/mcp-oauth` (OAuth-protected). Your reverse proxy must forward these paths to the backend and support streaming HTTP (`Transfer-Encoding: chunked`). See [Reverse Proxies](/reverse-proxies/caddy). ### Can MCP read or reply to comments? Not today. The current tool set is read-only on integrations and write-only on posts/media — there's no `getComments` or `replyToComment` exposed via MCP. Comment replies must be triggered through the Postiz UI. # Client Setup Source: https://docs.postiz.com/mcp/setup Configure your AI client to connect to the Postiz MCP server ## Claude Desktop Add the following to your Claude Desktop MCP configuration file: Edit `~/Library/Application Support/Claude/claude_desktop_config.json`: ```json theme={null} { "mcpServers": { "postiz": { "url": "https://api.postiz.com/mcp/your-api-key" } } } ``` Edit `%APPDATA%\Claude\claude_desktop_config.json`: ```json theme={null} { "mcpServers": { "postiz": { "url": "https://api.postiz.com/mcp/your-api-key" } } } ``` Replace `your-api-key` with your actual API key from **Settings > Developers > Public API**. ## Claude Code The fastest way is the CLI: ```bash theme={null} claude mcp add postiz --transport http --url https://api.postiz.com/mcp/your-api-key ``` Or add it to your Claude Code config directly: ```json theme={null} { "mcpServers": { "postiz": { "url": "https://api.postiz.com/mcp/your-api-key" } } } ``` The Bearer-token transport works too — pick whichever your client supports better. Both authenticate the same way: ```bash theme={null} claude mcp add postiz --transport http \ --url https://api.postiz.com/mcp \ --header "Authorization: Bearer your-api-key" ``` ## Cursor In Cursor, go to **Settings > MCP** and add a new server: * **Name:** Postiz * **Type:** HTTP * **URL:** `https://api.postiz.com/mcp/your-api-key` ## Other MCP Clients Any MCP-compatible client can connect to Postiz. Use the streamable HTTP transport: * **URL:** `https://api.postiz.com/mcp/your-api-key` * **Transport:** Streamable HTTP Or, if your client supports Bearer token authentication: * **URL:** `https://api.postiz.com/mcp` * **Transport:** Streamable HTTP * **Authorization:** `Bearer your-api-key` ## Self-Hosted For self-hosted Postiz instances, replace `https://api.postiz.com` with your `NEXT_PUBLIC_BACKEND_URL`: ``` https://your-postiz-server.com/mcp/your-api-key ``` ## Verify Connection Once connected, ask your AI agent: > "List my connected social media accounts" If the connection is working, the agent will call the `integrationList` tool and return your connected accounts. # Tools Reference Source: https://docs.postiz.com/mcp/tools Complete reference for all Postiz MCP tools ## integrationList List all connected social media accounts (channels) for your organization. **Parameters:** | Field | Type | Required | Description | | ------- | ------ | -------- | ------------------------------------------------------------------------------------------------------- | | `group` | string | No | Group (customer) ID from `groupList`. When provided, only channels belonging to that group are returned | **Returns:** | Field | Type | Description | | ---------- | ------ | ------------------------------------------------------------------------------------------------------------------- | | `id` | string | Integration ID (use this when scheduling posts) | | `name` | string | Display name of the account | | `picture` | string | Profile picture URL | | `platform` | string | Platform identifier (e.g., `x`, `linkedin`, `facebook`) | | `customer` | object | The group (customer) this channel belongs to, as `{ id, name }` — omitted if the channel is not assigned to a group | *** ## groupList List all groups (customers) for your organization. Use a group's `id` with `integrationList` to filter channels down to a single group. **Parameters:** None **Returns:** | Field | Type | Description | | ------ | ------ | ---------------------------------------------------------- | | `id` | string | Group (customer) ID (pass to `integrationList` as `group`) | | `name` | string | Group (customer) display name | *** ## integrationSchema Get the posting rules, character limits, required settings, and available helper tools for a specific platform. Call this before scheduling a post to understand what the platform expects. **Parameters:** | Field | Type | Required | Description | | ----------- | ------- | -------- | ---------------------------------------------------------------- | | `isPremium` | boolean | Yes | Whether the user has a premium subscription | | `platform` | string | Yes | Platform identifier (e.g., `x`, `linkedin`, `reddit`, `discord`) | **Returns:** | Field | Type | Description | | ----------- | ------ | -------------------------------------------------- | | `rules` | string | Platform-specific posting rules and best practices | | `maxLength` | number | Maximum character length for posts | | `settings` | object | JSON schema of required settings for this platform | | `tools` | array | Platform-specific helper tools (see `triggerTool`) | The `tools` array contains helper functions specific to the platform. For example, Discord returns a tool to list available channels, Reddit returns a tool to search for subreddits, and LinkedIn Page returns a tool to list pages. Each tool in the array has: | Field | Type | Description | | ------------- | ------ | -------------------------------------- | | `methodName` | string | Function name to pass to `triggerTool` | | `description` | string | What the tool does | | `dataSchema` | array | Parameters the tool accepts | *** ## triggerTool Execute a platform-specific helper function. These are discovered through `integrationSchema` and are used to fetch dynamic data like channel lists, subreddit suggestions, or page IDs. **Parameters:** | Field | Type | Required | Description | | --------------- | ------ | -------- | ------------------------------------------------ | | `integrationId` | string | Yes | The integration ID from `integrationList` | | `methodName` | string | Yes | The function name from `integrationSchema` tools | | `dataSchema` | array | Yes | Key-value pairs of parameters for the function | Each item in `dataSchema`: | Field | Type | Description | | ------- | ------ | --------------- | | `key` | string | Parameter name | | `value` | string | Parameter value | **Example use cases:** * Get the list of Discord channels for a server * Search for Reddit subreddits * List LinkedIn pages you manage * Get Facebook page options * Search Instagram audio (`audioSearch`) to attach to a Reel — only on Facebook Business-linked Instagram channels *** ## schedulePostTool Schedule, draft, or immediately publish posts to social media platforms. This is the main tool for creating content. **Parameters:** | Field | Type | Required | Description | | ------------ | ----- | -------- | ------------------------ | | `socialPost` | array | Yes | Array of posts to create | Each item in `socialPost`: | Field | Type | Required | Description | | ------------------ | ------- | -------- | --------------------------------------------------- | | `integrationId` | string | Yes | Integration ID from `integrationList` | | `isPremium` | boolean | Yes | Whether the user has premium | | `date` | string | Yes | UTC datetime (e.g., `2025-01-15T10:00:00.000Z`) | | `shortLink` | boolean | Yes | Whether to shorten links in the post | | `type` | string | Yes | `draft`, `schedule`, or `now` | | `postsAndComments` | array | Yes | The post content and optional comments | | `settings` | array | Yes | Platform-specific settings from `integrationSchema` | Each item in `postsAndComments`: | Field | Type | Description | | ------------- | ------ | ----------------------------------------- | | `content` | string | HTML content (see formatting rules below) | | `attachments` | array | Array of image/media URLs | Each item in `settings`: | Field | Type | Description | | ------- | ------ | ----------------------------------------------------- | | `key` | string | Setting name | | `value` | any | Setting value (prefer IDs over labels when available) | ### Content Formatting Content must be HTML with these allowed tags only: | Tag | Usage | | ---------------------- | --------------- | | `

` | Wrap each line | | `

`, `

`, `

` | Headings | | `` | Bold text | | `` | Underlined text | | `
    `, `
  • ` | Lists | You cannot combine `` and `` in the same element. Each line of text must be wrapped in `

    ` tags. ### Posts vs Comments The `postsAndComments` array behavior depends on the platform: * **Thread-based platforms** (X, Threads, Bluesky): Each array item becomes a separate post in a thread * **Comment-based platforms** (LinkedIn, Facebook): First item is the post, remaining items are comments ### Multiple Posts To schedule multiple posts (e.g., 20 posts across different days), add multiple items to the `socialPost` array — each with its own `date` and `integrationId`. **Returns:** | Field | Type | Description | | ------------- | ------ | -------------------------------- | | `postId` | string | The created post ID | | `integration` | string | The integration it was posted to | If validation fails, returns `{ errors: string }` with details about what went wrong (e.g., content exceeds character limit). *** ## generateImageTool Generate an AI image to use as a post attachment. **Parameters:** | Field | Type | Required | Description | | -------- | ------ | -------- | ------------------------------------ | | `prompt` | string | Yes | Description of the image to generate | **Returns:** | Field | Type | Description | | ------ | ------ | -------------------------- | | `id` | string | Media ID | | `path` | string | URL of the generated image | Use the returned `path` in the `attachments` array when scheduling a post. *** ## generateVideoOptions List all available video generation types and their required parameters. **Parameters:** None **Returns:** An array of video generators, each with: | Field | Type | Description | | -------------- | ------ | --------------------------------------------------------- | | `type` | string | Video type identifier (e.g., `image-text-slides`, `veo3`) | | `output` | string | Supported orientations: `vertical\|horizontal` | | `tools` | array | Helper functions (call with `videoFunctionTool`) | | `customParams` | object | JSON schema of required parameters | ### Available Video Types | Type | Description | Requirements | | ----------------- | -------------------------------------- | -------------------------------------------------------- | | Image Text Slides | Slide-based videos with text-to-speech | `prompt`, `voice` (get voice ID via `videoFunctionTool`) | | Veo3 | AI-generated video with audio | `prompt`, optional `images` (max 3) | *** ## videoFunctionTool Execute helper functions for video generators. Use this to fetch required data before generating a video (e.g., listing available voices). **Parameters:** | Field | Type | Required | Description | | -------------- | ------ | -------- | ------------------------------------------------- | | `identifier` | string | Yes | Video type identifier from `generateVideoOptions` | | `functionName` | string | Yes | Function name from the video type's `tools` array | **Example:** Call with `identifier: "image-text-slides"` and `functionName: "loadVoices"` to get available ElevenLabs voice IDs. *** ## generateVideoTool Generate a video to use as a post attachment. **Parameters:** | Field | Type | Required | Description | | -------------- | ------ | -------- | ------------------------------------------------ | | `identifier` | string | Yes | Video type from `generateVideoOptions` | | `output` | string | Yes | `vertical` or `horizontal` | | `customParams` | array | Yes | Key-value pairs of parameters for the video type | Each item in `customParams`: | Field | Type | Description | | ------- | ------ | --------------- | | `key` | string | Parameter name | | `value` | any | Parameter value | **Returns:** | Field | Type | Description | | ----- | ------ | -------------------------- | | `url` | string | URL of the generated video | Use the returned `url` in the `attachments` array when scheduling a post. # Bluesky Source: https://docs.postiz.com/providers/bluesky How to add Bluesky to Postiz You do not need to configure any environment variables for BlueSky, you can simply add your account from the UI. New Channel You should be redirected and be able to start posting immediately. If you have any issues, check the backend service logs. # Discord Source: https://docs.postiz.com/providers/discord How to add Discord to your system This integration requires that you have **Manage Server** permissions on the Discord server you want to integrate with. Login to Discord on the web, and then go to the [Discord Developer Portal](https://discord.com/developers/applications) and click on "New Application". New Application App Icon Upload the App Icon of your choice (1024x1024px max) and save your changes. If you do not do this, you will get 404 errors in logs when trying to add the Discord channel in the Postiz web interface. You can find this in the **OAuth2** section of the Discord Developer Portal. Copy Keys Set these in your .env file as follows; ```env theme={null} DISCORD_CLIENT_ID="your_client_id" DISCORD_CLIENT_SECRET="your_client_secret" ``` **Your Discord OAuth2 Redirect URI:** * Production: `https://your-postiz-domain.com/integrations/social/discord` * Local development: `http://localhost:4200/integrations/social/discord` * Docker: `http://localhost:5000/integrations/social/discord` You can find this in the **OAuth2** section of the Discord Developer Portal. Redirect URI Navigate to the "Bot" section of the Discord Developer Portal. Fill out the bot details however you like, and then click "Reset Token". With the token that is generated, set it in your .env file as follows; ```env theme={null} DISCORD_BOT_TOKEN_ID="your_bot_token" ``` If you do not set this, you will get 404 errors when trying to add the Discord channel in the Postiz web interface. Stop Postiz if it is running, and then start it using the .env file with the Discord details. Go to the Postiz web interface, and click on the "Add Channel" button, and then select "Discord". You should be redirected to Discord to login. # Dribbble Source: https://docs.postiz.com/providers/dribbble How to add Dribbble to your system [Register your application on Dribbble](https://dribbble.com/account/applications/new). * **Name:** `MyPostizInstance` * **Description:** `My Postiz Instance` * **Website:** `https://example.com` * **Redirect URI:** (see below) **Your Dribbble OAuth2 Redirect URI:** * Production: `https://your-postiz-domain.com/integrations/social/dribble` * Local development: `http://localhost:4200/integrations/social/dribble` * Docker: `http://localhost:5000/integrations/social/dribble` These can be found immediately after registering your application. These are both 64 characters long. ```env theme={null} DRIBBLE_CLIENT_ID="1234..." DRIBBLE_CLIENT_SECRET="5678..." ``` Restart Postiz with the updated environment variables Go to the Postiz web interface, and click on the "Add Channel" button. Select "Dribbble" from the list of available channels. You should be redirected to Dribbble to authorize the application. # Facebook Source: https://docs.postiz.com/providers/facebook How to add Facebook to your system **NOTE:** Please be advised that Instagram and Facebook can use the same app (no need to create two separate apps) Select a business portfolio, then create a [new app in Facebook developers](https://developers.facebook.com/apps/creation/). Please be advised that for public applications, you will need to verify your business. Business Portfolio Create app Select "Other" and click next Other app use cases Then select business ![Business](https://github.com/user-attachments/assets/74bde861-5441-46bb-b5b8-c5229e980237) Add all your details and click Create App ![Create app details](https://github.com/user-attachments/assets/f0c03825-0f9b-4467-94a4-ab8cf6ed7e1d) ![Setup Login with Facebook](https://github.com/user-attachments/assets/08d3c1d1-d498-49d1-adac-aa6248e7c10c) Set up login for business Set up a redirect URI back to the application ![step 6](https://github.com/user-attachments/assets/8bf1774b-b6fe-4ac6-aea5-97d8c8bbf5da) **Your Facebook OAuth2 Redirect URI:** * Production: `https://your-postiz-domain.com/integrations/social/facebook` * Local development: `http://localhost:4200/integrations/social/facebook` * Docker: `http://localhost:5000/integrations/social/facebook` ![step 7](https://github.com/user-attachments/assets/a81aa2a3-de66-4099-906b-b78c641d1a23) Go to advanced permission and request access for the following scopes: * `pages_show_list` * `business_management` * `pages_manage_posts` * `pages_manage_engagement` * `pages_read_engagement` * `read_insights` If your Postiz install is for personal use only these advanced permissions are not required for Postiz to function. Change the App Mode from 'Development' to 'Live'. If you do not do this then posts made via the API will display for yourself but will not be visible for other users. ![Keys](https://github.com/user-attachments/assets/ac11f87f-4951-47f8-8344-7fbc9de942e4) Go to basic permissions copy your App ID and App Secret and paste them in your `.env` file ```env theme={null} FACEBOOK_APP_ID="app id" FACEBOOK_APP_SECRET="app secret" ``` Facebook should now be working! ## Troubleshooting ### Image is missing from the published post Check the **App Mode** of your Facebook app. In **Development** mode, posts with media are only visible to app developers/testers — everyone else sees the post without the image. Switch the app to **Live** mode to make the image visible to all users. ### Facebook posts work for you but not for other users Same root cause as above — the app is in Development mode. Only roles you've explicitly added (developers, testers, admins) can see content published via the API. Set the app to Live. # Farcaster Source: https://docs.postiz.com/providers/farcaster How to add Farcaster (Warpcast) to your system Postiz uses [Neynar](https://neynar.com) as the Farcaster API provider. You need a Neynar developer account to obtain the API credentials below. Farcaster posts on Postiz accept images only — text + image, but no video. Posts can be up to 800 characters. Go to the [Neynar dashboard](https://dev.neynar.com) and create a new app. You'll get a **Client ID** and a **Secret Key**. Add the following to your `.env` file: ```env theme={null} NEYNAR_CLIENT_ID="your-neynar-client-id" NEYNAR_SECRET_KEY="your-neynar-secret-key" ``` Stop Postiz if it is running and start it again so the new environment variables are picked up. In the Postiz web interface, click **Add Channel**, select **Farcaster**, and complete the Neynar login flow. Your Farcaster account will appear in the channel list. # Google My Business Source: https://docs.postiz.com/providers/google-my-business How to add Google My Business to your system This integration allows you to post updates to your Google Business Profile. Before you start, make sure you have a Google account and a verified Google Business Profile. The **Google My Business API** requires approval from Google. You must submit an access request through Google's [access request form](https://developers.google.com/my-business/content/prereqs#request-access). Approval can take a few days up to a week. ## Setup Make sure you are logged in to your Google account and visit the [Google Cloud Console](https://console.cloud.google.com/projectselector2/apis/credentials). Make sure to read the terms and conditions and "Agree and Continue". Create a new project by clicking on the "Create Project" button, or select an existing project. Fill in the project name and details, then click "Create". You can use the same Google Cloud project for both YouTube and Google My Business integrations. Click **Enable APIs and Services** at the top of the dashboard. Search for and enable each of the following APIs: * `Google My Business API` - This will only appear after your access request has been approved * `My Business Account Management API` * `My Business Business Information API` For each API, click the **Enable** button on the API's detail page. Navigate to the **OAuth consent screen** tab. If not already configured: 1. Select "External" user type (unless you have a Google Workspace organization) 2. Fill in the required app information 3. Add yourself as a test user 4. Save and continue through the wizard 1. Navigate to the **Credentials** tab 2. Click on **Create Credentials** and select **OAuth client ID** 3. For Application Type, select **Web application** 4. Enter a name for your application **Your Google My Business OAuth2 Redirect URI:** * Production: `https://your-postiz-domain.com/integrations/social/gmb` * Local development: `http://localhost:4200/integrations/social/gmb` * Docker: `http://localhost:5000/integrations/social/gmb` Add your redirect URI under **Authorized redirect URIs**, then click **Create**. After creating the credentials, you will see your `Client ID` and `Client Secret`. Copy these and add them to your `.env` file: ```env theme={null} GOOGLE_GMB_CLIENT_ID="" GOOGLE_GMB_CLIENT_SECRET="" ``` These are the same credentials used for YouTube integration. If you've already configured YouTube, you don't need to add new environment variables - just ensure the GMB redirect URI is added to your existing OAuth credentials. Stop Postiz if it is running, and restart it with the updated environment variables. Go to the Postiz web interface, click on the "Add Channel" button, and select "Google My Business". You should be redirected to Google to authorize the application. ## Troubleshooting ### API Access Not Approved If you cannot find the "Google My Business API" in the API library, your access request may still be pending. Check your email for updates from Google, or resubmit the [access request form](https://developers.google.com/my-business/content/prereqs#request-access). ### 403 Forbidden Errors Make sure all three required APIs are enabled: * Google My Business API * My Business Account Management API * My Business Business Information API ### Business Profile Not Showing Ensure your Google Business Profile is verified and that you're logging in with the Google account that owns or manages the business profile. ### Analytics returns empty or filter errors The GMB performance API restricts which metric combinations and date ranges it will return data for. If Postiz shows an empty analytics chart, double-check the date range you've selected covers periods after the profile was verified. Some metrics aren't available for newer or unverified profiles at all — the API returns no data rather than an error. ### Known issue: connect page surfaces session-recording errors The `/integrations/social/gmb` page can surface session-replay overlay errors that mask the actual OAuth flow. The handshake usually still completes — refresh the Channels page after authorising on Google's side. See [Known Issues](/troubleshooting/known-issues). # Instagram Source: https://docs.postiz.com/providers/instagram How to add Instagram to your system **NOTE:** Please be advised that Instagram and Facebook can use the same app (no need to create two separate apps) ## Connection Options There are two ways to connect to an Instagram account: by using a Facebook Business or through a standalone flow that connects directly to an Instagram account. Both methods will require a [Meta for Developers account](https://developers.facebook.com/apps/). **What Postiz supports on Instagram:** * Feed posts (single image, carousel, video / Reels). * Stories (image and video). * Attaching audio (music or original sounds) to Reels — Facebook Business connection only, the [Instagram Audio API](https://developers.facebook.com/docs/instagram-platform/content-publishing/audio-api/) is not available for standalone connections. * Replying to comments via an explicit user action. **What Postiz does not support:** * Story link stickers / swipe-up links — the Instagram Graph API doesn't expose interactive sticker payloads, so Postiz can only upload the story media. * Automatic comment auto-reply — there is no agent that watches for incoming comments and replies on your behalf. You can post a reply through the API, but each reply is an explicit action. ## Setting up Meta Application The following steps will guide you through the setup of a Meta application that can be used for connecting Instagram to Postiz. Select a business portfolio, then create a [new app in Meta for developers](https://developers.facebook.com/apps/creation/). Please be advised that for public applications, you will need to verify your business. Business Portfolio Create app Select "Other" and click next Other app use cases Then select business ![Business](https://github.com/user-attachments/assets/74bde861-5441-46bb-b5b8-c5229e980237) Add all your details and click Create App ![Create an app details page](https://github.com/user-attachments/assets/f0c03825-0f9b-4467-94a4-ab8cf6ed7e1d) ## Facebook Business Option If you have a Facebook Business page that is linked to your Instagram account, you can connect to it by setting up the Login for Business flow. ![Setup Login with Instagram](https://github.com/user-attachments/assets/08d3c1d1-d498-49d1-adac-aa6248e7c10c) Set up login for business Set up a redirect URI back to the application ![Instagram](https://github.com/user-attachments/assets/78496d3f-3b84-4724-afc8-ed217d892c6d) **Your Instagram OAuth2 Redirect URI:** * Production: `https://your-postiz-domain.com/integrations/social/instagram` * Local development: `http://localhost:4200/integrations/social/instagram` * Docker: `http://localhost:5000/integrations/social/instagram` ![step 7](https://github.com/user-attachments/assets/a81aa2a3-de66-4099-906b-b78c641d1a23) Go to advanced permission and request access for the following scopes: * `instagram_basic` * `pages_show_list` * `pages_read_engagement` * `business_management` * `instagram_content_publish` * `instagram_manage_comments` * `instagram_manage_insights` ![Keys](https://github.com/user-attachments/assets/ac11f87f-4951-47f8-8344-7fbc9de942e4) Go to basic permissions copy your App ID and App Secret and paste them in your `.env` file ```env theme={null} FACEBOOK_APP_ID="app id" FACEBOOK_APP_SECRET="app secret" ``` Instagram should now be working! ## Instagram Standalone Option If you want to connect directly to your Instagram account without having to use a Facebook Business, use the standalone option. Please note that standalone option requires a professional Instagram account. "Add products to your app" section of app creation Set up Instagram. Set up Instagram Business Login Click on the button to set up Instagram Business Login Set up redirect URI **Your Instagram Standalone OAuth2 Redirect URI:** * Production: `https://your-postiz-domain.com/integrations/social/instagram-standalone` * Local development: `http://localhost:4200/integrations/social/instagram-standalone` * Docker: `http://localhost:5000/integrations/social/instagram-standalone` Instagram App ID and Secret From your Instagram API setup screen copy the Instagram App ID and Instagram App Secret and paste them in your `.env` file ```env theme={null} INSTAGRAM_APP_ID="app id" INSTAGRAM_APP_SECRET="app secret" ``` Go to the Postiz web interface, and click on the "Add Channel" button. Select "Instagram (Standalone)" from the list of available channels. You should be redirected to the Instagram login screen to authorize the application. ## Adding App Roles If you're having trouble connecting to your Instagram accounts, adding them as App Roles may help. Facebook App developers dashboard Click on "Add People" App Roles page Select the "Instagram Tester" option, and type in the handles of all the Instagram accounts you'd like to connect to. Then, click "Add". Add people window Go to your Instagram account, and accept the tester invitation in the [Apps and Websites section of the profile settings](https://www.instagram.com/accounts/manage_access/). Apps and Websites section of the profile settings ## Troubleshooting ### "Insufficient developer role" error This means the Instagram account you're trying to connect hasn't been added as a tester on the Meta app. Walk through the [Adding App Roles](#adding-app-roles) section above for that exact account: add it as an Instagram Tester, then accept the invitation from within the Instagram app's settings. ### Channel connects, but posts fail with permission errors The advanced permissions on the Meta app must be approved (or your account must be a developer/tester on the app). Submit the app for review with the required scopes — until then, only roles you've added can publish. # LinkedIn Source: https://docs.postiz.com/providers/linkedin How to add LinkedIn to your system Head over to [LinkedIn developers](https://www.linkedin.com/developers/apps) and create a new app. LinkedIn Fill in all the details, once created head over to Products and make sure you add all the required products. LinkedIn It is important to request the Advertising API permissions and fill up the request form, or you will not have the ability to refresh your tokens. **Your LinkedIn OAuth2 Redirect URI:** * Production: `https://your-postiz-domain.com/integrations/social/linkedin` * Local development: `http://localhost:4200/integrations/social/linkedin` * Docker: `http://localhost:5000/integrations/social/linkedin` If you are using the "LinkedIn Page" provider, replace `linkedin` with `linkedin-page` in the redirect URI. Copy the created `Client ID` and `Client Secret` and add them to your `.env` file. ```env theme={null} LINKEDIN_CLIENT_ID="" LINKEDIN_CLIENT_SECRET="" ``` You can find those under the Auth Tab of your LinkedIn App in the developer portal. # LinkedIn Page Source: https://docs.postiz.com/providers/linkedin-page How to add a LinkedIn Page to your system Head over to [LinkedIn developers](https://www.linkedin.com/developers/apps) and create a new app. LinkedIn Verify your app with LinkedIn LinkedIn You will need to follow the verification process to request the necessary permissions listed below. Fill in all the details, once created head over to Products and make sure you add all the required products: * Share on LinkedIn * Advertising API * Sign in with LinkedIn using OpenID connect It is important to request the Advertising API permissions and fill up the request form, or you will not have the ability to refresh your tokens. **Your LinkedIn Page OAuth2 Redirect URI:** * Production: `https://your-postiz-domain.com/integrations/social/linkedin-page` * Local development: `http://localhost:4200/integrations/social/linkedin-page` * Docker: `http://localhost:5000/integrations/social/linkedin-page` Copy the created `Client ID` and `Client Secret` and add them to your `.env` file. ```env theme={null} LINKEDIN_CLIENT_ID="" LINKEDIN_CLIENT_SECRET="" ``` You can find those under the Auth Tab of your LinkedIn App in the developer portal. ## Posting PDFs / document carousels LinkedIn is the only provider Postiz supports PDF posting on, and only through document-carousel posts. When you create a carousel of images on a LinkedIn Page, Postiz combines the images into a PDF document and uploads it as a LinkedIn document share. You don't upload a PDF directly — Postiz produces the PDF from your images. This is not supported on other platforms; the public-API upload endpoint does not accept `application/pdf`. # Mastodon Source: https://docs.postiz.com/providers/mastodon How to add Mastodon to your system Watch the YouTube Tutorial: [https://youtu.be/IAnfbE\_htqg?si=z30m5qS8qLDN9R0X](https://youtu.be/IAnfbE_htqg?si=z30m5qS8qLDN9R0X) Mastodon client registration is not done via the web interface, but by talking to the API directly. In the example below, we use `curl` to register a new client. Optionally check that you have `jq` installed on your system. You can normally install this with brew, apt-get, yum or chocolatey. If you don't have `jq` installed, you can remove it from the command below. The examples on this page use `https://mastodon.social` as the default Mastodon instance. If you are setting up Postiz to connect to a different self-hosted Mastodon instance (e.g., `https://fosstodon.org`), you must replace `https://mastodon.social` with your instance's URL in the `curl` command below. You will also need to ensure the `MASTODON_URL` environment variable in your application's `.env` file (or equivalent configuration for Docker, etc.) is set to your custom instance's URL. **Your Mastodon OAuth2 Redirect URI:** * Production: `https://your-postiz-domain.com/integrations/social/mastodon` * Local development: `http://localhost:4200/integrations/social/mastodon` * Docker: `http://localhost:5000/integrations/social/mastodon` Run the following curl command in a terminal to get the Mastodon client id and client secret. ```bash theme={null} curl -X POST -sS https://mastodon.social/api/v1/apps -F "client_name=YourAppName" -F "redirect_uris=http://localhost:4200/integrations/social/mastodon" -F "scopes=write:statuses write:media profile" | jq ``` This will give you output that looks something like this; ```json theme={null} { "id": "1234567890", "redirect_uris": [ "http://localhost:4200/integrations/social/mastodon" ], ... "client_id": "your_client_id", "client_secret": "your_client_secret" } ``` Make a note of your `client_id` and `client_secret` and add them to your `.env` file. ```env theme={null} MASTODON_CLIENT_ID="shown in the output from the above command" MASTODON_CLIENT_SECRET="shown in the output from the above command" MASTODON_URL="https://mastodon.social" # Change this if connecting to a different instance ``` Stop Postiz if it is running, and then start it using the .env file with the Mastodon details. Click through the new channel setup and you should be asked to login on Mastodon. ## Troubleshooting ### "Failed to fetch" / "fetch failed" when connecting The Postiz backend needs network access to reach your Mastodon instance. If the connect call fails at this stage, the backend container couldn't resolve or reach the `MASTODON_URL` host. **Fix** 1. From inside the backend container, run `curl -I https://your-instance.example.com/`. If that fails, fix DNS/egress before retrying. 2. If you're behind a corporate proxy, set `HTTPS_PROXY` on the backend. 3. Confirm `MASTODON_URL` exactly matches your instance — protocol included, no trailing slash issues. 4. If the logs show `Error: Blocked IP`, your instance hostname resolves to a private IP and the SSRF guard rejected it, see the media troubleshooting below. ### "fetch failed" when posting with media (text posts work) Text posts publish fine, but posts with images fail with `fetch failed`, and the orchestrator logs show `Error: Blocked IP`. **Cause:** before uploading media to your instance, Postiz downloads it from its own public media URL (e.g. `https://postiz.example.com/uploads/...`). If that hostname resolves to a private IP from inside the container, common behind home reverse proxies like Caddy, split DNS, or hairpin NAT, the SSRF guard blocks the fetch. **Fix** 1. Set `DISABLE_SSRF_PROTECTION=true` on the backend and orchestrator containers. 2. Restart the containers. Alternatively, fix resolution instead: make the media hostname resolve to a reachable address from inside the container (e.g. a Docker DNS alias or split-horizon DNS entry) and keep the protection on. Note this disables SSRF protection globally, only do it when Postiz runs on a trusted private network. See [`DISABLE_SSRF_PROTECTION`](/configuration/reference#disable_ssrf_protection). # MeWe Source: https://docs.postiz.com/providers/mewe How to add MeWe to your system MeWe's Developer Program is currently in beta with limited spots. Your application will be reviewed, and selected participants will be granted access to the API. MeWe supports posting to your personal timeline as well as to groups you belong to, including photo attachments. Head over to the [MeWe Developer Portal](https://dev.mewe.com/) and sign in with your MeWe account. Submit an application to join the MeWe Developer Program. The MeWe team will review your request and grant access once approved. Once approved, go to your [MeWe Developer Settings](https://mewe.com/developer) and create a new application. When selecting the application type, choose **Standalone App** since Postiz operates as an external application that connects to MeWe's API. Configure the permissions your application requires for posting and group access. **Your MeWe OAuth2 Redirect URI:** * Production: `https://your-postiz-domain.com/integrations/social/mewe` * Local development: `http://localhost:4200/integrations/social/mewe` * Docker: `http://localhost:5000/integrations/social/mewe` MeWe requires HTTPS for redirect URIs in production. Make sure your Postiz instance is served over HTTPS. From your MeWe Developer Settings, copy the **App ID** and **API Key** for your application, and add them to your `.env` file: ```env theme={null} MEWE_APP_ID="your_app_id" MEWE_API_KEY="your_api_key" ``` Keep your API Key confidential. Never expose it in client-side code or public repositories. All API requests using the API Key should originate from your backend server. Restart Postiz to apply the new environment variables. If you are using Docker Compose, run `docker compose down` and then `docker compose up`. Go to the Postiz web interface, click on "Add Channel", and select **MeWe**. You will be redirected to MeWe to authorize the connection. Once authorized, you can choose to post to your **Timeline** or to a specific **Group** when creating posts. ## Posting Options When creating a post for MeWe in Postiz, you can configure: * **My Timeline** — Posts directly to your personal MeWe timeline. * **Group** — Posts to a specific MeWe group you belong to. You will be prompted to select the target group from a dropdown. MeWe supports text posts with optional photo attachments. Video uploads are not currently supported through this integration. # Providers Overview Source: https://docs.postiz.com/providers/overview Configure social media providers for Postiz You can see all the providers that Postiz supports documented in the sidebar, under "**Providers**". Please note that no providers are configured by default. You will need to configure them all in your `.env` file, or as environment variables. You will need to restart Postiz whenever you change environment variables. If you are using docker compose, you must run `docker compose down` and then `docker compose up` to rebuild the containers with the updated variables. ## Available Providers Post to X/Twitter Post to LinkedIn profiles Post to LinkedIn pages Post to Facebook pages Post to Instagram Post to Threads Post to Bluesky Post to Mastodon Post to YouTube Post to Google Business Profile Post to TikTok Post to Reddit Post to Pinterest Post to Discord Post to Slack Post to Telegram Post to Dribbble Post to Skool communities Post to Whop forums Post to MeWe Post to Farcaster / Warpcast ## Providers available via Public API only These platforms are fully supported for scheduling and publishing through the [Public API](/public-api/introduction), but don't currently have an in-app channel-connect UI page. You can still configure their env-var keys (see [Configuration Reference](/configuration/reference)) and use them via the API. | Platform | Public API page | | --------- | ------------------------------------------------------------------ | | Twitch | [/public-api/providers/twitch](/public-api/providers/twitch) | | Kick | [/public-api/providers/kick](/public-api/providers/kick) | | VK | [/public-api/providers/vk](/public-api/providers/vk) | | Nostr | [/public-api/providers/nostr](/public-api/providers/nostr) | | Lemmy | [/public-api/providers/lemmy](/public-api/providers/lemmy) | | Medium | [/public-api/providers/medium](/public-api/providers/medium) | | Dev.to | [/public-api/providers/devto](/public-api/providers/devto) | | Hashnode | [/public-api/providers/hashnode](/public-api/providers/hashnode) | | WordPress | [/public-api/providers/wordpress](/public-api/providers/wordpress) | | Listmonk | [/public-api/providers/listmonk](/public-api/providers/listmonk) | # Pinterest Source: https://docs.postiz.com/providers/pinterest How to add Pinterest to your system This integration requires that you have a Pinterest Company Account. Head to [Pinterest Developer Dashboard](https://developers.pinterest.com/apps/) and create your App. Fill out all required Information and wait on the App to get approved. Copy the App ID at "App id" and the Secret Key at "App secret key" Copy App ID and Secret **Your Pinterest OAuth2 Redirect URI:** * Production: `https://your-postiz-domain.com/integrations/social/pinterest` * Local development: `http://localhost:4200/integrations/social/pinterest` * Docker: `http://localhost:5000/integrations/social/pinterest` Setup of Redirect URIs ```env theme={null} PINTEREST_CLIENT_ID="" PINTEREST_CLIENT_SECRET="" ``` You should now be able to add the Pinterest Provider to your User / Team Account. # Reddit Source: https://docs.postiz.com/providers/reddit How to add Reddit to your system Head over to [Reddit developers](https://www.reddit.com/prefs/apps) and click on **create a new app**. * **Name:** `MyPostizInstance` (or whatever you like) * **App type:** `web app` * **Redirect URI:** (see below) **Your Reddit OAuth2 Redirect URI:** * Production: `https://your-postiz-domain.com/integrations/social/reddit` * Local development: `http://localhost:4200/integrations/social/reddit` * Docker: `http://localhost:5000/integrations/social/reddit` Copy the Reddit client id and client secret and add them to your `.env` file. Reddit ```env theme={null} REDDIT_CLIENT_ID="" REDDIT_CLIENT_SECRET="" ``` # Skool Source: https://docs.postiz.com/providers/skool How to add Skool to Postiz Skool is a **cookie-based** integration that uses the Postiz browser extension to connect. Unlike OAuth-based providers, Skool authentication works by extracting session cookies from your browser. Using a browser extension to interact with a platform may violate its terms of service and could result in your account being suspended or banned. Postiz does not take responsibility for any issues arising from the use of this method. ## Prerequisites Before connecting Skool, you need: 1. The **Postiz Chrome Extension** installed — see the [Chrome Extension guide](/configuration/chrome-extension) for setup instructions. 2. The `EXTENSION_ID` environment variable configured in your Postiz instance. 3. An active **Skool account** — you must be logged in to [skool.com](https://www.skool.com) in the same browser where the extension is installed. ## Connecting Skool Open [skool.com](https://www.skool.com) in Chrome and make sure you are logged in to your Skool account. In Postiz, click **Add Channel** and select **Skool**. You will see a warning about browser extension usage — read it and click **I understand, continue**. The extension will automatically extract your Skool session cookies and connect your account. When creating a post for Skool, you will need to configure: * **Title** — The title of your Skool post (required). * **Group** — Select which Skool group to post in. * **Label** — Select a label for the post, or use the default. You can also attach images to your post. Files are uploaded directly to Skool's file storage. ## Features * **Post to groups** — Publish posts to any Skool group you are a member of. * **Labels** — Assign labels to categorize your posts. * **Comments** — Schedule threaded comments on your posts. * **Image attachments** — Attach images to posts and comments. * **Automatic cookie refresh** — The extension refreshes your cookies every 24 hours to keep the connection alive. ## Troubleshooting * **"Extension not found"** — Make sure the Postiz Chrome Extension is installed and that `EXTENSION_ID` is correctly set. See the [Chrome Extension guide](/configuration/chrome-extension). * **"Could not get cookies"** — Log in to [skool.com](https://www.skool.com) in Chrome before connecting. * **"You can't post to this channel"** — You must be an admin or have sufficient permissions in the Skool group. * **"Cannot post to this label"** — The selected label may have restrictions. Try a different label or the default. * **Session expires** — If your session expires, reconnect the integration. The extension will attempt to refresh cookies automatically every 24 hours. # Slack Source: https://docs.postiz.com/providers/slack How to add Slack to your system This integration requires that you have a Slack Workspace Head to [Slack Applications Dashboard](https://api.slack.com/apps) and select "From scratch", fill out all the required Information. **Your Slack OAuth2 Redirect URI:** * Production: `https://your-postiz-domain.com/integrations/social/slack` * Local development: `http://localhost:4200/integrations/social/slack` * Docker: `http://localhost:5000/integrations/social/slack` Head to Features > OAuth & Permissions 1. Add the redirect URI (see above) 2. At Scopes > Bot Token Scopes, add these Scopes: * `chat:write` * `channels:read` * `users:read` * `groups:read` * `channels:join` Head back to Settings > Basic Information > Display Information Now set an Icon that meets these Requirements: 1. It has to be a Square 2. It has to be 512px×512px to 2000px×2000px If you do not set an App Icon, Postiz won't let you install the Integration. Head back to App Credentials, copy the Client ID and Secret and paste it to your Environment: ```env theme={null} SLACK_ID="" SLACK_SECRET="" ``` # Telegram Source: https://docs.postiz.com/providers/telegram How to add Telegram to your system Postiz talks to Telegram using long-polling (`getUpdates`). Telegram allows only **one** connected process per bot token at a time — if you run Postiz against the same `TELEGRAM_TOKEN` from two places (e.g. self-hosted plus cloud, or two self-hosted instances), each instance will continuously kick the other off and you'll see `409 Conflict: terminated by other getUpdates` errors in the logs. Use one bot token per Postiz deployment. If you need Telegram on multiple environments, create a separate bot for each. 1. Open Telegram and message [@BotFather](https://t.me/botfather). 2. Click Start 3. Click Menu * Click "Create a new bot" * Enter a name for your bot (e.g., `MyPostizBot`). ![Name Bot](https://github.com/user-attachments/assets/974c1ad3-4648-4d41-8d20-22b6e8cb0bc8) * Choose a unique username ending with "bot" (e.g., `MyPostizBot_bot`). ![Unique Bot Name](https://github.com/user-attachments/assets/e4824ed2-c812-4a03-b43a-bf4f680eff23) Once your bot is created, **BotFather** will give you an **API Token**. Keep it safe—you'll need it later. ![Bot Token](https://github.com/user-attachments/assets/71bdc32c-36d5-45cd-b0d8-08d5eefcaadc) 1. Click on "Menu" 2. Click on "Edit your bots" 3. Select your bot 4. Click on "Bot Settings" ![Bot Settings](https://github.com/user-attachments/assets/908924ee-2567-4c13-bfeb-52f5684680a3) 5. Click on "Group Privacy" ![Group Privacy](https://github.com/user-attachments/assets/6a756212-7af3-437f-88fe-74ca5579666d) 6. If "Privacy mode" is enabled, turn it off (it is enabled by default) 1. Navigate to your group/channel 2. Add your bot to your group/channel 3. The bot requires these permissions to work with Postiz: * access to messages — so the bot can read messages sent in the group/channel * Send Text Messages — so the bot can send messages * Send Media — so the bot can send media While not strictly required, making your bot an `admin` is **recommended**. It will give the bot all the permissions needed and make the setup easier and faster. In your `.env` file, add the **Telegram Bot Name** (Without the @) and the **Telegram Bot API Token** that you received from **BotFather** in Step 1: ```env theme={null} TELEGRAM_BOT_NAME="MyPostizBot_bot" TELEGRAM_TOKEN="MyPostizBot token" ``` You should be able to connect your group/channel to Postiz now! # Threads Source: https://docs.postiz.com/providers/threads How to add Threads to your system This integration requires that you have setup a Meta for Developers account already. You can start by going to the [Meta/Facebook Developer Portal](https://developers.facebook.com/apps). This is a complex integration, and it may take some time to get it right. If you have any issues, please reach out to us on the Postiz Discord. Threads requires the `THREADS_APP_ID` and `THREADS_APP_SECRET` environment variables on the **backend**. If either is missing or unset when the backend starts, Threads will not appear in the channel list at all — you'll think the provider is broken when really the env vars never got loaded. After adding them, **restart Postiz** and confirm Threads is offered in the Add Channel dialog before troubleshooting further. Go to Meta for Developers and [create a new app](https://developers.facebook.com/apps/creation/). Give your app a name and email. Create app Select *Access the Threads API*. Request access Add business details If you're unable to skip or move to the next step, and get a message saying "There are no business portfolios available to connect to this app", go to your [Meta Business Suite Settings](https://business.facebook.com/latest/settings/business_users) and make sure you've enabled Two-Factor Authentication (2FA) for your account. You should not have any extra requirements to publish and maintain access. On the app dashboard, Select "Access the Threads API" to begin customizing the API access. Add the "threads\_content\_publish" and "threads\_basic" (automatically selected) to your app. Configure API access * Click the "Settings" tab. Copy your "Threads App ID", and set the postiz environment variables `THREADS_APP_ID` to this value. * Click the "Show" button next to the Threads App Secret, and set the postiz environment variable `THREADS_APP_SECRET` to this value. Note that the next box is quite small, make sure you scroll across the copy the full value. It should be 32 characters long. **Your Threads OAuth2 Redirect URI:** * Production: `https://your-postiz-domain.com/integrations/social/threads` * Local development: `http://localhost:4200/integrations/social/threads` * Docker: `http://localhost:5000/integrations/social/threads` You have to "click" the URL to make it active, otherwise the form will not save. You can use the same value for the **Uninstall Callback URL** and **Delete Callback URL**, but note that Postiz does not support either at this time. The form will not save unless you enter something. Set threads API settings Go back to the 'Dashboard' view of the Facebook developers portal and click "Finish customization". Make sure you clicked through the setup wizard, and select "Yes I'm finished" to complete the setup. The API may not work until you've done this. Stop Postiz if it is running, and then start it again to pick up the new environment variables. You should not try to add a Threads account to Postiz at this time. * In the sidebar go to "App roles" -> "roles". * Select the "Testers" tab. Click "Add People". * Under *Additional Roles for this App*, select *Threads Tester*. * Enter usernames of the threads users you want to test the app. Probably your own username. Note that this is probably different from your Meta developers account which is tied to Facebook. * On threads.com, open your [account settings](https://www.threads.net/settings/account); * Open [Website permissions](https://www.threads.com/settings/website_permissions), and switch to the "Invites" tab; * If all has gone well, you should have a pending invite. Accept that invite. Threads invite * Go back to the Meta developers portal, and in the sidebar, click *Testing*, and then open the *Open Graph API Explorer*. * In the header, dropdown the API selector and change it to threads.net v1. API Version * In the right sidebar, under *Access Token*, click *Generate Threads Access Token*. This will open a new window where you can select the Threads account you want to test with - it should be an account that accepted the earlier invite. If everything has worked successfully you should be provided with a very long alphanumeric access token - you do not need to do anything with this, but it proves things are working correctly. At this stage things should be working correctly, try a test post from Postiz to confirm. This is a complex integration, and it may take some time to get it right. If you have any issues, please reach out to us on the Postiz Discord. # TikTok Source: https://docs.postiz.com/providers/tiktok How to add TikTok to your system This integration requires that you have a TikTok developer account. It also requires that you have a public website, with https, and can upload files to that site to verify ownership. TikTok will also not allow http\:// for your app redirect URI, so you will need to be accessing Postiz from HTTPS. **NOTE:** TikTok fetches media via pull\_from\_url. Your media files must be publicly reachable over HTTPS; localhost or private routes (e.g., /uploads) will fail. Expose your uploads via a reverse proxy (e.g., [Caddy](/reverse-proxies/caddy)) or use object storage/CDN such as [Cloudflare R2](/configuration/r2) with public access. Ensure the media domain is listed under your TikTok developer account's verified sites. TikTok approval is not the final step for Direct Post access. TikTok allows unaudited API clients to use the Direct Post API, but posts are restricted to private visibility until the API client completes TikTok's audit process for Terms of Service compliance. If your API client has not passed this audit, TikTok also limits Direct Post usage to up to 5 users posting in a 24-hour window, and those user accounts must be private at the time of posting. See TikTok's [Direct Post API developer guidelines](https://developers.tiktok.com/doc/content-sharing-guidelines#direct_post_api_-_developer_guidelines) for the current requirements. In practice this means: before your TikTok developer app passes audit, the `privacy_level` setting for posts is forced to **SELF\_ONLY** (private) regardless of what you choose in Postiz. Once your app is audited, all privacy levels become available. **Known issue:** the `/integrations/social/tiktok` connect page can crash for some accounts. If you see a blank page or "cannot read properties of undefined" error, retry from a fresh incognito window. See [Known Issues](/troubleshooting/known-issues). Go here: [https://developers.tiktok.com/apps](https://developers.tiktok.com/apps) Create a new app * **App Name:** `MyPostiz` * **Redirect URI:** (see below) **Your TikTok OAuth2 Redirect URI:** * Production: `https://your-postiz-domain.com/integrations/social/tiktok` * Local development (HTTPS required): `https://localhost:4200/integrations/social/tiktok` * Docker (HTTPS required): `https://localhost:5000/integrations/social/tiktok` This needs to be on a public domain that you have access to, that is hosted on HTTPS. Tick "Web" for your platforms. Add the "Login Kit" and "Content Posting API" to your app. For "Login Kit", set the redirect URI to [http://localhost:4200/integrations/social/tiktok](http://localhost:4200/integrations/social/tiktok) For Content posting API, enable "Direct Post". Add the following scopes: * user.info.basic * video.create * video.publish * video.upload * user.info.profile These can be found immediately after registering your application. The client ID is 16 characters long and the secret is 32 characters long. ```env theme={null} TIKTOK_CLIENT_ID=1234567890123456 TIKTOK_CLIENT_SECRET=12345678901234567890123456789012 ``` Restart Postiz with the updated environment variables Go to the Postiz web interface, and click on the "Add Channel" button. Select "TikTok" from the list of available channels. You should be redirected to TikTok to authorize the application. ## Posting settings When composing a TikTok post, the "How do you want to post?" setting decides which other settings apply: * **Post directly to TikTok** publishes the post and applies all the settings below. * **Upload without posting** does **not** publish: it sends the media to the account's TikTok app inbox, where the owner must finish and publish it manually within 24 hours or it is discarded. TikTok keeps only the post content/title in this mode, so the settings panel collapses to hide the other settings while it is selected. Some settings also depend on the media type: * **Allow Duet**, **Allow Stitch**, and **Video made with AI** apply to videos only, TikTok has no equivalent for photo posts. * **Auto add music** applies to photos only. * Privacy level, comments, and the branded-content toggles apply to both videos and photos. # Whop Source: https://docs.postiz.com/providers/whop How to add Whop to Postiz This integration requires that you have a [Whop](https://whop.com) account with at least one company and a forum experience set up. Go to the [Whop Developer Portal](https://dash.whop.com/settings/developer) and create a new application. Copy the **Client ID** from your Whop application settings and add it to your `.env` file: ```env theme={null} WHOP_CLIENT_ID="your_client_id" ``` **Your Whop OAuth2 Redirect URI:** * Production: `https://your-postiz-domain.com/integrations/social/whop` * Local development: `http://localhost:4200/integrations/social/whop` * Docker: `http://localhost:5000/integrations/social/whop` Add this redirect URI in your Whop application settings. Go to the Postiz web interface, click **Add Channel**, and select **Whop**. You will be redirected to Whop to authorize the connection. When creating a post for Whop, you will need to configure: * **Company** — Select which Whop company to post under. * **Forum** — Select the forum (experience) to post in. * **Title** — An optional title for your forum post. The post content supports **Markdown** formatting. You can also attach files to your post. ## Features * **Post to forums** — Publish posts to any Whop forum you have access to. * **Markdown support** — Write posts using full Markdown formatting. * **Comments** — Schedule threaded comments on your posts. * **File attachments** — Attach images and files to posts and comments. * **Token refresh** — Postiz automatically refreshes your Whop access token to keep the connection alive. ## Troubleshooting * **"Invalid token, please re-authenticate"** — Your access token has expired or been revoked. Reconnect the integration by removing and re-adding the Whop channel. * **"Insufficient permissions"** — Your Whop account may not have the required scopes. Re-authenticate to grant the necessary permissions. * **"Forum or experience not found"** — The selected forum may have been deleted or you no longer have access to it. Select a different forum. * **No companies appear** — Make sure your Whop account owns or manages at least one company. * **No forums appear** — The selected company must have at least one forum experience created. Set up a forum in your Whop company dashboard first. # X (Twitter) Source: https://docs.postiz.com/providers/x-twitter How to add X to your system Watch the YouTube Tutorial: [https://m.youtube.com/watch?si=swqzAXiSTFOXZiFo\&v=3WneMPnOu88](https://m.youtube.com/watch?si=swqzAXiSTFOXZiFo\&v=3WneMPnOu88) X is a bit different. They created an oAuth2 flow, but it works only with Twitter v2 API. But in order to upload pictures to X, you need to use the old Twitter v1 API. So you are going to use the normal oAuth1 flow for that (that supports Twitter v2 also 🤷🏻‍). Head over the [Twitter developers page](https://developer.twitter.com/en/portal/dashboard) and create a new app. Click to sign-up for a new free account X Click to edit the application settings X Click to set up an authentication flow X * In the App Permission set it to `Read and Write` * In the Type of App set it to `Native App` * In the App Info set the `Callback URI / Redirect URL` You must select `Native App` for OAuth 1.0a to work correctly. Selecting `Web App, Automated App or Bot` will cause authentication to fail with error code 32. **Your X OAuth2 Redirect URI:** * Production: `https://your-postiz-domain.com/integrations/social/x` * Local development: `http://localhost:4200/integrations/social/x` * Docker: `http://localhost:5000/integrations/social/x` * If X requires HTTPS for localhost: `https://redirectmeto.com/http://localhost:4200/integrations/social/x` Save it and go to "Keys and Tokens" tab. Click on "Regenerate" inside "Consumer Keys" and copy the `API Key` and `API Key Secret`. Open .env file and add the following: ```env theme={null} X_API_KEY="" X_API_SECRET="" ``` ## Optional environment variables * `X_URL` — override the X API base URL (e.g. for self-hosted X-compatible APIs). * `DISABLE_X_ANALYTICS=true` — skip the analytics fetch on the X channel page. Useful if your X plan doesn't include analytics access and the failed call is noisy. * `STRIP_LINKS_FROM_X_POSTS=true` — automatically remove URLs from post text before publishing. Some accounts see better reach with no links; this enforces it globally. ## Post Settings When creating a post for X, you can configure the following options: * **Who can reply** — Control who can reply to your post: everyone, accounts you follow, mentioned accounts, subscribers, or verified accounts. * **Post to a community** — Optionally share your post to an X community by providing the community URL. * **Made with AI** — Mark your post as containing AI-generated content. When enabled, the post will be labeled accordingly on X. * **Paid partnership** — Mark your post as a paid promotion. When enabled, the post will be labeled as a paid partnership on X. # YouTube Source: https://docs.postiz.com/providers/youtube How to Add YouTube to Your System Watch the YouTube Tutorial: [https://youtu.be/b8fxx6DqIAw](https://youtu.be/b8fxx6DqIAw) Follow the instructions as available in the [Obtaining authorization credentials](https://developers.google.com/youtube/registering_an_application). ## General Setup Make sure you are logged in to your Google account and visit the [Credentials - APIs & Services](https://console.cloud.google.com/projectselector2/apis/credentials) page. Make sure to read the terms and conditions and "Agree and Continue". Create a new project by clicking on the "Create Project" button. Fill in the project name, and details and click "Create". Create credentials by clicking on the "Create Credentials" button. Select the "OAuth client ID" option. Make sure that your consent screen has been configured. Create the OAuth client ID. Select "Web application" as the application type and fill in the details. **Your YouTube OAuth2 Redirect URI:** * Production: `https://your-postiz-domain.com/integrations/social/youtube` * Local development: `http://localhost:4200/integrations/social/youtube` * Docker: `http://localhost:5000/integrations/social/youtube` Under "Authorized redirect URIs", insert your OAuth2 Redirect URI. YouTube After following all of the steps above you should be met with a screen that shows your client ID and client secret. Add these to your providers configuration. ```env theme={null} YOUTUBE_CLIENT_ID="" YOUTUBE_CLIENT_SECRET="" ``` Add yourself as a test user of the application YouTube Go to "Enabled APIs and Services". Then click on "Enable APIs and Services". Search "YouTube Data API v3" and activate the API by selecting it and clicking "Enable". Do the same Process with "YouTube Analytics API" and "YouTube Reporting API". Enabled APIs Enabled APIs ## Additional Steps for Brand Accounts When using a Brand account you will need to set your APP to External and setup a test user. You do not need to publish the APP, but it does take time for the changes to propagate. You will also need to add the app to the trusted apps within your google workspace Admin. Sign in Go to Security → Access and data Controls → API Controls Click Manage Third Party App Access Click "Configure new App" Put your Client ID for the app you created in previous steps into the search box and select your app Set scopes and Google Data Access for the app to Trusted Once set, click save Wait at least 5 hours for the changes to propagate. After this time you should now be able to add your YouTube channel to your Postiz account # Platform Analytics Source: https://docs.postiz.com/public-api/analytics/platform GET /analytics/{integration} Get analytics data for a specific integration/channel. Returns metrics like followers, impressions, engagement, etc. depending on the platform. ## Path parameter: `integration` The `{integration}` path parameter must be the integration's **`id`** (the UUID-shaped string returned by `GET /public/v1/integrations`). It is **not** the platform `__type` (e.g. `x`, `linkedin`) and **not** the channel display name. Passing either of those will return `400 Invalid integration`. ```bash theme={null} # Get the integration ID curl -H "Authorization: your-api-key" \ https://api.postiz.com/public/v1/integrations # Use the returned id curl -H "Authorization: your-api-key" \ https://api.postiz.com/public/v1/analytics/ ``` ## Common errors * `400 Invalid integration` — the path param is the wrong shape. * `404 Channel not found` — the integration exists but has been disconnected. Reconnect the channel from the Postiz UI. * Empty data — some platforms (notably Google My Business) restrict analytics by date range or by profile verification status. The endpoint returns an empty payload rather than an error. # Post Analytics Source: https://docs.postiz.com/public-api/analytics/post GET /analytics/post/{postId} Get analytics data for a specific published post. Returns metrics like likes, comments, shares, impressions, etc. depending on the platform. # Connect Channel (OAuth) Source: https://docs.postiz.com/public-api/integrations/connect GET /social/{integration} Generate an OAuth authorization URL for a given integration. Use this to connect a new social media channel. Only OAuth-based integrations are supported (integrations that require an external URL, such as Mastodon, are not available via this endpoint). # Delete Channel Source: https://docs.postiz.com/public-api/integrations/delete DELETE /integrations/{id} Delete a connected channel by its integration ID. Any scheduled posts associated with this channel will also be deleted. # Find Available Slot Source: https://docs.postiz.com/public-api/integrations/find-slot GET /find-slot/{id} Get the next available time slot for posting to a specific channel. # List Groups (Customers) Source: https://docs.postiz.com/public-api/integrations/groups GET /groups Returns all customers (groups) for your organization. Use a group ID to filter the integrations list. # Check Connection Source: https://docs.postiz.com/public-api/integrations/is-connected GET /is-connected Verify if your API key is valid and connected. # List Integrations Source: https://docs.postiz.com/public-api/integrations/list GET /integrations Returns all connected social media channels for your organization. # Get Settings & Tools Source: https://docs.postiz.com/public-api/integrations/settings GET /integration-settings/{id} Returns the posting rules, maximum content length, settings schema, and available provider tools for a connected channel. Use this to discover which tools can be executed with the trigger endpoint. # Trigger a Tool Source: https://docs.postiz.com/public-api/integrations/trigger POST /integration-trigger/{id} Executes a provider-specific tool on a connected channel, for example searching Instagram audio for Reels, listing Discord channels, or fetching Reddit flairs. Discover available tools with the integration settings endpoint. Some providers expose helper tools for fetching dynamic data needed when constructing post settings — for example searching [Instagram audio](/public-api/providers/instagram#audio) to attach to a Reel, listing Discord channels, or fetching Reddit flairs. Discover the tools available for a channel with the [settings endpoint](/public-api/integrations/settings) — each tool entry contains the `methodName` to pass here together with its parameter schema. # API Overview Source: https://docs.postiz.com/public-api/introduction Getting started with the Postiz Public API For N8N, check out this video: [https://www.youtube.com/watch?v=c50u3K3xsCI](https://www.youtube.com/watch?v=c50u3K3xsCI) ## SDKs & Integrations Official Postiz NodeJS SDK Custom n8n node for Postiz ## Authentication There are two ways to authenticate with the Postiz API: ### API Key Get your API key from **Settings > Developers > Public API**. Include it in the `Authorization` header: ```bash theme={null} curl -H "Authorization: your-api-key" https://api.postiz.com/public/v1/integrations ``` ### OAuth2 Token If you're building an app for other Postiz users, use [OAuth2 Authentication](/public-api/oauth) to get tokens that act on behalf of users. OAuth tokens start with `pos_` and are used the same way: ```bash theme={null} curl -H "Authorization: pos_your-oauth-token" https://api.postiz.com/public/v1/integrations ``` ## Base URL | Environment | Base URL | | ------------ | --------------------------------------------- | | Postiz Cloud | `https://api.postiz.com/public/v1` | | Self-hosted | `https://{NEXT_PUBLIC_BACKEND_URL}/public/v1` | ## Rate Limits **90 requests per hour** (100 for the cloud) limit applies to only the create post endpoint. This doesn't mean you can only post 90 times per hour—each API call counts as one request. Schedule multiple posts in a single request to maximize throughput. The rate limit is a single global value for the whole instance — it doesn't tier by subscription plan. Plans tier on channel and post-per-month quotas instead. Self-hosters can adjust the per-hour limit with the `API_LIMIT` env var (see [Configuration Reference](/configuration/reference)). ## Errors | Status | Meaning | | ----------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `400 Bad Request` | The request body or path parameter is malformed (wrong shape, unknown enum, missing required field). | | `401 Unauthorized` | `Authorization` header is missing or the API key is unrecognised. | | `403 Forbidden` | The API key is valid but doesn't own the resource (e.g. you tried to delete a post in another organisation). | | `404 Not Found` | The endpoint doesn't exist, or the path parameter (integration ID, post ID) was correct format but no row matched. | | `413 Payload Too Large` | Your request body exceeded 50 MB on `/posts` — usually because images were base64-inlined instead of pre-uploaded. See [Uploads troubleshooting](/troubleshooting/uploads). | | `429 Too Many Requests` | You exceeded `API_LIMIT` per hour on the create-post endpoint. | | `5xx` | Server error — retry with exponential backoff. | For `DELETE` endpoints, `404` always means "already deleted" and is safe to ignore. A `500` *can* mean the same thing today because of a [known issue](/troubleshooting/known-issues) where a missing post ID surfaces as 500 instead of 404 — but only if the error matches that specific signature. Treat other `500` responses as real server errors: log them, retry with exponential backoff, and don't silently suppress them. ## Terminology The Postiz UI uses the term **channel**, while the API uses **integration**. They refer to the same thing—a connected social media account. ## Generate Output The easiest way to generate your post payloads is by using this wizard. It's the same wizard to schedule posts in the Postiz app, however instead of scheduling posts, it generates the JSON payload for you to use in your API requests. * For cloud, make sure you are logged in. * For local, make sure your Postiz server is running and your are logged in.