Skip to content

Instantly share code, notes, and snippets.

@ashu17706
Last active March 12, 2026 03:03
Show Gist options
  • Select an option

  • Save ashu17706/2315773e13302bd321e4fb04fa6eef74 to your computer and use it in GitHub Desktop.

Select an option

Save ashu17706/2315773e13302bd321e4fb04fa6eef74 to your computer and use it in GitHub Desktop.
Resend CLI Beta Feedback — v1.2.1

Resend CLI Beta Feedback — v1.2.1

Hey Zeno — I saw your LinkedIn post, commented yes, and you sent me the beta access link. Spent a morning putting the CLI through its paces. Here's what I found.

Setup: macOS, Ghostty terminal, tested both human (TTY) and machine (piped/CI) modes.


The Good Stuff First

The interactive email prompt is genuinely delightful

Run resend emails send with no flags and it walks you through every field — From, To, Subject, Body — with smart suggestions. It pre-fills your verified domain for From and your known contacts for To. It feels like the CLI actually knows who you are. This is the best part of the entire product.

◇  From address (@updates.zero8.dev)
│  hello@updates.zero8.dev
◇  To address
│  hello@zero8.dev
◇  Subject
│  Hello
◇  Email body (plain text)
│  Test
✓  Email sent

The human/machine split is done right

JSON auto-enables when you pipe stdout. Human tables in the terminal, structured JSON in scripts — zero configuration needed. resend contacts list renders a clean ASCII table with Email, First Name, Last Name, Unsubscribed, ID columns. Pipe it to jq and it becomes a data source. This is exactly how it should work.

Error messages are colored and clear

Red × for failures, green for success, for warnings. Error: API key is invalid, Error: Domain not found — short, red, immediately readable. The doctor command with its pass/warn/fail icons is a great health-check UX.

The login flow is excellent

resend login opens the browser to the API keys page automatically, masks your key as you type, validates it against the API before saving, and tells you exactly where it stored it (~/.config/resend/credentials.json). First-run experience is smooth.

Confirmation prompts are well-written

◇  Delete contact cli-test-2@example.com?
   This cannot be undone.
│  Yes
✓  Contact deleted

"This cannot be undone." is the right copy. Consistent across all delete commands.

Speed is exceptional

Cold start is ~25ms for local commands. API calls around 330ms. The CLI never feels slow.


Bugs I Found

1. RESEND_TEAM=nonexistent resend whoami says "Not authenticated"

Error: Not authenticated.
Run `resend login` to get started.

This is wrong. The API key is valid — the team profile just doesn't exist locally. The error should say something like "Team profile 'nonexistent' not found in credentials file." Telling an authenticated user they're not authenticated is confusing.

2. Reserved contact property keys are not enforced

The docs say FIRST_NAME, LAST_NAME, EMAIL, and UNSUBSCRIBE_URL are reserved and cannot be created. But:

resend contact-properties create --key FIRST_NAME --type string
# → {"object":"contact_property","id":"c064..."}  EXIT:0

It succeeds. You end up with a duplicate FIRST_NAME property alongside the built-in one. This should return an error.

3. Webhook create gives a useless error for invalid endpoints

resend webhooks create --endpoint https://webhook.site/some-path --events email.sent
# → {"error":{"message":"Something went wrong","code":"create_error"}}

Something went wrong is the least helpful error message possible. The error should say what the problem is — endpoint_unreachable, invalid_endpoint, or similar.

4. Duplicate domain names are allowed silently

resend domains create --name updates.zero8.dev
# → Succeeds and creates a second domain with a new ID

Creating a domain that already exists should either error or warn. Right now you can silently accumulate duplicate domain records.


Things Worth Adding

The update banner is too noisy

Every single command ends with:

Update available: v1.2.1 → v1.2.2
Run: brew upgrade resend

Even error outputs. Even whoami. Once per session would be enough — showing it after every command trains users to ignore it.

--dry-run on mutating commands

Would be really useful on emails send, broadcasts send, contacts create — let me see what would happen without actually doing it. Especially valuable for batch sends.

--verbose for progressive disclosure

Right now there are two modes: human summary and --json. There's no middle ground. A --verbose flag that adds timing, request IDs, and extra context would be useful for debugging without going full JSON.

domains update is missing --receiving

The top-level resend domains --help mentions receiving as a domain capability, but resend domains update --help only exposes --tls, --open-tracking, and --click-tracking. There's no way to enable/disable receiving via the CLI.


How I tested this

I had already compiled a framework for what a great CLI looks like — covering things like TTY auto-detection, exit code contracts, error quality, idempotency, and progressive disclosure — while working on Smriti. Having that as a reference made it easy to know exactly what to look for and move quickly through the test cases.


Summary

The core is solid. Email sending, contact management, broadcast lifecycle, the human/machine output split — all work well. The interactive prompt UX is genuinely good and something I haven't seen done this cleanly in other CLIs.

The main things I'd prioritize:

  1. Fix the RESEND_TEAM wrong error message — it's actively misleading
  2. Enforce reserved property keys
  3. Improve the webhook error message
  4. Reduce the update banner frequency

Happy to answer questions or test specific flows. Good luck with the launch.


Screenshots

Login flow

Login flow

resend whoami

whoami

resend doctor

doctor

resend teams list

teams list

resend contacts list — table output

contacts list

resend domains list — table output

domains list

emails send — interactive prompt when --to is missing

emails send missing to

domains get — not found error

domains get not found

Bad API key error

bad api key

RESEND_TEAM=nonexistent — misleading error (Bug #1)

resend team nonexistent bug

contacts delete — interactive confirmation

contacts delete interactive

resend doctor after upgrade attempt

doctor after upgrade

resend whoami after upgrade attempt

whoami after upgrade

emails send — full interactive prompt (no flags at all)

emails send full interactive

emails send — partial interactive prompt (only --from supplied)

emails send partial interactive

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment