postflow
Health Warn
- No license — Repository has no license file
- Description — Repository has a description
- Active repo — Last push 0 days ago
- Community trust — 17 GitHub stars
Code Fail
- rm -rf — Recursive force deletion command in scripts/backup.sh
- rm -rf — Recursive force deletion command in scripts/restore.sh
Permissions Pass
- Permissions — No dangerous permissions requested
No AI report is available for this listing yet.
Self-hosted social media publishing: web UI, HTTP API, MCP endpoint and CLI in a single service
postflow
⚠️ Disclaimer: postflow is in active development. It is not tested enough yet to guarantee correct behavior in all scenarios. Use at your own risk.
postflow is a lightweight social publishing service with:
- Web UI
- HTTP API
- MCP endpoint (Streamable HTTP)
- CLI (
postflow)
This README is a basic setup guide.
1) Local Setup (5 minutes)
Requirements
- Go 1.26.11+
- Optional: Homebrew (for installing CLI binary)
Start locally
git clone https://github.com/antoniolg/postflow.git
cd postflow
cp .env.example .env
Generate required secrets:
# 32-byte base64 key (required)
openssl rand -base64 32
# API token (recommended)
openssl rand -hex 32
Put those values in .env:
POSTFLOW_MASTER_KEY=<base64-from-openssl>
API_TOKEN=<hex-token>
PUBLIC_BASE_URL=http://localhost:8080
[email protected]
OWNER_PASSWORD_HASH=<bcrypt-hash>
POSTFLOW_DRIVER=mock
Generate OWNER_PASSWORD_HASH with the helper script in this repo:
go run ./scripts/hash-password.go 'replace-with-your-password'
If you store it in a local .env, quote the value because bcrypt hashes contain $:
OWNER_PASSWORD_HASH='$2a$10$...'
Run:
go run ./cmd/postflow-server
Open:
- UI:
http://localhost:8080 - MCP:
http://localhost:8080/mcp
2) Environment Variables (and where to get them)
Use .env.example as template. These are the key ones:
Core (recommended in all setups)
| Variable | Required | Where it comes from |
|---|---|---|
POSTFLOW_MASTER_KEY |
Yes | Generate locally: openssl rand -base64 32 |
API_TOKEN |
Recommended | Generate locally (random token), kept for API/MCP auth for CLI, Codex, Claude, and other legacy clients |
OWNER_EMAIL |
Recommended for UI/ChatGPT | Owner email for the single-user local login |
OWNER_PASSWORD_HASH |
Recommended for UI/ChatGPT | Bcrypt hash for the owner password |
PUBLIC_BASE_URL |
Yes for OAuth and Instagram media URLs | Your app URL (http://localhost:8080 locally, your public HTTPS domain in prod) |
UI_BASIC_USER / UI_BASIC_PASS |
Temporary compatibility only | Optional legacy Basic Auth fallback for the UI |
Storage/runtime
| Variable | Default | Notes |
|---|---|---|
PORT |
8080 |
HTTP port |
DATABASE_PATH |
postflow.db |
SQLite DB path |
DATA_DIR |
data |
Uploaded media path |
Network credentials (only if you use that network)
| Network | Variables | Where to get them |
|---|---|---|
| X | X_CLIENT_ID, X_CLIENT_SECRET |
X Developer Portal OAuth 2.0 app credentials for account connection |
LINKEDIN_CLIENT_ID, LINKEDIN_CLIENT_SECRET |
LinkedIn Developer app with member posting enabled. Company page connections additionally require organization posting/admin scopes. | |
| Facebook/Instagram | META_APP_ID, META_APP_SECRET |
Meta Developers app |
Important:
- If you want real publishing, set
POSTFLOW_DRIVER=live. - For local testing without real publishing, keep
POSTFLOW_DRIVER=mock. - OAuth account connection is available for X, LinkedIn, Facebook, and Instagram.
- LinkedIn OAuth connects personal profiles by default. Use the LinkedIn organization connection action, or
account_kind=organizationon/oauth/linkedin/start, to request company page scopes. - LinkedIn root posts with a first
http(s)link and no attached media are published as article posts at publish time so PostFlow can send explicit unfurl metadata. If media is attached, media wins and link unfurl is skipped. - LinkedIn article posts send their text in LinkedIn's "little" text format, so PostFlow backslash-escapes its reserved characters (links included) and the post renders exactly as written, while
#hashtagsstay clickable. Other LinkedIn posts and comments are sent as plain text. - In the web UI, if an OAuth provider returns multiple accounts, PostFlow shows a selection step before saving them.
- OAuth accounts in error show a reauthorize action. Recovery is bound to the existing account identity, and credentials are replaced only when the provider returns the same account.
- Publish failure emails are configured from Settings, the CLI, or MCP. SMTP passwords are stored encrypted with
POSTFLOW_MASTER_KEY. - In production (Coolify), set secrets in the platform UI, not in committed files.
- In Coolify, mark
OWNER_PASSWORD_HASHas a literal/secret value so$is not interpolated.
3) MCP Setup (for LLMs)
Endpoint:
http://localhost:8080/mcp
For Codex, Claude, CLI, and other legacy clients, if API_TOKEN is set, send:
Authorization: Bearer <API_TOKEN>
For ChatGPT / remote MCP clients with OAuth:
- Authorization metadata:
http://localhost:8080/.well-known/oauth-authorization-server - Protected resource metadata:
http://localhost:8080/.well-known/oauth-protected-resource - The login page is
http://localhost:8080/login - Dynamic client registration is available at
POST /oauth/register - PostFlow allows MCP discovery requests without auth (
initialize,notifications/initialized,ping, andtools/list) so ChatGPT can complete the handshake cleanly. - Actual MCP tool execution (
tools/call) remains protected and requires OAuth bearer auth (or the legacyAPI_TOKENflow for non-OAuth clients).
Main MCP tools available:
postflow_healthpostflow_list_schedulepostflow_list_draftspostflow_list_accountspostflow_create_static_accountpostflow_connect_accountpostflow_disconnect_accountpostflow_reauthorize_accountpostflow_set_x_premiumpostflow_delete_accountpostflow_list_failedpostflow_create_postpostflow_cancel_postpostflow_schedule_postpostflow_edit_postpostflow_delete_postpostflow_validate_postpostflow_upload_mediapostflow_list_mediapostflow_delete_mediapostflow_prune_mediapostflow_requeue_failedpostflow_delete_failedpostflow_set_timezone
Thread payload support (same shape in API/MCP/CLI):
segments:[{ "text": "...", "media_ids": ["med_x"] }]- If
segmentsis present, step1is the root post and steps2..Nare follow-ups. - Publishing semantics: X follow-ups are chained as replies; other supported thread platforms publish follow-ups as comments on the root post.
- Backward compatibility is preserved for legacy
text+media_ids. postflow_edit_postaccepts optionalmedia_idsto replace media on editable posts ([]clears all media).- Editing without
intentand withoutscheduled_atpreserves the current scheduling state.
Codex CLI
codex mcp add postflow --url http://localhost:8080/mcp
~/.codex/config.toml example:
[mcp_servers.postflow]
url = "http://localhost:8080/mcp"
bearer_token_env_var = "POSTFLOW_API_TOKEN"
Then:
export POSTFLOW_API_TOKEN="<same-value-as-API_TOKEN>"
Claude Code
claude mcp add -t http postflow http://localhost:8080/mcp --header "Authorization: Bearer <API_TOKEN>"
Tip: in the app UI (settings) you can copy ready-to-use MCP snippets for Claude and Codex.
4) CLI Setup (postflow)
Option A: Homebrew (recommended)
brew tap antoniolg/tap
brew install antoniolg/tap/postflow
postflow --help
Option B: Run from source
go run ./cmd/postflow --help
Configure CLI env:
export POSTFLOW_BASE_URL="http://localhost:8080"
export POSTFLOW_API_TOKEN="<API_TOKEN>"
Common commands:
postflow health
postflow schedule list --from 2026-03-01T00:00:00Z --to 2026-03-31T23:59:59Z
postflow schedule list --view posts --from 2026-03-01T00:00:00Z --to 2026-03-31T23:59:59Z
postflow drafts list --limit 20
postflow posts validate --account-id acc_xxx --text "hello"
postflow posts validate --account-id acc_xxx --segments-json '[{"text":"root"},{"text":"reply 1"}]'
postflow posts create --account-id acc_xxx --segments-json '[{"text":"root"},{"text":"reply 1","media_ids":["med_x"]}]' --scheduled-at 2026-03-01T10:00:00Z
postflow posts schedule --id pst_xxx --scheduled-at 2026-03-01T10:00:00Z
postflow posts edit --id pst_xxx --text "copy updated" --intent schedule --scheduled-at 2026-03-01T10:30:00Z
postflow posts edit --id pst_xxx --segments-json '[{"text":"root updated"},{"text":"reply updated"}]'
postflow posts edit --id pst_xxx --text "copy + media" --replace-media --media-id med_a --media-id med_b
postflow posts delete --id pst_xxx
postflow posts cancel --id pst_xxx
postflow accounts list
postflow accounts reauthorize --id acc_xxx
postflow settings set-timezone --timezone Europe/Madrid
postflow settings set-smtp --host smtp.sendgrid.net --port 587 --username apikey --password "$SMTP_PASSWORD" --from [email protected] --to [email protected]
postflow media list --limit 20
postflow media prune --dry-run
postflow media prune
media prune deletes every media file that no pending post uses: media attached only to published or canceled posts, plus media attached to no post. Files are removed permanently, so run it with --dry-run first. It matches the "clean unused media" button in Settings, and is also available as POST /media/prune and the postflow_prune_media MCP tool.
--text and --segments-json are mutually exclusive on posts create, posts validate, and posts edit.
schedule list returns grouped publications by default. Use --view posts to inspect the raw per-post/thread rows.
5) Deploy (Coolify + GHCR image)
You can deploy from prebuilt image (no build on server):
ghcr.io/antoniolg/postflow:latest
or pinned:
ghcr.io/antoniolg/postflow:vX.Y.Z
Full production runbook:
6) Troubleshooting
401 unauthorized:- check
API_TOKEN - check
Authorization: Bearer ...in MCP/API clients
- check
- OAuth callback errors:
- verify
PUBLIC_BASE_URLmatches your real public domain - for X, verify
X_CLIENT_IDis set and the callback URL is registered in the X app settings
- verify
- Instagram media create errors (
code=9004,error_subcode=2207052):- verify
PUBLIC_BASE_URLis public/reachable from the internet (notlocalhostin production) - for image posts, upload JPEG or PNG (
.jpg/.jpeg/.png) - for video posts, use MP4 or MOV
- media uploads are capped at 512 MiB and must finish within 15 minutes
- verify
- CLI auth errors:
- verify
POSTFLOW_API_TOKENmatches serverAPI_TOKEN
- verify
7) Additional Docs
- API contract: docs/specs/openapi.yaml
- Deployment guide: docs/coolify-deploy.md
Reviews (0)
Sign in to leave a review.
Leave a reviewNo results found