quorlasocial/docs/platforms.md
metatroncubeswdev fc2179b343 Track platform quotas; wait and retry instead of failing
- quota.js: per-platform limits (YouTube 10,000 units/day Pacific per project,
  Instagram 100 / Threads 250 / TikTok 15 posts per 24 h per account, ...),
  usage log, .env overrides (QUOTA_<PLATFORM>)
- Publishing checks the budget first and records usage; when a platform is
  out of quota (or answers quota exceeded / 429) the post stays approved and
  the queue retries after the reset, without re-posting finished platforms
- Drafts that exceed a day's limit get a warning
- Connections page shows used/limit and when it frees up
- Tests for windows (incl. DST), shared vs per-account limits and the
  wait-and-retry queue flow; docs for quotas and the Google app setup

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 21:56:54 -04:00

6.9 KiB
Raw Permalink Blame History

Other platforms: LinkedIn, Google Business Profile, YouTube, TikTok, Pinterest, Threads, Reddit

Facebook and Instagram still come through the agency's Meta system user. Every other platform is connected per client on the Connections page: an admin signs in to the client's account once ("Connect"), and from then on posts to that platform go through Publishing with the same draft → approve → publish flow.

How it fits together

Piece Where
Connections page (set up apps, connect/disconnect accounts, choose company Page / location) dashboard/web/src/pages/Connections.jsx
"Post to" picker with each platform's fields, used in New post and library Schedule dashboard/web/src/PlatformPicker.jsx
API routes, OAuth callback /oauth/<platform>/callback, media links /m/<token>/<file> dashboard/server/index.js
Encrypted store for app keys and tokens (data/connections.json, AES-256-GCM) connector/src/platforms/vault.js
Sign-in (OAuth 2, PKCE for Google), token refresh connector/src/platforms/oauth.js, index.js
One adapter per platform connector/src/platforms/<platform>.js
Queue integration (validation, publishing) connector/src/schedule.js
Agent tools: list_connections; schedule_post / schedule_from_library accept all platforms + options connector/src/server.js
Offline tests (simulated platform APIs) connector/test/platforms.test.mjs → node --test connector/test/platforms.test.mjs

Tokens and app secrets are encrypted with a key derived from DASHBOARD_SECRET (or QUORLA_SECRET_KEY). They are never sent to the browser. If that secret changes, every platform shows "Reconnect needed".

What each platform needs before it works for real

Every platform requires a developer app owned by the agency, with the Redirect URI shown in its Set up dialog (https://app.quorlasocial.com/oauth/<platform>/callback), plus the privacy policy and terms pages on quorlasocial.com.

Platform Posts Developer app Approval needed for real use Until approved
LinkedIn text, image, multi-image, video; as a person or company Page linkedin.com/developers Community Management API (company Pages, analytics) Personal profiles only: set Scopes to openid profile w_member_social
Google Business Profile update with photo and button Google Cloud (OAuth client) Business Profile API access form + OAuth consent verification API calls return quota errors; test users only
YouTube video (Shorts when vertical, under 3 min) Google Cloud (same OAuth client) YouTube API audit + consent verification Uploads are forced to Private; small daily quota
TikTok video, photo / photo carousel developers.tiktok.com App review for Content Posting API (Direct Post) Posts are "Only me"; sandbox users only
Pinterest image, multi-image (2–5), video Pins on a board developers.pinterest.com Standard access (demo video) Trial: set PINTEREST_SANDBOX=true, Pins go to the sandbox
Threads text, image, video, carousel Meta for Developers (Threads use case) Meta app review for Threads permissions Only accounts added as Threads testers
Reddit text or link post to a subreddit reddit.com/prefs/apps (web app) Reddit's approval for commercial API use Works for the agency's own account; check each subreddit's rules

Platforms that fetch media from a link (TikTok photos, Threads, Google Business Profile) get a signed link to the file that expires after 6 hours. On the server this uses PUBLIC_URL, or https://APP_DOMAIN when that's set. TikTok also needs the domain verified in its developer portal.

What was tested, and what wasn't

  • Tested offline (15 tests, all passing): encryption at rest, sign-in links for every platform (client ID parameter, scopes, PKCE, offline access), the callback state check, token refresh and "reconnect needed", and each platform's publish requests (LinkedIn image to a company Page with hashtags, YouTube resumable upload, GBP post with button and photo link, TikTok video chunks and photo pull, Pinterest base64 Pin, Threads long-lived token and image post, Reddit text post and error handling), plus format and field validation.
  • Tested against the local dashboard (21 checks): admin-only setup and connect, secrets never returned, sign-in redirect and callback errors (forged state, cancelled, signed out), media links (served, tampered refused), drafts to connected and unconnected platforms, per-platform captions, disconnect.
  • Not tested: real calls to each platform. Each platform needs its developer app and keys first. The request formats follow each platform's documentation as of this build, but expect small fixes the first time each one connects and posts. Test with the agency's own accounts before connecting clients.
  • Not built yet: comments/inbox and analytics for these platforms (only follower counts are shown), Reddit image posts, Pinterest video cover images, LinkedIn document (PDF) posts, X (paid API).

Quotas and limits

Quorla tracks each platform's limit (connector/src/platforms/quota.js, usage in data/quota.json):

Platform Limit tracked Window
YouTube 10,000 units per Google project, shared by all clients (upload = 1,600, read = 1) → about 6 uploads/day until the YouTube audit raises it calendar day, Pacific time
Instagram 100 API posts per account rolling 24 h
Facebook 200 posts per Page rolling 24 h
Threads 250 posts per profile rolling 24 h
TikTok 15 posts per creator rolling 24 h
LinkedIn 100 posts per member/Page rolling 24 h
Pinterest 100 Pins per account rolling 24 h
Google Business Profile 50 updates per account rolling 24 h
Reddit 10 posts per account (kept low: subreddits ban frequent posters) rolling 24 h
  • Before publishing, Quorla checks the budget. If it's used up, or the platform answers "quota exceeded" / "too many requests", the post stays approved and waits; the queue retries it automatically after the reset. Platforms that already went out are not posted twice.
  • Saving a draft that would exceed a day's limit shows a warning on the draft (not an error).
  • The Connections page shows used / limit and when it frees up.
  • Raise a limit after a platform approves more with QUOTA_<PLATFORM>=n in .env (e.g. QUOTA_YOUTUBE=1000000).
  • Tests: node --test connector/test/quota.test.mjs.

Suggested order after merging

  1. Deploy (git pull && docker compose up -d --build), then Connections → Set up for one platform at a time.
  2. Start with LinkedIn (personal-profile scopes) and Google Business Profile, using Metatroncube's own accounts.
  3. Apply for the approvals in the table above. They take days to weeks, which is why they should start early.
  4. Connect client accounts once each platform is approved.