MailDesk docs
Get MailDesk
Pro

Gmail realtime — Google Cloud setup, A to Z (administrator)

This page is the full Google Cloud side of turning on Gmail realtime push: every project, API, Pub/Sub topic, IAM grant, service account, and push subscription you create — and where each one lives. The MailDesk setup assistant generates a single script that does all of this for you, so most administrators never run these commands by hand. This page exists so you can see exactly what that script creates, run the steps manually if you prefer, and understand the one trap that c

12 min read Pro

This page is the full Google Cloud side of turning on Gmail realtime push: every project, API, Pub/Sub topic, IAM grant, service account, and push subscription you create — and where each one lives. The MailDesk setup assistant generates a single script that does all of this for you, so most administrators never run these commands by hand. This page exists so you can see exactly what that script creates, run the steps manually if you prefer, and understand the one trap that catches everyone: the project-mismatch between your OAuth client and your Pub/Sub topic.

Available in: Pro. Gmail realtime push is part of the MailDesk Pro module — it is not present in Basic, which always uses scheduled background sync. The mailbox is connected with the same Gmail OAuth credentials used for normal sync; realtime push reuses that connection and asks for no new permissions.

Honest expectation on speed

With push enabled, new Gmail mail typically appears in MailDesk in about 3 to 8 seconds. This is genuine provider push — Google notifies MailDesk the moment mail arrives — but it is not instantaneous: the notification still has to travel to your server and trigger a short sync. MailDesk does not claim "instant" delivery.


Everything is in your own Google Cloud project

Read this before you start. Every resource on this page is created in, owned by, and billed to your own Google Cloud project. MailDesk creates nothing in Google Cloud on your behalf and holds no Google credentials of its own. After setup, MailDesk stores only three non-secret identifiers — your project id, the fully-qualified topic name, and the signer service-account email — plus the settings its own webhook needs to verify incoming notifications (the expected audience, the webhook URL, and the allow-listed signer email). No Google Cloud secret, key, or token ever leaves your project for MailDesk.

Resource Created where Owned by
GCP project, enabled APIs Your Google Cloud project You
Pub/Sub topic Your Google Cloud project You
IAM grant on the topic Your Google Cloud project You
OIDC signer service account Your Google Cloud project You
Push subscription Your Google Cloud project You
The webhook endpoint Google pushes to Your Odoo server (/maildesk/push/gmail) You
project_id, topic_name, signer email (non-secret) Stored in MailDesk You

What you will build (the six pieces)

The setup creates six things, in this order. The generated Cloud Shell script does all six as six numbered phases; the manual sections below mirror them one-to-one.

  1. Select the right project — the one that owns your Gmail OAuth client.
  2. Enable two APIs — Gmail API and Pub/Sub API.
  3. Create a Pub/Sub topic — where Gmail publishes "this mailbox changed" events.
  4. Grant Gmail permission to publish — give [email protected] the roles/pubsub.publisher role on that topic.
  5. Create an OIDC signer service account — Pub/Sub uses it to sign every push with a verifiable token.
  6. Create the push subscription — pointed at your MailDesk webhook, authenticated with the signer and an audience equal to that exact webhook URL.

Step A — Pick the project that owns your OAuth client (the mismatch trap)

This is the single most important decision, and the most common cause of a failed setup.

Gmail requires the Pub/Sub topic to live in the same Google Cloud project as the OAuth client your mailbox is connected with. If your topic is in a different project, Gmail refuses to watch it and the validation step fails with WATCH_400_TOPIC_PROJECT_MISMATCH ("Pub/Sub topic is in the wrong Google Cloud project"). The error detail names the project Gmail expects — that is your OAuth client's project.

Use your OAuth client's project — not a fresh one

Do not create a brand-new "MailDesk push" project unless that is also where your Gmail OAuth client lives. The topic and the OAuth client must share one project id. If you already connected the mailbox via Gmail OAuth setup, use that same project here.

