Lean SMTP

Lean SMTP  /  Transports  /  Gmail / Google Workspace

Setup guide · API transport

Send through Gmail / Google Workspace.

Lean SMTP sends WordPress mail through the Gmail API as a Google Cloud service account with domain-wide delegation — no OAuth consent screen, no refresh tokens, no Google SDK. This guide takes you from a fresh Google Cloud project to a working test email.

Workspace domains only

This transport works only for mailboxes in a Google Workspace domain, because domain-wide delegation is a Workspace-admin feature. It cannot send from a consumer @gmail.com account. For a personal Gmail account, use the SMTP transport with an app password instead, or pick one of the other providers.

You are here · Service account

Service account

A Super Admin authorizes domain-wide delegation once; sends as any mailbox in the domain.

This guide ↓
Alternative · OAuth

OAuth (per-mailbox)

Owner of one mailbox clicks Connect. No Super Admin. Consent screen must be Internal.

OAuth guide →

01 How it works

Unlike the usual "sign in with Google" flow, Lean SMTP never asks a user to click through a consent screen. Instead it authenticates as a service account — a non-human Google identity you create once — and uses domain-wide delegation to act on behalf of one of your Workspace mailboxes.

On each send, the plugin:

  1. builds a short-lived JWT and signs it (RS256) with the service account's private key;
  2. exchanges that JWT at oauth2.googleapis.com for a one-hour access token, scoped to gmail.send and impersonating your Send As mailbox;
  3. posts the message as raw MIME to the Gmail API at gmail.googleapis.com.

The access token is cached (just under its hour) and re-minted as needed. Nothing but the signed JWT and the message ever leaves your site, and the private key is stored AES-256 encrypted in your database.

The three things you'll end up with

(1) the Gmail API enabled in a Google Cloud project, (2) a service-account JSON key you paste into the plugin, and (3) that service account's client ID authorised for the gmail.send scope in your Workspace Admin Console. Miss the third and Google returns unauthorized_client.

02 Before you start

You will need:

  • A Google Workspace domain (e.g. example.com) with a real mailbox you want mail to come from, such as noreply@example.com.
  • Super Admin access to that Workspace's Admin Console — or someone who has it to do Step 7 for you. Domain-wide delegation can only be authorised by a Super Admin.
  • Access to the Google Cloud Console, with permission to create a project and a service account. This can be any Google account; it does not have to be in the same Workspace.
  • Lean SMTP installed and activated on your WordPress site.

03 Create a Google Cloud project

A project is just a container for the Gmail API and the service account. If you already have one you want to use, skip ahead.

  1. Go to the Google Cloud Console and sign in.
  2. Open the project picker at the top and choose New Project.
  3. Name it something recognisable — e.g. wordpress-mail — and click Create.
  4. Make sure the new project is selected in the picker before continuing.

04 Enable the Gmail API

  1. With your project selected, open the Gmail API page in the API Library.
  2. Click Enable.
The Gmail API product page in the Google Cloud Console, with an Enable button and service name gmail.googleapis.com.
Google Cloud Console → Gmail API → Enable.

That is all this step needs. You do not need to configure an OAuth consent screen for the service-account method — that's only for the browser-consent flow, which Lean SMTP's separate Gmail OAuth transport uses instead.

05 Create a service account

  1. Open APIs & Services → Credentials, or go straight to the Service Accounts page.
  2. Click Create service account.
  3. Give it a name such as lean-smtp. Google fills in an account email like lean-smtp@your-project.iam.gserviceaccount.com.
  4. Click Create and continue. You can skip the optional "Grant this service account access to the project" and "Grant users access" steps — they are not needed here. Click Done.
The Create service account form: a name field, the auto-generated account ID and email address, and the optional Permissions and Principals steps that can be skipped.
Name the account; the email auto-fills. Steps 2 and 3 are optional — skip them.
No project roles required

The service account needs no IAM roles on the project. Its only power comes from the delegation you authorise in Step 7, scoped to just gmail.send.

06 Download a JSON key

  1. On the Service Accounts list, click your new account, then open the Keys tab.
  2. Click Add key → Create new key, choose JSON, and click Create.
  3. A .json file downloads. This is the private key — treat it like a password. You will paste its entire contents into the plugin shortly.
The Keys tab of a service account with the 'Create private key' dialog open, JSON selected as the recommended key type.
Keys → Add key → Create new key → JSON → Create. The file downloads immediately.

The file looks like this (your values will differ):

service-account-key.json
{
  "type": "service_account",
  "project_id": "wordpress-mail",
  "private_key_id": "a1b2c3…",
  "private_key": "-----BEGIN PRIVATE KEY-----\n…\n-----END PRIVATE KEY-----\n",
  "client_email": "lean-smtp@wordpress-mail.iam.gserviceaccount.com",
  "client_id": "109876543210987654321",
  …
}
Keep it whole

Paste the file exactly as downloaded, including the \n sequences inside private_key. The plugin decodes them back into the real newlines the key needs. If you re-type or reformat the key, signing will fail with Could not read the service-account private key.

07 Authorise domain-wide delegation

This is the step that is unique to Gmail, and it must be done by a Workspace Super Admin. It grants the service account permission to send as mailboxes in your domain — and only for the one scope you name.

