quorlasocial/docs/google-app.md
metatroncubeswdev 15bc0b9bf2 YouTube comments: read with an API key instead of the channel token
commentThreads.list rejects youtube.readonly ("insufficient authentication
scopes"); it needs youtube.force-ssl, which we don't want to request.
Public comments are now read with YOUTUBE_API_KEY. Also records the
re-submitted Google verification (video v2, new scope justification).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-25 12:29:27 -04:00

103 lines
9.7 KiB
Markdown

# Google apps: Quorla (YouTube + Google Business Profile)
Two Google Cloud projects, both owned by dev@metatroncubesolutions.com:
- **YouTube** → project **Quorla** (`quorla`, number 275320055192), described below.
- **Google Business Profile** → project **GMB Audit** (`gmb-audit-489406`), which was **already approved** for the Business
Profile API (300 requests/minute). A new access request for `quorla` was declined because an approved project
already exists, so Quorla uses GMB Audit for Business Profile. Its consent screen was renamed to **Quorla** (logo,
links, domain quorlasocial.com), scopes openid / userinfo.email / business.manage, test users dev@ and the Gmail, and a
new web client **"Quorla GBP (app.quorlasocial.com)"** with redirect https://app.quorlasocial.com/oauth/gbp/callback.
Its keys are `GBP_CLIENT_ID` / `GBP_CLIENT_SECRET` in `.env`.
## YouTube project (Quorla)
- Google Cloud project **Quorla** (ID `quorla`, number 275320055192) in the **metatroncubesolutions.com** organization, created with
dev@metatroncubesolutions.com.
- APIs enabled: YouTube Data API v3, My Business Account Management API, My Business Business Information API,
Google My Business API. No billing account needed; all are free within quota.
- OAuth consent screen (Google Auth Platform): app name Quorla, logo `branding/quorla-icon-1024.png`, support and
developer contact dev@metatroncubesolutions.com, home https://app.quorlasocial.com, privacy /privacy, terms /terms,
authorized domain quorlasocial.com. Audience **External**, publishing status **Testing**.
- Scopes: openid, userinfo.email, business.manage (non-sensitive); youtube.upload, youtube.readonly (sensitive).
No restricted scopes, so no paid security assessment is needed.
- Test users (only these can connect while in Testing; max 100): dev@metatroncubesolutions.com,
metatroncubesoftwaresolutions@gmail.com. Add each client's Google account here until the app is verified.
- OAuth web client "Quorla web (app.quorlasocial.com)" with redirect URIs
https://app.quorlasocial.com/oauth/youtube/callback and https://app.quorlasocial.com/oauth/gbp/callback.
Its ID and secret are in the PC's `.env` as YOUTUBE_CLIENT_ID/SECRET (GBP_* now point to the GMB Audit client); copy those four lines
to the server's `.env`, or enter them in Quorla → Connections → Set up. Google shows the secret only once; if it is
lost, add a new secret on the client in Google Cloud.
## Still to do
1. ~~Business Profile API access~~ — done by reusing the approved GMB Audit project (see above).
2. ~~Verify quorlasocial.com in Google Search Console~~ — done 2026-10-21 (domain property, owner dev@; DNS TXT `google-site-verification=…` on the Hestia server — keep it).
3. After deploy: **publish the app** (Audience → Publish app) and submit **verification** (sensitive YouTube scopes):
Google asks for a demo video of the sign-in and how each scope is used.
4. **YouTube API audit** (free form) to lift the 10,000 units/day quota and the private-only upload limit.
## Verification submission (paste-ready)
**App purpose (both projects):**
> Quorla (https://quorlasocial.com) is a social media management tool for marketing agencies. An agency admin connects
> a client's Google account on Quorla's Connections page. The team prepares posts in Quorla; an admin reviews and
> approves the exact post, and Quorla publishes it at the scheduled time. Nothing is posted without that approval.
> Tokens are encrypted at rest and deleted when the account is disconnected.
**Quorla project, scope justifications:**
- `youtube.upload`: Upload the video an agency admin approved in Quorla to the connected channel, with the title,
description, visibility and "made for kids" setting chosen on the post screen. Only approved posts are uploaded.
- `youtube.readonly`: Show which channel is connected (name, handle, avatar, subscriber count) on the Connections
page, so the agency posts to the right channel, and link to the uploaded video afterwards.
**Quorla project, Data Access → "How will the scopes be used?"** (one box, max 1000 characters; Google only saves it
together with the demo video link):
> Quorla (quorlasocial.com) is a social media management tool for marketing agencies. An agency admin connects a client's YouTube channel on Quorla's Connections page. youtube.upload: the team prepares a video post in Quorla; an admin reviews and approves the exact video, title, description, visibility and made-for-kids setting, and Quorla uploads it to the connected channel at the scheduled time. Nothing is uploaded without that approval. youtube.readonly: show which channel is connected (name, handle, avatar, subscriber count) so the agency posts to the right channel, and link to the uploaded video afterwards. More limited scopes are not sufficient: youtube.upload is the narrowest scope that allows uploading, and youtube.readonly is needed to read the channel identity, which upload alone does not return. Tokens are encrypted at rest and deleted when the channel is disconnected.
Status 24 Oct 2026: Google asked for a **new demo video** (first one didn't show the consent screen with both YouTube
scopes expanded, or every feature that uses them). New "How will the scopes be used?" text (replaces the one above):
> Quorla (quorlasocial.com) is a social media management tool for marketing agencies. An agency admin connects a client's YouTube channel on Quorla's Connections page. youtube.upload: an admin reviews and approves the exact video, title, description, visibility and made-for-kids setting, and Quorla uploads it to the connected channel (videos.insert). Nothing is uploaded without that approval. youtube.readonly is used for two read-only features: (1) Connections page: channels.list (mine=true) shows the connected channel's name, handle, avatar and subscriber count, so the agency posts to the right channel; (2) Published page: videos.list shows the views, likes and comment counts of the videos Quorla uploaded, so the agency can report results to the client. youtube.upload cannot read any of this and there is no narrower read scope. We do not request the broader youtube or youtube.force-ssl scopes. Tokens are encrypted at rest and deleted when the channel is disconnected.
v2 video recorded 25 Oct 2026: https://youtu.be/CHHreGKIpXE (unlisted). **Submitted 25 Oct 2026** (Data Access updated with
the text below and the new link; replied to Google's email from dev@). Waiting for Google's answer.
**Re-recorded demo video (v2)**, in this order, address bar visible, English:
1. Before recording: in Quorla disconnect YouTube; at https://myaccount.google.com/connections remove Quorla's access
for the test Google account (so the full consent screen shows). YouTube comments in the Inbox use an API key
(`YOUTUBE_API_KEY`), not these scopes, so they are not part of this video.
2. quorlasocial.com → Privacy policy → app.quorlasocial.com sign in.
3. Connections → client → YouTube → Connect → account chooser. **Pause on the consent screen**: zoom the address bar
(client_id and `scope=` with youtube.upload + youtube.readonly), click each scope's arrow / "Show all services" so
both descriptions are fully readable, tick both, Continue.
4. Back in Quorla: channel name, avatar, subscribers shown → say "youtube.readonly: channels.list".
5. Publishing → new post with a vertical video → tick YouTube → title, visibility, made for kids → save draft → Approve →
published → open on YouTube → say "youtube.upload: videos.insert".
6. Published page → refresh stats → the YouTube video's views / likes / comments → "youtube.readonly: videos.list".
7. Connections → Disconnect YouTube.
Status 22 Oct 2026 (evening): **Quorla project submitted for verification** (youtube.upload, youtube.readonly) with demo
video https://youtu.be/lB8FMGDbBOo (unlisted); Data access status "under review". Google replies by email to dev@.
Status 22 Oct 2026: GMB Audit (Business Profile) is **done**: In production, branding verified and published, and
"Verification is not required" for data access (business.manage is non-sensitive). Home page https://quorlasocial.com.
Any Google account can now connect Business Profile; reconnect connections made while it was in Testing once, so
their sign-in no longer expires after 7 days.
Status 21 Oct 2026: Quorla project published (In production), branding verified and published (home page changed to
https://quorlasocial.com). Data access verification waits for the demo video. business.manage counts as non-sensitive
on this project, so only the two YouTube scopes need justifying.
**GMB Audit project, scope justification:**
- `business.manage`: List the business locations the connected account manages so the admin can pick the client's
location, and publish approved "updates" (local posts with text, photo and an optional button) to that location.
**Demo video** (one recording, uploaded to YouTube as Unlisted; link it in both forms). Use English and show the
browser's address bar so the project's OAuth client ID is visible in the consent URL:
1. quorlasocial.com homepage → privacy policy link → Sign in at app.quorlasocial.com.
2. Connections → pick a client → YouTube → Connect → Google account chooser → consent screen showing **Quorla** and
the YouTube scopes → Allow → back in Quorla, channel shown as connected.
3. Publishing → New post → upload a vertical video → caption → tick YouTube → title, visibility → Save as draft →
Approve → post publishes → open the video on YouTube.
4. Connections → Google Business Profile → Connect → consent screen (business.manage) → choose the location.
5. New post with a photo → tick Google Business Profile → button "Learn more" → Approve → open the update on Google.
6. Connections → Disconnect, to show tokens can be removed.