A WordPress support thread describes a pattern that shows up constantly with CF7-to-Google-Sheets connector plugins:
- The connector works fine right after setup
- Some time later, it just stops sending data
- Deactivating and reactivating the plugin doesn't help
- Revoking Google permissions and re-granting them doesn't help
- Connecting to a brand new sheet from scratch has the same problem
The plugin author's own reply in that thread points people to check debug.log for authentication errors — which is the right instinct, because this almost never turns out to be a sheet permissions issue. It's an OAuth token lifecycle issue, and it's baked into how Google treats third-party apps that haven't gone through full verification.
The Actual Cause: Testing Mode and the 7-Day Token Expiry
Every plugin that connects to Google Sheets via OAuth does so through a Google Cloud project. That project has a publishing status: Testing or In Production. If the project is in Testing (which is the default, and where a lot of smaller plugin integrations stay), Google enforces a hard rule: refresh tokens issued to that app expire after exactly 7 days, regardless of how many times the user re-authorizes.
This explains the exact symptom in the thread. The connector works after setup because the token is fresh. A week or so later it silently dies. The site owner revokes and re-grants access, which issues a brand new token — so it works again for a few days, then dies again. Because the failure is intermittent and recovers temporarily after re-auth, it looks like a bug that "sometimes" happens rather than a fixed 7-day countdown.
A few related conditions produce the same invalid_grant failure and are worth ruling out too:
- 100-token cap — each Google account can hold at most 100 live refresh tokens per OAuth client. Past that, Google silently invalidates the oldest one with no warning
- 6-month inactivity — a refresh token that sits unused for six consecutive months is auto-revoked
- Password change — if the connected scopes touch Gmail, a password reset revokes the token immediately
- Workspace admin policy — an admin can restrict specific scopes after the fact, breaking a previously working connection
None of these are fixed by deactivating/reactivating the plugin, and none are fixed by connecting a new spreadsheet, since the token — not the sheet — is what's broken.
Why Re-Authorizing Doesn't Actually Fix It
Re-granting permission resets the clock, it doesn't remove the 7-day ceiling. As long as the underlying Google Cloud project stays in Testing mode, every token issued to it — old sheet or new sheet — will expire on the same schedule. Publishing the app to Production removes the 7-day limit, but for apps that use sensitive or restricted scopes, that requires completing Google's full OAuth verification process, which most small WordPress plugin setups never go through.
Sidestepping the Whole Problem
Contact Form to API doesn't authenticate to Google Sheets the way an OAuth connector does, which means the 7-day/100-token/6-month expiry rules that govern user-consent tokens don't apply at all. There are two ways to wire it up:
Option 1 — Google Apps Script Web App (fastest to set up)
Deploy a small Apps Script bound to your sheet as a Web App, set to "Execute as: Me" and "Anyone can access":
function doPost(e) {
const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Sheet1');
const data = JSON.parse(e.postData.contents);
sheet.appendRow([new Date(), data.name, data.email, data.message]);
return ContentService.createTextOutput(JSON.stringify({ status: 'ok' }))
.setMimeType(ContentService.MimeType.JSON);
}
Authorization here happens once, at deployment time, under your own Google account — there's no separate per-request OAuth handshake and no refresh token to expire. Point Contact Form to API's request URL at the deployment's /exec URL, map your CF7 fields into the JSON body, and submissions land in the sheet directly.
Option 2 — Sheets API v4 with a service account (more robust)
If you'd rather call Google's real API instead of routing through Apps Script, create a service account in Google Cloud, share the target sheet with the service account's email, and authenticate requests with a JWT signed by the service account key:
POST https://sheets.googleapis.com/v4/spreadsheets/{spreadsheetId}/values/Sheet1!A1:append?valueInputOption=RAW
Authorization: Bearer {service_account_access_token}
Content-Type: application/json
{
"values": [["2026-07-03", "Jane Doe", "jane@example.com", "Hello"]]
}
Service account credentials aren't part of the end-user consent lifecycle at all, so none of the Testing-mode expiry rules apply to them.
Troubleshooting Checklist
- Check whether the connector's Google Cloud project is in Testing or Production status
- If you have access to the project, check the refresh token count against the 100-token cap
- Confirm the connected scopes don't include Gmail if a password was recently changed
- If moving to a direct API/Apps Script approach, confirm the sheet is shared with the correct account (your own account for Apps Script, the service account email for the Sheets API)
- Watch for
invalid_grantspecifically in logs — that error code, not a generic failure, confirms it's a token issue rather than a sheet permissions issue
If your CF7 integration keeps dying every week or so despite reconnecting, don't keep re-authorizing — check the publishing status of whatever Google Cloud project sits behind the connector, or skip the OAuth layer entirely with Contact Form to API.
Top comments (0)