Set the active project (the script's phase [1/6]):

gcloud config set project <oauth-client-project>

To confirm which project your OAuth client belongs to, open the credentials page and read the project selector at the top:

  • Open OAuth credentialshttps://console.cloud.google.com/apis/credentials

If the project is wrong on a topic you already created elsewhere, re-run the setup script with PROJECT_ID set to the OAuth client's project so the topic is created there, then paste the new topic name back into the wizard.


Step B — Enable the Gmail API and the Pub/Sub API

users.watch needs the Gmail API enabled, and push delivery needs the Pub/Sub API enabled, both in the project from Step A. Enable both at once (the script's phase [2/6]):

gcloud services enable gmail.googleapis.com pubsub.googleapis.com

Or enable them from the console:

  • Open APIs & Serviceshttps://console.cloud.google.com/apis/library

Newly-enabled APIs need a moment

If you enable the Gmail API and immediately try to register the watch, Gmail may still answer WATCH_403_GMAIL_API_NOT_ENABLED ("Gmail API is not enabled in your project") for a short window. Wait 30–60 seconds and retry.


Step C — Create the Pub/Sub topic

Create the topic Gmail will publish change events to (the script's phase [3/6]; the script reuses an existing topic if one is already there). Using the MailDesk default name:

gcloud pubsub topics describe maildesk-gmail-push >/dev/null 2>&1 \
  || gcloud pubsub topics create maildesk-gmail-push

The default short topic name is maildesk-gmail-push. The fully-qualified resource name MailDesk needs is projects/<project-id>/topics/maildesk-gmail-push. You can read it back with:

gcloud pubsub topics describe maildesk-gmail-push --format='value(name)'

Manage topics in the console:

  • Open Pub/Sub Topicshttps://console.cloud.google.com/cloudpubsub/topic/list

Topic name format matters

MailDesk validates the topic name against the shape projects/<project-id>/topics/<topic-name>. A name with the slashes in the wrong place fails validation as WATCH_400_BAD_TOPIC ("Topic name is malformed"). If in doubt, paste back the exact value that gcloud pubsub topics describe … --format='value(name)' prints.


Step D — Grant Gmail permission to publish to the topic

Gmail publishes through its own Google-owned service account, [email protected]. That account must hold the roles/pubsub.publisher role on your topic, or Pub/Sub refuses to forward new-email events and the watch fails with WATCH_403_TOPIC_IAM_MISSING ("Gmail cannot publish to your Pub/Sub topic").

Grant it (the script's phase [4/6]):

gcloud pubsub topics add-iam-policy-binding maildesk-gmail-push \
  --member='serviceAccount:[email protected]' \
  --role='roles/pubsub.publisher'

The generated script does this automatically; the command above is the exact manual equivalent. To review the binding in the console, open the topic from the Pub/Sub Topics page above; project-wide IAM is at:

  • Open IAMhttps://console.cloud.google.com/iam-admin/iam

Step E — Create the OIDC signer service account

Pub/Sub signs every push it sends to your webhook with an OIDC token, so MailDesk can prove the request really came from Google. That signing identity is a service account you create. With the MailDesk default name, its email is maildesk-pubsub-push@<project-id>.iam.gserviceaccount.com (the script's phase [5/6], reused if it already exists):

gcloud iam service-accounts create maildesk-pubsub-push \
  --display-name='MailDesk Pub/Sub push signer'

The default signer local name is maildesk-pubsub-push. Manage service accounts in the console:

  • Open Service Accountshttps://console.cloud.google.com/iam-admin/serviceaccounts

Paste the full signer email — lowercase, no display name

MailDesk validates the signer email against the shape <sa-name>@<project-id>.iam.gserviceaccount.com (all lowercase). A display name, stray spaces, or a mixed-case value fails as SIGNER_BAD_FORMAT ("Signer service account email is malformed"). List your accounts and copy the exact email with gcloud iam service-accounts list.


Step F — Create the push subscription (endpoint + OIDC + audience)

The push subscription is what actually delivers events: it pulls from your topic and pushes each one to MailDesk's webhook, signed by the service account from Step E. Three values must be exactly right (the script's phase [6/6], which creates or updates the subscription):

  • Push endpoint — your MailDesk webhook: <web.base.url>/maildesk/push/gmail.
  • Push auth service account — the signer email from Step E.
  • Push auth token audience — must equal the push endpoint, character for character.

Using the MailDesk default subscription name maildesk-gmail-push-sub:

gcloud pubsub subscriptions create maildesk-gmail-push-sub \
  --topic=maildesk-gmail-push \
  --push-endpoint='https://your-odoo-host/maildesk/push/gmail' \
  --push-auth-service-account='maildesk-pubsub-push@<project-id>.iam.gserviceaccount.com' \
  --push-auth-token-audience='https://your-odoo-host/maildesk/push/gmail' \
  --ack-deadline=10

The default subscription name is maildesk-gmail-push-sub and the ack deadline is 10 seconds. If the subscription already exists, the script updates its push endpoint, signer, and audience instead of recreating it. Manage push subscriptions in the console:

  • Open Push Subscriptionshttps://console.cloud.google.com/cloudpubsub/subscription/list

Audience must equal the endpoint

The OIDC token Pub/Sub sends carries an aud (audience) claim. MailDesk's webhook only accepts a token whose audience equals the URL it was delivered to. If the saved audience and the computed webhook URL differ, validation stops at AUDIENCE_MISMATCH ("OIDC audience does not match the webhook URL"). The setup script sets both to the same value; if you create the subscription by hand, make --push-auth-token-audience identical to --push-endpoint.

Your webhook URL comes from web.base.url

MailDesk derives the webhook URL as <web.base.url>/maildesk/push/gmail. It must be a stable, public HTTPS address with a valid certificate — Google will not push to http:// or to a self-signed certificate. Set web.base.url (and freeze it with web.base.url.freeze = True) under Settings → Technical → System Parameters before you create the subscription.


Step G — Run the generated script in Cloud Shell (the easy path)

You almost never need to type Steps A–F yourself. The MailDesk setup assistant generates one idempotent bash script that performs all six phases in order and prints the values to paste back. Running it twice is safe — existing resources are detected and reused.

  1. In the assistant, choose Recommended mode and enter your GCP project id (the one from Step A). MailDesk fills in the default names for the topic (maildesk-gmail-push), subscription (maildesk-gmail-push-sub), and signer service account (maildesk-pubsub-push), and derives the webhook URL from web.base.url.
  2. Copy the whole script. Open Google Cloud Shell with the provided link: - Open Google Cloud Shellhttps://shell.cloud.google.com/
  3. Paste the script and run it. It runs the six phases — select project, enable APIs, create topic, grant the publisher role, create the signer account, create/update the push subscription — and finishes by printing a single line that begins:

    MAILDESK_PUSH_RESULT={"project_id":"…","topic_name":"projects/…/topics/maildesk-gmail-push","signer_service_account_email":"maildesk-pubsub-push@….iam.gserviceaccount.com"}
    

Idempotent and safe to re-run

Every phase checks before it creates: it describes the topic, the service account, and the subscription first and only creates what is missing. If something failed half-way, fix the cause and run the same script again — it will not duplicate or break existing resources.


Step H — Paste the result back into MailDesk

The MAILDESK_PUSH_RESULT= line is the bridge from Google Cloud back to MailDesk.

  1. Copy that entire last line the script printed (the prefix and the {…} JSON).
  2. In the assistant's Paste result step, paste it and parse it. MailDesk extracts the three non-secret identifiers it stores: project_id, the fully-qualified topic_name, and the signer_service_account_email.
  3. The assistant advances to Validate & enable, where it checks the URL, the audience, the topic and signer formats, saves the signer to the webhook's allow-list, and finally calls Gmail's users.watch against your topic. When that succeeds, push is live.

If you provisioned everything by hand and used Advanced mode, enter the same three values directly instead of pasting the script line.

What MailDesk verifies on its side

On a successful enable, MailDesk saves the signer email to its allow-list and records the expected audience and webhook URL, then verifies every incoming push: the OIDC signature, that the audience matches your URL, and that the signer is on the allow-list. Anything that fails those checks is rejected — Google's push system and MailDesk's webhook authenticate each other end to end.


On the script step, the assistant shows one-click deep links to the relevant Google Cloud Console pages. Most are pre-scoped to your project (?project=<project-id>) so you land on the right page already filtered; the Cloud Shell link is the one exception and is not project-scoped. They are convenience shortcuts to the pages used above:

Button Console page
Open APIs & Services https://console.cloud.google.com/apis/library
Open Pub/Sub Topics https://console.cloud.google.com/cloudpubsub/topic/list
Open Push Subscriptions https://console.cloud.google.com/cloudpubsub/subscription/list
Open Service Accounts https://console.cloud.google.com/iam-admin/serviceaccounts
Open IAM https://console.cloud.google.com/iam-admin/iam
Open Google Cloud Shell https://shell.cloud.google.com/

Troubleshooting the Google Cloud side

Symptom (validation step / error code) What it means in Google Cloud What to do
WATCH_400_TOPIC_PROJECT_MISMATCH The topic is in a different project than your OAuth client Re-run the script with PROJECT_ID set to your OAuth client's project (gcloud config set project <oauth-client-project>), then paste the new topic name back
WATCH_403_GMAIL_API_NOT_ENABLED The Gmail API is off in this project gcloud services enable gmail.googleapis.com pubsub.googleapis.com, then retry after 30–60 s
WATCH_403_TOPIC_IAM_MISSING [email protected] lacks roles/pubsub.publisher on the topic Re-run the script (phase [4/6] grants it), or run the add-iam-policy-binding command from Step D
WATCH_400_BAD_TOPIC The topic resource name is malformed Paste the exact value from gcloud pubsub topics describe maildesk-gmail-push --format='value(name)'
WATCH_404_TOPIC_NOT_FOUND The named topic does not exist in the project List topics with gcloud pubsub topics list --format='value(name)'; recreate it with the script or paste the correct name
SIGNER_BAD_FORMAT The signer email is not a valid …@<project>.iam.gserviceaccount.com Copy the exact email from gcloud iam service-accounts list — lowercase, no display name
AUDIENCE_MISMATCH The subscription's audience does not equal the webhook URL Re-run the wizard so MailDesk re-aligns both to <web.base.url>/maildesk/push/gmail; update the subscription audience on the GCP side
WATCH_401_OAUTH_INVALID The mailbox's Gmail authorization expired or was revoked Reconnect the Gmail mailbox under Settings → Mailboxes, then re-open the assistant
WATCH_400_OAUTH_SCOPE_MISSING The stored OAuth token lacks gmail.modify Disconnect and reconnect the mailbox, accepting all requested scopes

Mail keeps flowing while you fix Google Cloud

Every error above only blocks realtime push registration. Existing mail and scheduled fallback polling are unaffected — Gmail fallback polling keeps delivering new mail every 2 minutes until realtime is restored. Push is an accelerator, never a dependency.


After setup — what keeps running

  • The watch renews itself. A Gmail watch is valid for at most 7 days. MailDesk renews it automatically with about a day to spare, using a scheduled background job, so push keeps running without any further Google Cloud work.
  • Silence is caught. If notifications ever stop arriving, MailDesk notices the silence and triggers a catch-up sync, so no mail is lost.
  • Nothing to babysit. Once users.watch succeeds and the mailbox shows realtime status Active, you are done — there is no recurring console task.