- 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>
89 lines
6.9 KiB
Markdown
89 lines
6.9 KiB
Markdown
# 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.
|