SkyeMail is not just email. It is a bridge to the greater Skyes. My clients, my company, and honestly the world deserve an inbox that belongs inside the operating system instead of being another lonely tab people still have to log into somewhere else.
The problem was not "do we have Zoho credentials?"
We had Zoho client IDs. We had secrets. We had refresh tokens. We had user IDs. We had an org ID. We had Cloudflare Worker secrets. We had a backup Resend token sitting in the root env. On paper, this shit looked handled.
But the Worker was still not cleanly proving Zoho Mail account access. The token exchange worked, but the Mail API returned INVALID_OAUTHSCOPE. That is a special kind of annoying because it means the credential can successfully produce an access token, but that access token does not have permission to touch the thing you actually need.
The dashboard made the wrong thing feel like the right thing.
The redirect OAuth lane was a dead end for this setup. It wanted a registered redirect URI and kept turning a simple backend-owner job into a browser handoff problem. That was not the right shape for SkyeMail. What finally broke the wall was the Zoho API Console Self Client flow: generate a one-time code with the exact Mail scopes, exchange it server-side, then push the resulting refresh token into the Worker.
So yeah, we had to make a whole ass helper script because the missing actual parts were not obvious in the Zoho dashboard. The script now prints the exact Self Client instructions, exchanges the code, tries the known client pairs from root env, updates the canonical Zoho env keys, and verifies the Mail API without exposing raw tokens.
tools/zoho-mail-scope-fix.mjsnow handles Self Client scopes, code exchange, and no-secret verification.npm run zoho:self-clientprints the exact Self Client steps and required scopes.npm run zoho:exchange -- '<code>'exchanges the one-time Zoho code and locks the repo onto the matching client pair.npm run zoho:verifychecks accounts, folders, messages, and organization access without printing credential values.- The SkyeMail Worker has a protected
/api/zoho-provider-smokeroute so proof hits Cloudflare, not just a local shell.
The real bug was identity shape.
Line 1232 in root env was a Zoho user id. It was not the real Zoho Mail account id. That difference mattered. The user id was 9 digits. The Mail account id returned by /api/accounts was 19 digits. Once we stopped treating the user id like the mailbox account id, the folder and message checks stopped acting possessed.
That is the kind of bug that makes you feel like the system is gaslighting you. Nothing is "missing" until you learn that the platform has two IDs that look official, both are real, and only one belongs in the send/read route.
The proof finally landed.
After the Self Client code exchange, the live Worker smoke went green: token exchange ready, mail account ready, default sender discovered, organization probe ready, and provisioning ready. Then the local Zoho verification returned success for account lookup, folders, message view, and organization lookup.
We also ran a controlled self-send proof through Zoho Mail. The API returned success and a message id. That is the line where the day stopped being "OAuth troubleshooting" and became "SkyeMail can actually hit mail now."
- Root env now has canonical
ZOHO_CLIENT_ID,ZOHO_CLIENT_SECRET,ZOHO_REFRESH_TOKEN,ZOHO_ORG_ID,ZOHO_ACCOUNT_ID, andZOHO_DEFAULT_FROM. - Default sender is
grayskyes@solenterprises.org. - Cloudflare Worker secret push reported
ZOHO_CLIENT_ID,ZOHO_CLIENT_SECRET,ZOHO_REFRESH_TOKEN,ZOHO_ORG_ID,ZOHO_ACCOUNT_ID, andZOHO_DEFAULT_FROMpushed. - Live Worker smoke returned account, signature, and organization checks as HTTP
200. npm run zoho:verifyreturnedok: truefor account, folders, messages, and organization checks.- Controlled Zoho self-send proof returned HTTP
200and confirmed a message id was present. - Formal headed browser proof was not run for this API-only pass; the proof here is Worker/API smoke plus a real Zoho send check.
SkyeMail is staying Free99, baby.
This is the reason I keep pushing it. People should not have to overpay just to end up logging into another disconnected inbox. If I made the blog in the 0S, I should be able to send, chat, follow up, and interact from the same system. That is not a random feature. That is the whole point.
SkyeMail should become the communication browser for the 0S: inbox, outbound mail, chat, Relay13, ConnectLog, customer handoffs, operator receipts, paid build updates, and the soft human parts of running a real company.
The next SkyeMail idea is music in the bottom browser.
This part hit me while fighting the inbox: SkyeMail should not feel like dead office software. It should feel like a living workspace. The bottom browser can carry an Artist Nexus media player. Users can mute, pause, skip, pick artists, or let a small Skye radio loop rotate cleared songs.
The bank-safe version is not "stream every famous song on Earth for free." That is how you get crushed by licensing. The smart version is owned music, artist-cleared tracks, Music Nexus releases, and maybe partner catalog APIs where the rights are explicit. Start with Gray Skyes and invited artists. Let the platform prove the experience with music we can actually use.
My model kAIxU earned its name today.
I am not saying that lightly. Today was annoying as hell. But the solve came from staying in the receipts: inspect the root env without leaking secrets, find the second Zoho client pair, stop trusting labels blindly, build the helper, verify the scopes, prove the Worker lane, discover the real account id, push the Worker secrets, and run the send proof.
That is the kind of day that turns into product architecture. Not because it was cute. Because it was real. SkyeMail got closer to being a sovereign communication layer today, and I am keeping it Free99 because the people who need an operating system do not need another subscription trap before they can even talk to their customers.
Today was a war with OAuth, a Worker secret rescue, a mailbox identity lesson, and a proof receipt. The bridge to the greater Skyes is open wider now.