EchoFlow 2 uses Meta Graph API for new Facebook Reel jobs. The existing browser uploaders remain available for legacy jobs and the other platforms. A Page-specific manual browser mode can be selected for pending/scheduled Reels or used when a Reel needs review.
From the repository root:
backend/venv/bin/python -m pip install -r backend/requirements.txt
npm install --prefix frontend
./start.commandThe frontend runs at http://localhost:3000, the API at http://127.0.0.1:8000. Run one backend process/worker per database. FFmpeg (including ffprobe) must be on the backend's PATH. Keep the computer awake and EchoFlow running until scheduled content is published. The schedule is local: at the due time EchoFlow starts uploading; Meta processing can delay public availability.
The existing activation mechanism is unchanged. This update does not provision a license.
Two supported routes:
- Page token: Accounts → Connect Page. Enter the numeric Page ID, a folder alias (
page-01…page-10) and that Page's access token. EchoFlow verifies/meagainst the supplied Page ID before saving it. Optionally enter the token's known expiry. - OAuth: Set these environment variables before starting the backend, then choose Continue with Facebook in Accounts. Configure the exact redirect URI in your own Meta app's Facebook Login settings.
export META_APP_ID='your-app-id'
export META_APP_SECRET='your-app-secret'
export META_REDIRECT_URI='http://localhost:8000/api/facebook/oauth/callback'
export META_GRAPH_VERSION='v25.0'
./start.commandUse a redirect URI accepted by your Meta app configuration; if Meta requires HTTPS, supply an HTTPS callback that routes to this local backend. The callback is bound to a one-use, ten-minute OAuth state cookie. Start and finish the flow with the same hostname. EchoFlow exchanges the authorization code, exchanges the user token for a long-lived token, and paginates /me/accounts to discover Page tokens. User tokens and app secrets are not stored in the database. Set friendly aliases after OAuth; default aliases are page-<numeric-page-id>.
The OAuth flow requests pages_show_list, pages_read_engagement, and pages_manage_posts. Your app's access level, Page role/tasks and any required Meta app review still control what it can publish. A successful identity check does not guarantee publishing permission. Use the permissions and version supported by your Meta app; the Graph version is configurable and is not an assertion that v25.0 is the latest version.
Tokens are encrypted in SQLite. The local Fernet key is created at backend/.secrets/token.key with owner-only permissions. Alternatively provide ECHOFLOW_TOKEN_KEY as an environment variable containing a Fernet key. Keep that key with a private backup of the database; losing it requires reconnecting Pages. Never commit tokens, app secrets, browser profiles or the key. The API omits token values from Page responses and validation errors. Expiry is shown as unknown if Meta did not provide it. Use Check token or reconnect; expired/revoked tokens block the job for review instead of silently switching to a browser.
EchoFlow remains a trusted, single-user local application. Bind it to loopback. The existing license screen is not network authentication; do not expose this API publicly without adding authentication and deployment hardening.
EchoDropzone/
page-01/
reel-001.mp4
reel-001.json
page-07/
reel-001.mp4
reel-001.json
Simple format (reel-001.json):
{
"platform": "facebook",
"account": "page-07",
"video": "reel-001.mp4",
"caption": "আজকের নতুন ভিডিও ❤️",
"schedule": {
"date": "2026-09-10",
"time": "20:00",
"timezone": "Asia/Dhaka"
},
"options": { "publish_as_reel": true }
}Advanced format:
{
"account": "page-07",
"video": "reel-001.mp4",
"facebook": {
"caption": "আজকের গল্পটা কেমন লাগলো? ❤️",
"schedule": "2026-09-10T20:00:00+06:00"
},
"youtube": null,
"instagram": null,
"tiktok": null
}The Page alias can be inferred from the first folder under Dropzone. Explicit account and mapped folder must agree. Workspace/account ambiguity, invalid JSON, path escapes and invalid dates are reported instead of defaulting to all platforms. ISO timestamps without offsets use timezone if present, otherwise UTC. Nonexistent/ambiguous daylight-saving times require a valid timestamp with an explicit offset. A missing schedule uses the next workspace default time (UTC), preserving the existing Settings behavior; clear that default or explicitly set "schedule": null to use the next available worker slot. Past schedules are due immediately.
Legacy platforms, platform_settings.<platform>.schedule_time, title/description/tags and batch.json filename-to-instruction maps are supported. For new Facebook imports add a Page alias. New imports create one job per enabled platform, preserving distinct captions and times. YouTube, Instagram and TikTok still use the workspace's existing browser session; this update does not introduce their official API publishers.
Content → Scan Content Folder previews ready jobs, duplicates, errors and Page totals. Choose files, then Import. The content folder is set in Settings. Subfolder auto-import is off by default so a bulk scan can be reviewed; enable it in Scheduler to import stable video/JSON pairs automatically, including files later added to existing folders. Dashboard uploads and root-level pairs import automatically. Uploads require a destination. Finish copying before a manual import; the watcher waits for two unchanged polls (about six seconds).
Import identity uses the root, video path, workspace, platform and Page. Repeated imports cannot enqueue the same destination twice, including overlapping watcher/manual imports. Job instructions are snapshotted; editing JSON after import does not rewrite an existing job. Use Calendar to change its time. A changed source video is blocked before a new upload; use a new filename for replacement content. Deleting a queued record keeps shared source files; scanning that file again can create a new job.
States: pending / scheduled → queued → uploading → processing → published. Browser success keeps the legacy completed status. retry holds a persisted backoff time; permanent failures or exhausted attempts become manual_review.
- Only due jobs run, with one active worker. Workspace upload spacing applies independently to each Facebook Page and collectively to legacy workspace browser jobs.
- Retry-After and Meta usage headers extend Page cooldowns. Transient pre-publish errors retry up to five worker attempts. Processing polls are bounded to 120 total attempts/checks.
- The remote video ID and publish phase are committed before the final publish request. A lost response/restart triggers status checks on that object. It does not start another publish. A definite rejected finish request can retry finishing the same uploaded object.
publishedrequires Meta'spublishing_phase.status == complete, or an explicit operator confirmation. Asuccessresponse from uploading is not enough.- For manual fallback, open a pending or review job in Calendar, check whether it already published, and choose Open Page in browser. This opens the Page-specific Business Suite context. Verify the destination and choose the local video yourself, publish, then mark it published in EchoFlow. This flow deliberately does not click Publish automatically.
- Retrying an interrupted browser job requires checking its destinations. Destinations already recorded as successful are skipped.
New imported jobs retain their source media because several destinations may share it. Legacy successful jobs keep their Completed-folder archival and retention behavior. Keep source files until every destination is finished; deleting a job does not delete the video.
The protocol follows Meta's Reels publishing collection and official sample collection: start /{page-id}/video_reels, stream bytes to the returned rupload.facebook.com URL, finish, then query video status. Page discovery follows Meta's Page access-token example. Consult Meta's publishing guide for current media eligibility and app requirements. Local validation checks a playable stream/duration; Meta remains authoritative for format, length and eligibility limits.
cd backend
./venv/bin/python -m unittest discover -s tests -v
./venv/bin/python -m unittest test_backend -v
cd ../frontend
npm run buildThe automated tests use temporary SQLite databases and mocked Meta responses. They do not post to real Pages. A live OAuth + Reel test still requires your Meta app and Page authorization.
For isolated environments, ECHOFLOW_DATABASE_URL, ECHOFLOW_CONFIG_FILE, and ECHOFLOW_SECRET_DIR, and ECHOFLOW_SESSIONS_DIR override local storage locations. Normal startup adds the new SQLite columns and indexes without dropping existing jobs or workspaces. Back up an existing database before upgrading; startup will continue pending work unless its saved queue setting is paused.