Chuhai Cloud · 1583523 B.C. LTD. · prepared for a Basic Access token application
Chuhai Cloud is a subscription web application for cross-border e-commerce exporters. A customer connects the marketing channels their own business owns — Facebook Pages, Instagram professional accounts, TikTok accounts, YouTube channels and Google Ads accounts — and then runs all of them from one console: publishing content, reading performance, answering messages and comments, and monitoring paid campaigns.
The product is deployed and running. The Google Ads module is written, deployed and calling the Google Ads API today; it runs against a test account, because our developer token is at Explorer Access. This application is for the live token that lets that module operate on the real accounts our customers own.
Scope of this application. We are requesting access for two capabilities only: reporting and campaign management (enable / pause). Both are built and working. Campaign creation is deliberately not part of this request — we would rather ask for exactly what we have shipped than for a roadmap.
What the tool is not. It is not an agency service, not a reseller platform, not an automated bid manager, and not a prospecting or account-audit product. It never creates, buys or sells Google Ads accounts, never bids autonomously, and never touches an account whose owner has not connected it through Google OAuth.
Every Chuhai Cloud customer is a business that owns its own Google Ads account. During
onboarding that customer's admin clicks “Connect Google Ads”, completes the
standard Google OAuth consent screen under their own Google identity, and grants
https://www.googleapis.com/auth/adwords. We store the resulting token against
that customer's tenant.
Tenant isolation is enforced in the data layer rather than in the UI: every query in the
ads module carries a company_id predicate bound to the caller's session, so a
customer's staff can only ever read or act on the customer IDs their own tenant linked.
The token used on any given API call is the token of the account that call is about; it is
looked up through the same tenant-scoped path.
Our manager account 456-255-7113 exists only to hold the developer token and to supply
login-customer-id. We do not take ownership of customer accounts and we do not
run ads out of our own account on anyone's behalf.
Browser (React 19 SPA)
| POST /gw/p/<module.action>/<version> x-auth-token: tenant session
v
FastAPI backend (single gateway contract, {code,data} envelope)
|
+-- handlers/ads.py <- tenant-scoped: every query filters company_id
|
+-- adapters/ads.py :: AdsAdapter <- one interface, four platforms
| +-- TikTokAds (platform 1)
| +-- GoogleAds (platform 2) <=== this application
| +-- InstagramAds (platform 3)
| +-- FacebookAds (platform 4)
|
+-- tables: ad_account, ad_campaign, ad_daily_stat (all keyed by company_id)
ad_account.access_token = that customer's own Google OAuth token
The Google implementation is one adapter class, GoogleAds, targeting the
Google Ads API REST interface at https://googleads.googleapis.com/v17. Every
request carries three things: the application's developer-token, the manager
account as login-customer-id, and an Authorization: Bearer header
holding that customer's own OAuth access token. The developer token is read from
the server environment; it is never sent to the browser, never written to a repository, and
never shared with a third party.
| Service / method | Where it is used | What triggers it |
|---|---|---|
GoogleAdsService.SearchStreamcustomers/{cid}/googleAds:searchStream |
Three GAQL queries. (a) Account header: customer.currency_code,
customer_budget.amount_micros. (b) Campaign list:
campaign.id, name, status,
advertising_channel_type, campaign_budget.amount_micros.
(c) Daily performance: segments.date,
metrics.cost_micros, impressions, clicks,
conversions, conversions_value. |
A signed-in user of that tenant opens the Ads page or clicks Sync. |
CampaignService.Mutate (update)customers/{cid}/campaigns:mutate |
Pause / enable one campaign. updateMask is set to status
only; this path writes no other field. |
A signed-in user clicks Pause or Enable on a specific campaign. |
Two services. That is the complete list, and it is the complete scope of this
application. We do not call CampaignBudgetService,
AdGroupService or AdGroupAdService; we do not call Keyword
Planner, Recommendation or Audience Insight services; we do not pull Search Term or Change
Event reports; and we do not use the API for prospecting, competitive research or account
discovery of any kind.
Campaign creation is not requested here. Chuhai Cloud does let a user compose a campaign draft, but a draft is a local record: the user takes it into the Google Ads UI themselves. If we later ship creation through the API we will come back and ask for it then, with the working feature to show.
Build status, stated plainly. Everything described in this document is implemented and deployed: authentication, account linking, the three reporting queries, and campaign status write-back. There is no feature in this application that we are still building. That is why campaign creation is excluded from the request — not because we have no interest in it, but because we are only asking for what already works.
SearchStream calls for that one account.429 and 503 are
retried, at most three attempts total, with a 2s then 6s backoff; every other status
returns immediately, because a 4xx means our request was wrong and repeating it only
consumes quota. Quota on the Google Ads API is charged against the developer token, so a
naive retry loop would spend the whole application's budget on one broken call.1583523 B.C. LTD. accepts and operates under the Google Ads API Terms and Conditions and the Required Minimum Functionality policy. One developer token, one product. We do not sublicense, resell or otherwise pass API access to another party, and we do not operate the API on behalf of anyone who has not connected their own account through Google OAuth.