First, note the service account's client ID — the long numeric client_id from the JSON key (also shown on the service account's detail page as its Unique ID / OAuth 2 Client ID). Then, in the Admin Console:

  1. Go to Security → Access and data control → API controls.
  2. Under Domain-wide delegation, click Manage domain-wide delegation.
  3. Click Add new.
  4. For Client ID, paste the numeric client ID from above.
  5. For OAuth scopes, enter exactly:
    https://www.googleapis.com/auth/gmail.send
  6. Click Authorise.
The Google Workspace Admin Console Domain-wide Delegation page with the 'Add a new client ID' dialog, showing the Client ID and OAuth scopes (comma-delimited) fields and an Authorize button.
Admin Console → Security → API Controls → Domain-wide Delegation → Add new. Paste the client ID and the gmail.send scope.
Scope exactly

Enter the scope exactly as https://www.googleapis.com/auth/gmail.send — no trailing spaces, no broader mail.google.com. If the authorised scope doesn't match what the plugin requests, Google rejects the token with unauthorized_client.

Changes can take a few minutes (occasionally up to ~24 hours) to propagate. If your first test fails with an auth error right away, wait and try again before changing anything.

08 Configure Lean SMTP

In WordPress, go to Settings → Lean SMTP and:

  1. Set Mailer to Gmail / Google Workspace.
  2. Under the Gmail section, set Send As to the Workspace mailbox mail should come from — e.g. noreply@example.com. This is the mailbox the service account impersonates.
  3. Paste the entire JSON key file into Service Account Key. It is stored encrypted and never shown again; leave it blank on later saves to keep the existing key.
  4. In the From section, set the From Email to the same address as Send As (or an alias that mailbox is allowed to send as). A mismatch makes Google reject the message.
  5. Click Save Changes.
From must match Send As

Google only lets the impersonated mailbox send as itself or its permitted aliases. If your From Email is a different address, the send fails. Keep them aligned — and if another plugin sets its own From address, turn on Force From so Lean SMTP's address wins.

09 Configure in wp-config.php (optional)

Every setting can be pinned as a constant in wp-config.php, which always beats the stored option and renders the field read-only in the admin. This keeps credentials out of the database and lets you deploy the same config across environments:

wp-config.php
define( 'LEAN_SMTP_MAILER', 'gmail' );
define( 'LEAN_SMTP_FROM_EMAIL', 'noreply@example.com' );
define( 'LEAN_SMTP_GMAIL_SENDER', 'noreply@example.com' );

// the full JSON key, as one string — easiest from an env var
define( 'LEAN_SMTP_GMAIL_SERVICE_ACCOUNT', getenv( 'LEAN_SMTP_GMAIL_KEY' ) );
Constants hold secrets in plaintext

A value set in a constant is used as-is and is not run through the plugin's encryption — so load the key from an environment variable rather than hard-coding the JSON into a file under version control. A constant-backed field shows read-only in the settings screen and names the constant in force.

10 Send a test email

Confirm it works end to end:

  • On the settings page, use the Send a Test Email button, or
  • From the command line, run WP-CLI:
wp lean-smtp test
$ wp lean-smtp test you@example.com

Success: Test email sent to you@example.com.

You can also check wp lean-smtp status to see the mailer and each setting's source (option or constant); secret values are never printed, only where they came from. If the send fails, the error names the HTTP status and Google's own message — see below.

11 Troubleshooting

SymptomLikely cause & fix
unauthorized_client Domain-wide delegation isn't authorised, the client ID is wrong, or the authorised scope doesn't match gmail.send. Re-check Step 7, and allow a few minutes for changes to propagate.
HTTP 400 / 401 at auth "Google refused the authentication request." Usually the service account, scope, or Send As subject is off, or the system clock is badly skewed (the JWT's iat/exp become invalid). Verify the Send As mailbox is a real, active mailbox in the domain.
Could not read the private key The key JSON wasn't pasted in full, or the private_key newlines were mangled. Re-download a fresh JSON key and paste the whole file unchanged.
HTTP 403 (Gmail rejected) Gmail API not enabled on the project (Step 4), or the delegation scope is too narrow/wrong. Confirm the API is enabled and the scope is exactly gmail.send.
"From" / delegation denied The From Email isn't the Send As mailbox or one of its permitted aliases. Align them, or enable Force From. Sending from a @gmail.com account will never work — it must be a Workspace mailbox.
Worked, then stopped The JSON key may have been deleted/disabled in Google Cloud, or the service account removed. Check the Keys tab and the delegation entry still exist.

12 Security notes

  • Least privilege. The delegation is scoped to gmail.send only — the service account can send mail as your domain's mailboxes, but cannot read inboxes or access anything else.
  • Encrypted at rest. When stored in the database, the key JSON is AES-256 encrypted against your site salts and never re-displayed in the browser.
  • Rotate keys periodically, and whenever one may have been exposed: create a new JSON key, paste it in, test, then delete the old key in the Google Cloud Keys tab.
  • Revoking access is a single click: remove the client-ID entry under Domain-wide delegation in the Admin Console, or disable/delete the service account in Google Cloud.
Still stuck?

Open an issue on GitHub or ask on the WordPress.org support forum. Google's own reference for this flow is service-account authentication and sending with the Gmail API.