Send emails with Convex
Install the Convex component and call sendEmail.
What you'll build
A Convex mutation that enqueues a welcome email. The PostShiba component commits the email with your transaction, then delivers it from a background action with retries.
Before you start
- An API key with Send only access. Full access also works. See Create an API key. Support keys cannot send.
- A verified sending domain. See Add and verify a domain.
frommust be an address on it. - A cluster that is ready to send (
sending_readyistrue). See Clusters. - Your team id and cluster id. Both are on the API keys page.
Install
- Create the client and call
sendEmailfrom a mutation inconvex/email.ts.
Run it with npx convex run email:sendWelcome '{"to": "you@example.com"}'. sendEmail returns an EmailId once the email is stored as queued. Delivery happens after the mutation commits, and the message then shows under Events in the dashboard.
You can leave out apiKey, teamId, and clusterId and set POSTSHIBA_API_KEY, POSTSHIBA_TEAM_ID, and POSTSHIBA_CLUSTER_ID with npx convex env set instead.
Two options matter before you send for real:
| Option | Default | Why it matters |
|---|---|---|
testMode |
true |
While on, every recipient must be on inbound.postshiba.com. Set it to false to send to real addresses. |
hourlyLimit |
25 |
The component sends at most this many emails an hour. Raise it to just under your cluster's hourly capacity. |
Attachments and templates
Attach a file
Pass attachments with filename, contentType, and base64 content. Up to 10 files, 18 MB in total. See Attachments for allowed types.
Send a published template
Pass template with the alias or public id and its variables. Leave out html and text.
Keep the key safe
Use a Send only key in your deployment. It can send email and call GET /api/v1/users/me. Every other request returns 403 forbidden.
| If this key leaks | Send only | Full access |
|---|---|---|
| Send mail from your verified domains | Yes | Yes |
| Read events, recipients, templates, and sent messages | No | Yes |
| See SMTP passwords and webhook secrets | No | Yes |
| Change clusters, domains, credentials, webhooks, and suppressions | No | Yes |
- Keep Full access keys for setup scripts, the CLI, and MCP. Don't set one on a Convex deployment.
- Set the key with
npx convex env set POSTSHIBA_API_KEY YOUR_API_KEYand leaveapiKeyout ofnew PostShiba. Don't commit it, log it, or put it in a URL. - The component sends from a background action, so the key stays in your deployment. Never pass a key, even a Send only key, from your React or mobile client.
- Create one key per deployment, so dev and prod never share one. If a key leaks, delete it on API keys and create a new one. Deleting stops it straight away.
Troubleshooting
Errors do not reach your mutation, because delivery runs later. Read the outcome with postshiba.status(ctx, emailId), or subscribe to webhooks with handleEventWebhook.
| Status | error |
What the component does |
|---|---|---|
401 |
Invalid token or no user signed in |
Marks the email failed without retrying. Check apiKey or POSTSHIBA_API_KEY. |
403 |
forbidden, domain_unverified, or kyc_required |
Marks the email failed. Support keys cannot send, so use a Send only key and a verified from. While your team is pending production approval, send only to team members, live inbox addresses, and inbox forward-to addresses. |
422 |
invalid |
Marks the email failed. errorCode and errorMessage on the email say why. |
429 |
throttled |
Retries with backoff, because the component owns the queue. Keep hourlyLimit under your cluster's capacity so this stays rare. See What happens at your cap. |
Every other status is on Errors.
Next steps
- Verify webhook requests before you route events to
handleEventWebhook. - Event types to see which statuses the component can record.
- Send emails with Node.js for the plain client.
- SDKs for every language client.