@noah/hey-email
HEY.com email integration with Kody: search threads, read messages/attachments, create drafts, and send with explicit confirmation.
README.md
174 lines · 5.6 KB · Markdown@noah/hey-email
Intent
Give Kody packages a thin, secret-backed way to search, read, and draft the
signed-in user's HEY mail without running the local hey CLI inside Workers.
Powered by the heyToken user secret against https://app.hey.com.
Success means another package can search threads, read bodies, download PDF
receipts, and save a reply draft that the user opens in HEY. Nothing in this
package sends mail unless a send function is called with { confirm: true }.
Prerequisites
- A HEY account. HEY is a paid product from 37signals with a free trial — sign
up at hey.com and see hey.com/pricing.
A
@hey.compersonal address and a HEY for Domains address both work. - The
heyCLI, used once, locally, to mint the token. Kody never runs it. - A Kody account, to store that token as the
heyTokensecret.
Setup
1. Install the HEY CLI
macOS / Linux / WSL2:
curl -fsSL https://hey.com/install-cli | bashWindows (PowerShell):
irm https://hey.com/install-cli.ps1 | iexHomebrew, mise, Scoop, Nix, deb/rpm/apk and go install also work — see the
install guide.
Confirm it is on your PATH:
hey version2. Sign in
hey auth loginThis opens a browser for HEY's own OAuth server (PKCE). Credentials land in your system keyring. Verify:
hey auth statusYou want "authenticated": true and "auth_type": "oauth".
Do not sign in with
hey auth login --cookie. That stores a browser session cookie rather than a bearer token, andhey auth tokenwill refuse to print it, so there is nothing to give this package.
3. Print the token
hey auth tokenThat is a bearer token for your mailbox. Treat it like a password: do not commit it, paste it into a chat, or echo it into shell history you sync.
4. Save it to Kody as heyToken
Add a user secret at kody.codes/account/secrets/new:
| Field | Value |
|---|---|
| Name | heyToken |
| Value | the output of hey auth token |
| Allowed host | app.hey.com |
Pipe it straight from the CLI to your clipboard so it never hits the screen:
hey auth token | pbcopy # macOS
hey auth token | wl-copy # Linux (Wayland)Approve the host if Kody prompts you on the first fetch, and approve the package's access to the secret if asked.
5. Verify
import identity from 'kody:@noah/hey-email'
await identity()Done when that returns your HEY user id and name. A HEY 401 means the token is
missing, stale, or was saved from a --cookie login.
Keeping the token working
The CLI refreshes its own token automatically, but the Kody secret is a static
copy and does not refresh with it. When it lapses, calls start failing with
HEY 401. Fix it by re-running step 3 and updating the same secret:
hey auth refresh && hey auth tokenhey auth status reports expires_at for the local credential, which is a good
indication of when the copy in Kody will need refreshing.
Security
The token is read from the secret per request and is only ever sent to
app.hey.com. resolveHeyUrl rejects every other origin, so attachment links
parsed out of untrusted email HTML cannot redirect a credentialed fetch off-site.
Exports
kody:@noah/hey-email— identity smoke testkody:@noah/hey-email/search— advanced searchkody:@noah/hey-email/boxes— list boxeskody:@noah/hey-email/topic— get topic JSONkody:@noah/hey-email/entries— list topic entrieskody:@noah/hey-email/message— full message JSON (/messages/{id}.json)kody:@noah/hey-email/attachments— list/download PDF attachments from message HTMLkody:@noah/hey-email/drafts— list / create / read drafts; optional send with confirmkody:@noah/hey-email/reply— alias forcreateReplyDraftkody:@noah/hey-email/send—sendMessage/sendReply/sendDraft(confirm required)
Draft vs send
HEY's POST /entries/{id}/replies.json sends when to is present and
saves a draft when to is omitted. createReplyDraft never sends to.
Every send function is a dry run unless you pass { confirm: true }, so a
mistaken call reports what it would have sent instead of mailing anyone.
Draft edit URL: https://app.hey.com/messages/{id}/edit
Topic URL: https://app.hey.com/topics/{topicId}
Troubleshooting
| Symptom | Cause | Fix |
|---|---|---|
HEY 401 | Secret is stale, missing, or from a --cookie login | hey auth refresh && hey auth token, update heyToken |
hey auth token refuses to print | Signed in with --cookie | hey auth logout, then hey auth login |
Refusing to send HEY credentials to … | A non-HEY URL was passed to a fetch helper | Expected: only app.hey.com is allowed |
| Secret resolves but the host is blocked | app.hey.com not approved for the secret | Approve it at /account/secrets |
| Attachments come back empty | Read the entry instead of the message | Use getMessage; entries omit attachment markup |
Not a replacement for the CLI
This package is for agents calling HEY inside Kody. For your own terminal,
use the CLI directly — it also ships an agent skill and an MCP server
(hey mcp). See docs/agents.md.
License
MIT. See LICENSE.
Smoke test
import identity from 'kody:@noah/hey-email'
import { listDrafts } from 'kody:@noah/hey-email/drafts'
await identity()
await listDrafts()